зеркало из https://github.com/microsoft/Briefcase.git
Add .NET Implementation
This commit is contained in:
Родитель
4167c9a137
Коммит
25ce109ca3
|
@ -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>
|
|
@ -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
|
|
@ -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
|
||||||
|
|
Загрузка…
Ссылка в новой задаче