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
.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 # 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: stages:
- stage: Compliance - stage: Compliance
jobs: jobs:

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

@ -1,3 +1,4 @@
from .auth import * from .auth import *
from .keyvault 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 .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.clientid = clientid
self.tenantid = tenantid self.tenantid = tenantid
def get_client(self): def get_client_lazy(self):
from azure.common.credentials import ServicePrincipalCredentials from azure.common.credentials import ServicePrincipalCredentials
# TODO add cloud environment # 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 import KeyVaultClient, KeyVaultAuthentication, KeyVaultId
from azure.keyvault.models import KeyVaultErrorException from azure.keyvault.models import KeyVaultErrorException
from ..credentialprovider import CredentialProvider from ..credentialprovider import CredentialProvider

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

@ -3,11 +3,14 @@ import yaml
from enum import Enum from enum import Enum
import datetime import datetime
# TODO: from workspace.base import ... should work # TODO: from workspace.base import ... should work
from ..base import Resource from ..base import KeyNotFoundException, Resource
from ..datasource import URLDataSource 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): def __init__(self, accountname, accountkey=None, credentialstore=None):
self.accountname = accountname self.accountname = accountname
self.accountkey = accountkey self.accountkey = accountkey
@ -22,33 +25,82 @@ class AzureStorage(Resource):
def is_secret_a_sas_token(self) -> bool: def is_secret_a_sas_token(self) -> bool:
return self.get_secrettype().lower() == 'sas' 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): def get_client_lazy(self):
# only import if method is used # only import if method is used
from azure.storage.common import CloudStorageAccount from azure.storage.common import CloudStorageAccount
# key vs SAS token # key vs SAS token
account_key = sas_token = None account_key = sas_token = None
if self.is_secret_a_sas_token():
sas_token = self.get_secret() try:
else: if self.is_secret_a_sas_token():
account_key = self.get_secret() 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 # TODO: endpoint_suffix
return CloudStorageAccount(account_name=self.accountname, return CloudStorageAccount(account_name=self.accountname,
account_key=account_key, account_key=account_key,
sas_token=sas_token, sas_token=sas_token,
is_emulated=getattr(self, "is_emulated", None), is_emulated=getattr(
endpoint_suffix=getattr(self, "endpoint_suffix", None)) self, "is_emulated", None),
endpoint_suffix=getattr(self, "endpoint_suffix", None))
class AzureBlob(URLDataSource): class AzureBlob(URLDataSource):
yaml_tag = u'!azure.blob' yaml_tag = u'!azure.storage.blob'
def __init__(self, datasource, path): def __init__(self, datasource, path):
self.datasource = datasource self.datasource = datasource
self.path = path self.path = path
def download(self, target) -> None: def download(self, target) -> None:
block_blob_service = self.datasource.get_client().create_block_blob_service() 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: def get_url(self) -> str:
if not hasattr(self, 'datasource'): if not hasattr(self, 'datasource'):
@ -61,14 +113,17 @@ class AzureBlob(URLDataSource):
from azure.storage.blob.models import ContainerPermissions 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 # 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() now = datetime.datetime.utcnow()
sas_token = sas.generate_blob( sas_token = sas.generate_blob(
self.containername, self.containername,
self.path, self.path,
permission=ContainerPermissions(read=True), # TODO: maybe write? permission=ContainerPermissions(
start=now - datetime.timedelta(hours=1), # this feels like trouble read=True), # TODO: maybe write?
# this feels like trouble
start=now - datetime.timedelta(hours=1),
expiry=now + datetime.timedelta(hours=12)) expiry=now + datetime.timedelta(hours=12))
# TODO: secure vs non-secure # TODO: secure vs non-secure
@ -78,4 +133,4 @@ class AzureBlob(URLDataSource):
self.datasource.accountname, self.datasource.accountname,
self.containername, self.containername,
self.path, 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): for name in os.listdir(path):
# TODO: allow for different name. global param? ctor param? # 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) return os.path.join(path, name)
# going up the directory structure # going up the directory structure

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

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

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

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

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

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