This commit is contained in:
Markus Cozowicz 2019-08-20 01:51:01 +02:00 коммит произвёл GitHub
Родитель 4167c9a137
Коммит 25ce109ca3
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
50 изменённых файлов: 1452 добавлений и 32 удалений

5
.gitignore поставляемый
Просмотреть файл

@ -105,3 +105,8 @@ venv.bak/
# VSCode
.vscode
.vs
dotnet/**/bin
dotnet/**/obj
dotnet/**/packages
dotnet/**/*.csproj.user

Просмотреть файл

@ -1,5 +1,12 @@
[![Build Status](https://dev.azure.com/ossworkspace/Workspace/_apis/build/status/microsoft.Workspace?branchName=master)](https://dev.azure.com/ossworkspace/Workspace/_build/latest?definitionId=1&branchName=master)
[![Build Status](https://dev.azure.com/ossworkspace/Workspace/_apis/build/status/Microsoft.Workspace%20Python?branchName=master)](https://dev.azure.com/ossworkspace/Workspace/_build/latest?definitionId=1&branchName=master)
Python
[![Build Status](https://dev.azure.com/ossworkspace/Workspace/_apis/build/status/Microsoft.Workspace%20DotNet%20Core?branchName=master)](https://dev.azure.com/ossworkspace/Workspace/_build/latest?definitionId=3&branchName=master)
.NET Core
[![Build Status](https://dev.azure.com/ossworkspace/Workspace/_apis/build/status/Microsoft.Workspace%20DotNet%20Desktop?branchName=master)](https://dev.azure.com/ossworkspace/Workspace/_build/latest?definitionId=2&branchName=master)
.NET Desktop
# Contributing

Просмотреть файл

@ -0,0 +1,26 @@
using Microsoft.Azure.KeyVault;
using Microsoft.Workspace;
using System;
using System.Threading.Tasks;
using YamlDotNet.Serialization;
namespace Workspace.Azure.KeyVault
{
public class KeyVault : Resource, ICredentialProvider
{
private readonly Lazy<KeyVaultClient> client = new Lazy<KeyVaultClient>(() =>
null); // new KeyVaultClient(new KeyVaultClient.AuthenticationCallback("abc-securityToken")));
public async Task<string> GetSecretAsync(string key)
{
//ClientCredential
//// TODO: catch exception
var secret = await client.Value.GetSecretAsync(key);
return secret.Value;
}
[YamlMember(Alias = "dnsname")]
public string DNSName { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.KeyVault" Version="3.0.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Workspace\Workspace.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,25 @@
using YamlDotNet.Serialization;
using Microsoft.WindowsAzure.Storage;
namespace Microsoft.Workspace.Azure.Storage
{
public class Account : Resource
{
[YamlMember(Alias = "accountname")]
public string Name { get; set; }
public CloudStorageAccount Client
{
get
{
// TODO: lookup subscriptions
// search for storage account
// TODO: lookup keyvault
// TODO: support service prinicipal
// TODO: support env variable
// CloudStorageAccount.Parse
return null;
}
}
}
}

Просмотреть файл

@ -0,0 +1,22 @@
using System.IO;
using System.Threading.Tasks;
using YamlDotNet.Serialization;
namespace Microsoft.Workspace.Azure.Storage
{
public class Blob : Resource
{
[YamlMember(Alias = "containername")]
public string ContainerName { get; set; }
public string Path { get; set; }
[YamlMember(Alias = "datasource")]
public Account DataSource { get; set; }
public Task DownloadToAsync(Stream target)
{
return Task.FromResult(true);
}
}
}

Просмотреть файл

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<AssemblyName>Microsoft.Workspace.Azure.Storage</AssemblyName>
<RootNamespace>Microsoft.Workspace.Azure.Storage</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="WindowsAzure.Storage" Version="9.3.3" />
<PackageReference Include="YamlDotNet" Version="6.1.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Workspace\Workspace.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Workspace.AzureActiveDirectory.Desktop")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Workspace.AzureActiveDirectory.Desktop")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("d8934a46-02b3-4f81-b32a-8712d49ca3e0")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Просмотреть файл

@ -0,0 +1,101 @@
using Microsoft.Identity.Client;
using System.Threading.Tasks;
using System.Linq;
namespace Microsoft.Workspace.AzureActiveDirectory.Desktop
{
public class SilentAuthentication
{
private string clientId;
private string tenantId;
public SilentAuthentication()
{
clientId = "42e402c8-afee-41a0-99f8-ab51ca6c6dce";
tenantId = "72f988bf-86f1-41af-91ab-2d7cd011db47";
}
public async Task<AuthenticationResult> GetATokenForGraphAsync()
{
// string authority = "https://login.microsoftonline.com/contoso.com";
string[] scopes = new string[] { "user.read" };
// ConfidentialClientApplicationBuilder.Create("abc")
IPublicClientApplication app = PublicClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantId)
.Build();
var accounts = await app.GetAccountsAsync();
AuthenticationResult result = null;
if (accounts.Any())
{
result = await app
.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
.ExecuteAsync();
}
else
{
try
{
result = await app
.AcquireTokenByIntegratedWindowsAuth(scopes)
.ExecuteAsync();
}
catch (MsalUiRequiredException)
{
// MsalUiRequiredException: AADSTS65001: The user or administrator has not consented to use the application
// with ID '{appId}' named '{appName}'.Send an interactive authorization request for this user and resource.
// you need to get user consent first. This can be done, if you are not using .NET Core (which does not have any Web UI)
// by doing (once only) an AcquireToken interactive.
// If you are using .NET core or don't want to do an AcquireTokenInteractive, you might want to suggest the user to navigate
// to a URL to consent: https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={clientId}&response_type=code&scope=user.read
var url = string.Format("https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={0}&response_type=code&response_mode=query&scope=user.read",
clientId);
System.Diagnostics.Process.Start(url);
// AADSTS50079: The user is required to use multi-factor authentication.
// There is no mitigation - if MFA is configured for your tenant and AAD decides to enforce it,
// you need to fallback to an interactive flows such as AcquireTokenAsync or AcquireTokenByDeviceCode
}
catch (MsalServiceException)
{
// Kind of errors you could have (in ex.Message)
// MsalServiceException: AADSTS90010: The grant type is not supported over the /common or /consumers endpoints. Please use the /organizations or tenant-specific endpoint.
// you used common.
// Mitigation: as explained in the message from Azure AD, the authoriy needs to be tenanted or otherwise organizations
// MsalServiceException: AADSTS70002: The request body must contain the following parameter: 'client_secret or client_assertion'.
// Explanation: this can happen if your application was not registered as a public client application in Azure AD
// Mitigation: in the Azure portal, edit the manifest for your application and set the `allowPublicClient` to `true`
}
catch (MsalClientException)
{
// Error Code: unknown_user Message: Could not identify logged in user
// Explanation: the library was unable to query the current Windows logged-in user or this user is not AD or AAD
// joined (work-place joined users are not supported).
// Mitigation 1: on UWP, check that the application has the following capabilities: Enterprise Authentication,
// Private Networks (Client and Server), User Account Information
// Mitigation 2: Implement your own logic to fetch the username (e.g. john@contoso.com) and use the
// AcquireTokenByIntegratedWindowsAuthAsync overload that takes in the username
// Error Code: integrated_windows_auth_not_supported_managed_user
// Explanation: This method relies on an a protocol exposed by Active Directory (AD). If a user was created in Azure
// Active Directory without AD backing ("managed" user), this method will fail. Users created in AD and backed by
// AAD ("federated" users) can benefit from this non-interactive method of authentication.
// Mitigation: Use interactive authentication
}
}
return result;
}
}
}

Просмотреть файл

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{D8934A46-02B3-4F81-B32A-8712D49CA3E0}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Microsoft.Workspace.AzureActiveDirectory.Desktop</RootNamespace>
<AssemblyName>Microsoft.Workspace.AzureActiveDirectory.Desktop</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Identity.Client, Version=4.3.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Identity.Client.4.3.0\lib\net45\Microsoft.Identity.Client.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.IdentityModel" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SilentAuthentication.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

Просмотреть файл

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Identity.Client" version="4.3.0" targetFramework="net46" />
</packages>

Просмотреть файл

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using YamlDotNet.Serialization;
namespace Microsoft.Workspace.Azure.AD
{
public class ServicePrincipal : Resource, ICredentialProvider
{
[YamlMember(Alias = "clientid")]
public string ClientId { get; set; }
public Task<string> GetSecretAsync(string key)
{
// ConfidentialClientApplicationBuilder.Create("abc")
throw new NotImplementedException();
}
}
}

Просмотреть файл

@ -0,0 +1,103 @@
using Microsoft.Identity.Client;
using System.Threading.Tasks;
using System.Linq;
namespace Microsoft.Workspace.Azure.AD
{
public class SilentAuthentication
{
private string clientId;
private string tenantId;
public SilentAuthentication()
{
clientId = "42e402c8-afee-41a0-99f8-ab51ca6c6dce";
tenantId = "72f988bf-86f1-41af-91ab-2d7cd011db47";
}
public string ClientId { get; set; }
public async Task<AuthenticationResult> GetATokenForGraphAsync()
{
// string authority = "https://login.microsoftonline.com/contoso.com";
string[] scopes = new string[] { "user.read" };
IPublicClientApplication app = PublicClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantId)
.Build();
var accounts = await app.GetAccountsAsync();
AuthenticationResult result = null;
if (accounts.Any())
{
result = await app
.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
.ExecuteAsync();
}
else
{
try
{
result = await app
.AcquireTokenByIntegratedWindowsAuth(scopes)
.ExecuteAsync();
}
catch (MsalUiRequiredException)
{
// MsalUiRequiredException: AADSTS65001: The user or administrator has not consented to use the application
// with ID '{appId}' named '{appName}'.Send an interactive authorization request for this user and resource.
// you need to get user consent first. This can be done, if you are not using .NET Core (which does not have any Web UI)
// by doing (once only) an AcquireToken interactive.
// If you are using .NET core or don't want to do an AcquireTokenInteractive, you might want to suggest the user to navigate
// to a URL to consent: https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={clientId}&response_type=code&scope=user.read
var url = string.Format("https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={0}&response_type=code&response_mode=query&scope=user.read",
clientId);
System.Diagnostics.Process.Start(url);
// AADSTS50079: The user is required to use multi-factor authentication.
// There is no mitigation - if MFA is configured for your tenant and AAD decides to enforce it,
// you need to fallback to an interactive flows such as AcquireTokenAsync or AcquireTokenByDeviceCode
}
catch (MsalServiceException)
{
// Kind of errors you could have (in ex.Message)
// MsalServiceException: AADSTS90010: The grant type is not supported over the /common or /consumers endpoints. Please use the /organizations or tenant-specific endpoint.
// you used common.
// Mitigation: as explained in the message from Azure AD, the authoriy needs to be tenanted or otherwise organizations
// MsalServiceException: AADSTS70002: The request body must contain the following parameter: 'client_secret or client_assertion'.
// Explanation: this can happen if your application was not registered as a public client application in Azure AD
// Mitigation: in the Azure portal, edit the manifest for your application and set the `allowPublicClient` to `true`
}
catch (MsalClientException)
{
// Error Code: unknown_user Message: Could not identify logged in user
// Explanation: the library was unable to query the current Windows logged-in user or this user is not AD or AAD
// joined (work-place joined users are not supported).
// Mitigation 1: on UWP, check that the application has the following capabilities: Enterprise Authentication,
// Private Networks (Client and Server), User Account Information
// Mitigation 2: Implement your own logic to fetch the username (e.g. john@contoso.com) and use the
// AcquireTokenByIntegratedWindowsAuthAsync overload that takes in the username
// Error Code: integrated_windows_auth_not_supported_managed_user
// Explanation: This method relies on an a protocol exposed by Active Directory (AD). If a user was created in Azure
// Active Directory without AD backing ("managed" user), this method will fail. Users created in AD and backed by
// AAD ("federated" users) can benefit from this non-interactive method of authentication.
// Mitigation: Use interactive authentication
}
}
return result;
}
}
}

Просмотреть файл

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<AssemblyName>Microsoft.Workspace.Azure.AD</AssemblyName>
<RootNamespace>Microsoft.Workspace.Azure.AD</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Identity.Client" Version="4.3.0" />
<PackageReference Include="YamlDotNet" Version="6.1.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Workspace\Workspace.csproj" />
</ItemGroup>
</Project>

55
dotnet/Workspace.sln Normal file
Просмотреть файл

@ -0,0 +1,55 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28803.452
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Workspace", "Workspace\Workspace.csproj", "{2207E2F3-3282-417C-B6ED-27D6490051A3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspaceTest", "WorkspaceTest\WorkspaceTest.csproj", "{33911FAE-9E30-4ACE-BADB-A4582333D104}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Workspace.Azure.AD", "Workspace.AzureActiveDirectory\Workspace.Azure.AD.csproj", "{CC4AA13B-7FE4-4CC7-AC21-B06558FE157C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspaceTest.Desktop", "WorkspaceTest.Desktop\WorkspaceTest.Desktop.csproj", "{8B137BFA-8A2E-4A59-89A3-8BC14FE0911D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Workspace.Azure.Storage", "Workspace.Azure.Storage\Workspace.Azure.Storage.csproj", "{AD55574A-7E7A-4B70-913C-9E3AB2F2E129}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Workspace.Azure.KeyVault", "Workspace.Azure.KeyVault\Workspace.Azure.KeyVault.csproj", "{43912DD2-DDDB-44D0-874B-5E18A3C0B50D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2207E2F3-3282-417C-B6ED-27D6490051A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2207E2F3-3282-417C-B6ED-27D6490051A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2207E2F3-3282-417C-B6ED-27D6490051A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2207E2F3-3282-417C-B6ED-27D6490051A3}.Release|Any CPU.Build.0 = Release|Any CPU
{33911FAE-9E30-4ACE-BADB-A4582333D104}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{33911FAE-9E30-4ACE-BADB-A4582333D104}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33911FAE-9E30-4ACE-BADB-A4582333D104}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33911FAE-9E30-4ACE-BADB-A4582333D104}.Release|Any CPU.Build.0 = Release|Any CPU
{CC4AA13B-7FE4-4CC7-AC21-B06558FE157C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CC4AA13B-7FE4-4CC7-AC21-B06558FE157C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CC4AA13B-7FE4-4CC7-AC21-B06558FE157C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CC4AA13B-7FE4-4CC7-AC21-B06558FE157C}.Release|Any CPU.Build.0 = Release|Any CPU
{8B137BFA-8A2E-4A59-89A3-8BC14FE0911D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8B137BFA-8A2E-4A59-89A3-8BC14FE0911D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8B137BFA-8A2E-4A59-89A3-8BC14FE0911D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8B137BFA-8A2E-4A59-89A3-8BC14FE0911D}.Release|Any CPU.Build.0 = Release|Any CPU
{AD55574A-7E7A-4B70-913C-9E3AB2F2E129}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD55574A-7E7A-4B70-913C-9E3AB2F2E129}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD55574A-7E7A-4B70-913C-9E3AB2F2E129}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AD55574A-7E7A-4B70-913C-9E3AB2F2E129}.Release|Any CPU.Build.0 = Release|Any CPU
{43912DD2-DDDB-44D0-874B-5E18A3C0B50D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{43912DD2-DDDB-44D0-874B-5E18A3C0B50D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{43912DD2-DDDB-44D0-874B-5E18A3C0B50D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{43912DD2-DDDB-44D0-874B-5E18A3C0B50D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CAAA81D2-7164-4DA2-86C4-5F17AAD69C5D}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,16 @@
using System;
using System.Threading.Tasks;
namespace Microsoft.Workspace
{
[YamlTag("env")]
public class EnvironmentCredentialProvider : Resource, ICredentialProvider
{
public static readonly EnvironmentCredentialProvider Instance = new EnvironmentCredentialProvider();
public Task<string> GetSecretAsync(string key)
{
return Task.FromResult(Environment.GetEnvironmentVariable(key));
}
}
}

Просмотреть файл

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.Workspace
{
public interface ICredentialProvider
{
Task<string> GetSecretAsync(string key);
}
}

Просмотреть файл

@ -0,0 +1,11 @@
namespace Microsoft.Workspace
{
public interface IResource
{
string Name { get; set; }
string[] Path { get; set; }
WorkspaceImpl Workspace { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,26 @@
using System.Collections.Generic;
namespace Microsoft.Workspace
{
public class Resource : IResource
{
string IResource.Name { get; set; }
WorkspaceImpl IResource.Workspace { get; set; }
string[] IResource.Path { get; set; }
public ICredentialProvider CredentialProvider { get; set; }
IEnumerable<ICredentialProvider> GetCredentialProviders()
{
if (CredentialProvider != null)
{
yield return CredentialProvider;
yield break;
}
yield return EnvironmentCredentialProvider.Instance;
}
}
}

Просмотреть файл

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.IO;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
using System.Linq;
namespace Microsoft.Workspace
{
public delegate T ResourceSelector<T>(Resource resource, IEnumerable<string> path, string name);
public delegate void ResourceAction(Resource resource, IEnumerable<string> path, string name);
public class WorkspaceImpl
{
private object root;
public WorkspaceImpl(string path = ".")
{
using (var reader = new StreamReader(File.OpenRead(Path.Combine(path, "resources.yaml"))))
{
Parse(reader);
}
}
public WorkspaceImpl(TextReader resourcesYamlReader)
{
Parse(resourcesYamlReader);
}
private void Parse(TextReader resourcesYamlReader)
{
var deserializer = new DeserializerBuilder()
.WithNamingConvention(new CamelCaseNamingConvention())
.WithNodeTypeResolver(new WorkspaceNodeTypeResolver())
.Build();
root = deserializer.Deserialize(resourcesYamlReader);
// setup path, name and workspace
Resources = Select((resource, path, name) =>
{
IResource res = resource as IResource;
res.Workspace = this;
res.Path = path.ToArray();
res.Name = name;
return resource;
})
.ToList();
}
private IEnumerable<T> SelectImpl<T>(ResourceSelector<T> visitor, List<string> path, object node, string name)
{
if (node is Resource resource)
{
var ret = visitor(resource, path, name);
yield return ret;
yield break;
}
if (node is IDictionary<object, object> dict)
{
if (name != null)
path.Add(name);
foreach (var item in dict)
{
var ret = SelectImpl(visitor, path, item.Value, item.Key.ToString());
// if we found something
foreach (var r in ret)
yield return r;
}
if (name != null)
path.RemoveAt(path.Count - 1);
yield break;
}
throw new ArgumentException("Unknown type in tree: " + node.GetType());
}
private IEnumerable<T> Select<T>(ResourceSelector<T> visitor)
{
return SelectImpl(visitor, new List<string>(), root, null);
}
public List<Resource> Resources { get; private set; }
public IEnumerable<Resource> this[string key]
{
get
{
var components = key.Split('.', '/').ToArray();
var path = string.Join(".", components.Take(components.Count() - 1));
var name = components.Last();
// find by name
if (path.Length == 0)
return Resources
.OfType<IResource>()
.Where(r => r.Name == name)
.OfType<Resource>();
// find by path
return Resources
.OfType<IResource>()
.Where(r => r.Name == name && string.Join(".", r.Path) == path)
.OfType<Resource>();
}
}
}
}

Просмотреть файл

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<AssemblyName>Microsoft.Workspace</AssemblyName>
<RootNamespace>Microsoft.Workspace</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="YamlDotNet" Version="6.1.2" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,63 @@
using System;
using System.Text.RegularExpressions;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
namespace Microsoft.Workspace
{
public class WorkspaceNodeTypeResolver : INodeTypeResolver
{
private readonly IDictionary<string, Type> tagToTypeMapping;
public WorkspaceNodeTypeResolver()
{
Assembly asm = typeof(YamlTagAttribute).GetTypeInfo().Assembly;
tagToTypeMapping = asm.DefinedTypes
.Select(t => new { Type = t, Attribute = t.GetCustomAttribute<YamlTagAttribute>() })
.Where(t => t.Attribute != null)
.ToDictionary(t => t.Attribute.Tag, t => t.Type.AsType());
// TODO: since .NET Standard doesn't have appdomains nor module initializer I'm not sure how to find the other classes
// difference
// tagToTypeMapping.Add("azure.serviceprincipal", Type.GetType("Microsoft.Workspace.Azure.AD.ServicePrincipal"))
}
public bool Resolve(NodeEvent nodeEvent, ref Type currentType)
{
// ignore non-tagged nodes
if (string.IsNullOrEmpty(nodeEvent.Tag))
return false;
// lookup built in tags
if (tagToTypeMapping.TryGetValue(nodeEvent.Tag, out currentType))
return true;
// lookup using naming scheme
var className = Regex.Replace(nodeEvent.Tag,
@"(\b)([a-z])",
m => m.Groups[1].Value + m.Groups[2].Value.ToUpperInvariant())
.Substring(1);
// Pattern: first 2 path elements define the package
var assemblyName = string.Join(".", className.Split('.').Take(2));
// e.g. tag name = azure.storage.account
// e.g. type name = Microsoft.Workspace.Azure.Storage.Account
var typeName = string.Format("Microsoft.Workspace.{0}, Microsoft.Workspace.{1}",
className,
assemblyName);
currentType = Type.GetType(typeName);
if (currentType == null)
throw new InvalidOperationException("Unable to find type: " + typeName);
return true;
}
}
}

Просмотреть файл

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.Workspace
{
[System.AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
sealed class YamlTagAttribute : Attribute
{
public YamlTagAttribute(string tag)
{
this.Tag = tag;
}
public string Tag
{
get; private set;
}
}
}

Просмотреть файл

@ -0,0 +1,23 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Workspace;
using Microsoft.Workspace.Azure.Storage;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.WorkspaceTest.Desktop
{
[TestClass]
public class AzureStorageTest
{
[TestMethod]
public async Task TestDownload()
{
var ws = new WorkspaceImpl("yamls/azure/storage");
var dataset = ws["dataset"].First() as Blob;
}
}
}

Просмотреть файл

@ -0,0 +1,20 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("WorkspaceTest.Desktop")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("WorkspaceTest.Desktop")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("8b137bfa-8a2e-4a59-89a3-8bc14fe0911d")]
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Просмотреть файл

@ -0,0 +1,17 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Workspace.Azure.AD;
using System.Threading.Tasks;
namespace WorkspaceTest
{
[TestClass]
public class SilentAuthenticationTest
{
[TestMethod]
public async Task TestSilentAuth()
{
var silentAuth = new SilentAuthentication();
var result = await silentAuth.GetATokenForGraphAsync();
}
}
}

Просмотреть файл

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{8B137BFA-8A2E-4A59-89A3-8BC14FE0911D}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Microsoft.WorkspaceTest.Desktop</RootNamespace>
<AssemblyName>Microsoft.WorkspaceTest.Desktop</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Azure.KeyVault, Version=3.0.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Azure.KeyVault.3.0.4\lib\net461\Microsoft.Azure.KeyVault.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Azure.KeyVault.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Azure.KeyVault.Core.1.0.0\lib\net40\Microsoft.Azure.KeyVault.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Azure.KeyVault.WebKey, Version=3.0.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Azure.KeyVault.WebKey.3.0.4\lib\net461\Microsoft.Azure.KeyVault.WebKey.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Identity.Client, Version=4.3.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Identity.Client.4.3.0\lib\net45\Microsoft.Identity.Client.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Rest.ClientRuntime, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Rest.ClientRuntime.2.3.20\lib\net461\Microsoft.Rest.ClientRuntime.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Rest.ClientRuntime.Azure, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Rest.ClientRuntime.Azure.3.3.19\lib\net461\Microsoft.Rest.ClientRuntime.Azure.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.1.4.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.1.4.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.WindowsAzure.Storage, Version=9.3.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\WindowsAzure.Storage.9.3.3\lib\net45\Microsoft.WindowsAzure.Storage.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Drawing" />
<Reference Include="System.IdentityModel" />
<Reference Include="System.Net" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Runtime" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="YamlDotNet, Version=6.0.0.0, Culture=neutral, PublicKeyToken=ec19458f3c15af5e, processorArchitecture=MSIL">
<HintPath>..\packages\YamlDotNet.6.1.2\lib\net45\YamlDotNet.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AzureStorageTest.cs" />
<Compile Include="SilentAuthenticationTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
<None Include="yamls\azure\storage\resources.yaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="yamls\env\resources.yaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Workspace.Azure.KeyVault\Workspace.Azure.KeyVault.csproj">
<Project>{43912dd2-dddb-44d0-874b-5e18a3c0b50d}</Project>
<Name>Workspace.Azure.KeyVault</Name>
</ProjectReference>
<ProjectReference Include="..\Workspace.Azure.Storage\Workspace.Azure.Storage.csproj">
<Project>{ad55574a-7e7a-4b70-913c-9e3ab2f2e129}</Project>
<Name>Workspace.Azure.Storage</Name>
</ProjectReference>
<ProjectReference Include="..\Workspace.AzureActiveDirectory\Workspace.Azure.AD.csproj">
<Project>{cc4aa13b-7fe4-4cc7-ac21-b06558fe157c}</Project>
<Name>Workspace.Azure.AD</Name>
</ProjectReference>
<ProjectReference Include="..\Workspace\Workspace.csproj">
<Project>{2207e2f3-3282-417c-b6ed-27d6490051a3}</Project>
<Name>Workspace</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets'))" />
</Target>
<Import Project="..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets')" />
</Project>

Просмотреть файл

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Azure.KeyVault.Core" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.4.0" newVersion="3.0.4.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /></startup></configuration>

Просмотреть файл

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Azure.KeyVault" version="3.0.4" targetFramework="net472" />
<package id="Microsoft.Azure.KeyVault.Core" version="1.0.0" targetFramework="net472" />
<package id="Microsoft.Azure.KeyVault.WebKey" version="3.0.4" targetFramework="net472" />
<package id="Microsoft.Identity.Client" version="4.3.0" targetFramework="net472" />
<package id="Microsoft.Rest.ClientRuntime" version="2.3.20" targetFramework="net472" />
<package id="Microsoft.Rest.ClientRuntime.Azure" version="3.3.19" targetFramework="net472" />
<package id="MSTest.TestAdapter" version="1.4.0" targetFramework="net46" />
<package id="MSTest.TestFramework" version="1.4.0" targetFramework="net46" />
<package id="Newtonsoft.Json" version="12.0.2" targetFramework="net46" />
<package id="WindowsAzure.Storage" version="9.3.3" targetFramework="net472" />
<package id="YamlDotNet" version="6.1.2" targetFramework="net46" />
</packages>

Просмотреть файл

@ -0,0 +1,7 @@
dataset:
!azure.storage.blob
containername: test
path: test.csv
datasource:
!azure.storage.account
accountname: workspacetest

25
dotnet/WorkspaceTest.Desktop/yamls/env/resources.yaml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,25 @@
myserviceprincipal1: &myserviceprincipal1
# created using
# Connect-AzureRmAccount
# $ServicePrincipal = New-AzureRmADServicePrincipal -DisplayName "Microsoft Workspace " `
# -Password (ConvertTo-SecureString "[Enter password]" -AsPlainText -Force)
# Out-Host -InputObject $ServicePrincipal.ApplicationId
!azure.serviceprincipal
clientid: b9a08b0b-2361-46ac-8487-f725c6730f67
tenantid: 72f988bf-86f1-41af-91ab-2d7cd011db47
myvault1: &myvault1
!azure.keyvault
dnsname: https://workspacetest.vault.azure.net/
credential: *myserviceprincipal1
workspacetest1: &workspacetest1
!azure.storage.account
accountname: workspacetest
credentialstore: *myvault1
dataset:
!azure.storage.blob
containername: test
path: test.csv
datasource: *workspacetest1

Просмотреть файл

@ -0,0 +1,19 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Workspace.Azure.AD;
using System.Threading.Tasks;
namespace Microsoft.WorkspaceTest
{
[TestClass]
public class SilentAuthenticationTest
{
[TestMethod]
public async Task TestSilentAuth()
{
//var silentAuth = new SilentAuthentication();
//var result = await silentAuth.GetATokenForGraphAsync();
//int x = 1;
}
}
}

Просмотреть файл

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<IsPackable>false</IsPackable>
<AssemblyName>Microsoft.WorkspaceTest</AssemblyName>
<RootNamespace>Microsoft.WorkspaceTest</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.3.2" />
<PackageReference Include="MSTest.TestFramework" Version="1.3.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Workspace.Azure.Storage\Workspace.Azure.Storage.csproj" />
<ProjectReference Include="..\Workspace.AzureActiveDirectory\Workspace.Azure.AD.csproj" />
<ProjectReference Include="..\Workspace\Workspace.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,61 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Workspace;
using Microsoft.Workspace.Azure.Storage;
using System.IO;
using System.Linq;
namespace Microsoft.WorkspaceTest
{
[TestClass]
public class YamlTest
{
private string yaml = @"
datasources: # just convention, support arbitrary structure
folder1:
myblobsource1: &myblobsource1
!azure.storage.account
accountname: webscaleai
";
[TestMethod]
public void TestTypeResolution()
{
var ws = new WorkspaceImpl(new StringReader(yaml));
var resources = ws.Resources.ToList();
Assert.AreEqual(1, resources.Count);
Assert.IsInstanceOfType(resources.First(), typeof(Account));
var storageAccount = resources.First() as Account;
Assert.AreEqual("webscaleai", storageAccount.Name);
IResource storageAccountResource = storageAccount;
Assert.AreEqual("myblobsource1", storageAccountResource.Name);
CollectionAssert.AreEqual(new[] { "datasources", "folder1" }, storageAccountResource.Path);
Assert.AreSame(ws, storageAccountResource.Workspace);
}
[TestMethod]
public void TestLookupByKey()
{
var ws = new WorkspaceImpl(new StringReader(yaml));
var q = ws["myblobsource1"];
Assert.AreEqual(1, q.Count());
Assert.IsInstanceOfType(q.First(), typeof(Account));
}
[TestMethod]
public void TestLookupByPath()
{
var ws = new WorkspaceImpl(new StringReader(yaml));
var q = ws["datasources/folder1/myblobsource1"];
Assert.AreEqual(1, q.Count());
Assert.IsInstanceOfType(q.First(), typeof(Account));
}
}
}

Просмотреть файл

@ -0,0 +1,53 @@
parameters:
operatingSystems: ["ubuntu-16.04", 'macos-10.13', 'vs2017-win2016']
jobs:
- job:
displayName: '.NET Core'
strategy:
matrix:
${{ each os in parameters.operatingSystems }}:
${{ format('{0}', os) }}:
imageName: ${{ os }}
pool:
vmImage: $(imageName)
steps:
- task: DotNetCoreInstaller@0
inputs:
version: '2.1.300'
- script: |
cd dotnet
dotnet restore
displayName: NuGet Restore
- task: DotNetCoreCLI@2
displayName: Build
inputs:
command: build
projects: |
dotnet/Workspace/*.csproj
dotnet/Workspace.Azure.KeyVault/*.csproj
dotnet/Workspace.Azure.Storage/*.csproj
dotnet/Workspace.AzureActiveDirectory/*.csproj
dotnet/WorkspaceTest/*.csproj
arguments: '--configuration Release'
- task: DotNetCoreCLI@2
displayName: Test
inputs:
command: test
projects: 'dotnet/WorkspaceTest/*.csproj'
# broken on ubuntu/mac --collect "Code coverage"
arguments: '--configuration Release '
# TODO: nuget
# - script: dotnet pack /p:PackageVersion=$(version) # define version variable elsewhere in your pipeline
# - task: NuGetCommand@2
# command: push
# nuGetFeedType: external
# publishFeedCredentials: '<Name of the NuGet service connection>'
# versioningScheme: byEnvVar
# versionEnvVar: version

Просмотреть файл

@ -0,0 +1,42 @@
trigger:
branches:
include:
- master
paths:
include:
- dotnet/*
stages:
- stage: Compliance
jobs:
- job:
steps:
- task: ComponentGovernanceComponentDetection@0
inputs:
scanType: 'Register'
verbosity: 'Verbose'
alertWarningLevel: 'High'
- stage: DotNetDesktop
jobs:
- job:
pool:
vmImage: 'windows-2019'
steps:
- script: |
cd dotnet
dotnet restore
displayName: NuGet Restore
- task: VSBuild@1
inputs:
solution: dotnet/Workspace.sln
restoreNugetPackages: true
- task: VSTest@2
inputs:
testSelector: 'testAssemblies'
testAssemblyVer2: |
'**\Microsoft.WorkspaceTest.Desktop.dll'
'**\Microsoft.WorkspaceTest.dll'
codeCoverageEnabled: True

Просмотреть файл

@ -0,0 +1,22 @@
trigger:
branches:
include:
- master
paths:
include:
- dotnet/*
stages:
- stage: Compliance
jobs:
- job:
steps:
- task: ComponentGovernanceComponentDetection@0
inputs:
scanType: 'Register'
verbosity: 'Verbose'
alertWarningLevel: 'High'
- stage: DotNetCore
jobs:
- template: azure-pipelines-template.yaml

Просмотреть файл

@ -1,3 +1,11 @@
trigger:
branches:
include:
- master
paths:
include:
- python/*
stages:
- stage: Compliance
jobs:

Просмотреть файл

@ -1,3 +1,4 @@
from .auth import *
from .keyvault import *
from .storage import *
from .storage import *
from .subscription import *

Просмотреть файл

@ -1,2 +1,3 @@
from .managed_service_identity import *
from .serviceprincipal import *
from .user_with_device_code import *
from .user_with_device_code import *

Просмотреть файл

@ -0,0 +1,20 @@
from ...base import Resource
class ManagedServiceIdentity(Resource):
yaml_tag = u'!azure.msi'
def __init__(self):
pass
def get_client_lazy(self):
try:
from msrestazure.azure_active_directory import MSIAuthentication
# TODO: add support for other resource types
msi_auth = MSIAuthentication()
msi_auth.set_token()
return msi_auth
except Exception as e:
return None

Просмотреть файл

@ -8,7 +8,7 @@ class AzureServicePrincipal(Resource):
self.clientid = clientid
self.tenantid = tenantid
def get_client(self):
def get_client_lazy(self):
from azure.common.credentials import ServicePrincipalCredentials
# TODO add cloud environment

Просмотреть файл

@ -0,0 +1,26 @@
from ..base import Resource
from .auth.managed_service_identity import ManagedServiceIdentity
class AzureResource(Resource):
def get_subscriptions(self) -> 'List[AzureSubscription]':
from .subscription import AzureSubscription
subscriptions = self.get_workspace().get_all_of_type(AzureSubscription)
# no subscriptions configured try to auto resolve through MSI
if len(subscriptions) == 0:
subscriptions.append(AzureSubscription())
return subscriptions
def get_resource_group(self):
return getattr(self, 'resource_group', None)
def get_auth_client(self):
if not hasattr(self, 'auth_client'):
# fallback to MSI
self.auth_client = ManagedServiceIdentity()
# this can also be a service principal or device auth
return self.auth_client.get_client()

Просмотреть файл

@ -1,4 +1,3 @@
import yaml
from azure.keyvault import KeyVaultClient, KeyVaultAuthentication, KeyVaultId
from azure.keyvault.models import KeyVaultErrorException
from ..credentialprovider import CredentialProvider

Просмотреть файл

@ -3,11 +3,14 @@ import yaml
from enum import Enum
import datetime
# TODO: from workspace.base import ... should work
from ..base import Resource
from ..base import KeyNotFoundException, Resource
from ..datasource import URLDataSource
from .azure_resource import AzureResource
class AzureStorage(AzureResource):
yaml_tag = u'!azure.storage.account'
class AzureStorage(Resource):
yaml_tag = u'!azure.storage'
def __init__(self, accountname, accountkey=None, credentialstore=None):
self.accountname = accountname
self.accountkey = accountkey
@ -22,33 +25,82 @@ class AzureStorage(Resource):
def is_secret_a_sas_token(self) -> bool:
return self.get_secrettype().lower() == 'sas'
def get_secret(self):
# TODO: cache secret
try:
return super().get_secret()
except KeyNotFoundException as ex:
try:
from azure.mgmt.storage import StorageManagementClient
auth_client = self.get_auth_client()
resource_group = self.get_resource_group()
# loop through subscriptions
for sub in self.get_subscriptions():
for id in sub.get_ids():
storage_client = StorageManagementClient(
auth_client, id)
if resource_group is None:
for acc in storage_client.storage_accounts.list():
if acc.name == self.accountname:
# found the account, let's break out
resource_group = acc.id.split('/')[4]
break
# let's check if we found the account, if not let's try the next subscription
if resource_group is None:
continue
storage_keys = storage_client.storage_accounts.list_keys(
resource_group, self.accountname)
# TODO: this seems to be model dependent?
return storage_keys.keys[0].value
except Exception as e:
raise e
raise ex
def get_client_lazy(self):
# only import if method is used
from azure.storage.common import CloudStorageAccount
# key vs SAS token
account_key = sas_token = None
if self.is_secret_a_sas_token():
sas_token = self.get_secret()
else:
account_key = self.get_secret()
try:
if self.is_secret_a_sas_token():
sas_token = self.get_secret()
else:
account_key = self.get_secret()
except KeyNotFoundException as e:
# fallback to subscription lookup
account_key = self._find_key_through_subscription()
if account_key is None:
raise e
# TODO: endpoint_suffix
return CloudStorageAccount(account_name=self.accountname,
account_key=account_key,
sas_token=sas_token,
is_emulated=getattr(self, "is_emulated", None),
endpoint_suffix=getattr(self, "endpoint_suffix", None))
account_key=account_key,
sas_token=sas_token,
is_emulated=getattr(
self, "is_emulated", None),
endpoint_suffix=getattr(self, "endpoint_suffix", None))
class AzureBlob(URLDataSource):
yaml_tag = u'!azure.blob'
yaml_tag = u'!azure.storage.blob'
def __init__(self, datasource, path):
self.datasource = datasource
self.path = path
def download(self, target) -> None:
block_blob_service = self.datasource.get_client().create_block_blob_service()
block_blob_service.get_blob_to_path(self.container_name, target, self.path)
block_blob_service.get_blob_to_path(
self.container_name, target, self.path)
def get_url(self) -> str:
if not hasattr(self, 'datasource'):
@ -61,14 +113,17 @@ class AzureBlob(URLDataSource):
from azure.storage.blob.models import ContainerPermissions
# could also get SAS on the fly by getting ADAL context: https://github.com/Azure/azure-storage-python/blob/master/azure-storage-blob/azure/storage/blob/sharedaccesssignature.py
sas = BlobSharedAccessSignature(self.datasource.accountname, account_key=self.datasource.get_secret())
sas = BlobSharedAccessSignature(
self.datasource.accountname, account_key=self.datasource.get_secret())
now = datetime.datetime.utcnow()
sas_token = sas.generate_blob(
self.containername,
self.containername,
self.path,
permission=ContainerPermissions(read=True), # TODO: maybe write?
start=now - datetime.timedelta(hours=1), # this feels like trouble
permission=ContainerPermissions(
read=True), # TODO: maybe write?
# this feels like trouble
start=now - datetime.timedelta(hours=1),
expiry=now + datetime.timedelta(hours=12))
# TODO: secure vs non-secure
@ -78,4 +133,4 @@ class AzureBlob(URLDataSource):
self.datasource.accountname,
self.containername,
self.path,
sas_token)
sas_token)

Просмотреть файл

@ -0,0 +1,42 @@
from ..base import Resource
from .azure_resource import AzureResource
from .auth import managed_service_identity
from typing import List
class AzureSubscription(AzureResource):
yaml_tag = u'!azure.subscription'
def __init__(self, id=None, ids=[]):
self.ids = ids
if id is not None:
self.ids.append(id)
def get_resource_group(self):
return getattr(self, 'resourcegroup', None)
def get_ids(self) -> List[str]:
ret = []
if hasattr(self, 'id'):
ret.append(self.id)
if hasattr(self, 'ids'):
ret.extend(self.ids)
if len(ret) == 0:
# TODO: resolve configured msi
auth = self.get_auth_client()
if auth is not None:
try:
# let's try to enumerate subscriptions
from azure.mgmt.subscription import SubscriptionClient
subscription_client = SubscriptionClient(auth)
ret.extend(map(lambda s: s.subscription_id,
subscription_client.subscriptions.list()))
except:
pass
return ret

Просмотреть файл

@ -33,7 +33,7 @@ class Workspace:
for name in os.listdir(path):
# TODO: allow for different name. global param? ctor param?
if name == 'resources.yaml' or name == 'resources.yml':
if name == 'resources.yaml':
return os.path.join(path, name)
# going up the directory structure

Просмотреть файл

@ -34,6 +34,7 @@ setup(name='pyworkspace',
'test': ['azureml-dataprep[pandas]',
'azure-keyvault',
'azure-storage-blob',
'azure-mgmt-subscription',
'sqlalchemy',
'keyring',
'keyrings.alt', # not recommended for production

Просмотреть файл

@ -14,7 +14,7 @@ myvault1: &myvault1
credential: *myserviceprincipal1
workspacetest1: &workspacetest1
!azure.storage
!azure.storage.account
accountname: workspacetest
credentialstore: *myvault1
@ -22,7 +22,7 @@ csv1:
!csv
separator: "\t"
dataset:
!azure.blob
!azure.storage.blob
containername: test
path: test.csv
datasource: *workspacetest1

Просмотреть файл

@ -12,12 +12,12 @@ credentials:
datasources: # just convention, support arbitrary structure
folder1:
myblobsource1: &myblobsource1
!azure.storage
!azure.storage.account
accountname: webscaleai
credentialstore: *myvault1
myblobsource2: &myblobsource2
!azure.storage
!azure.storage.account
accountname: storage2
secrettype: SAS
@ -47,13 +47,13 @@ cogservices:
datasets:
criteo:
day0:
!azure.blob
!azure.storage.blob
datasource: *myblobsource1
containername: criteo
path: 1_csv/day_0.gz
all_days: &all_days
!azure.blob
!azure.storage.blob
datasource: *myblobsource1
containername: criteo
path: 1_csv/*gz