Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/iai #141

Merged
merged 20 commits into from
Nov 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@
/**/TestResults/**
/**/packages/**
.env

*.crt
*.key

14 changes: 14 additions & 0 deletions Industrial-IoT.sln
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.IIoT.Module
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{4CF22591-6F77-486F-BBFE-6173536D0625}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deploy", "deploy", "{8D316DA2-FC23-4F5A-ABE0-725C6E9653EC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{24CC83C2-F144-4EB9-9C7E-48406B1E2932}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.IIoT.Deployment", "deploy\src\Microsoft.Azure.IIoT.Deployment\Microsoft.Azure.IIoT.Deployment.csproj", "{4676D2B5-492B-47FF-9AD8-CB546CA0CEEF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -641,6 +647,12 @@ Global
{277079D0-B557-4280-A698-37EDADA10066}.Develop|Any CPU.Build.0 = Debug|Any CPU
{277079D0-B557-4280-A698-37EDADA10066}.Release|Any CPU.ActiveCfg = Release|Any CPU
{277079D0-B557-4280-A698-37EDADA10066}.Release|Any CPU.Build.0 = Release|Any CPU
{4676D2B5-492B-47FF-9AD8-CB546CA0CEEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4676D2B5-492B-47FF-9AD8-CB546CA0CEEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4676D2B5-492B-47FF-9AD8-CB546CA0CEEF}.Develop|Any CPU.ActiveCfg = Debug|Any CPU
{4676D2B5-492B-47FF-9AD8-CB546CA0CEEF}.Develop|Any CPU.Build.0 = Debug|Any CPU
{4676D2B5-492B-47FF-9AD8-CB546CA0CEEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4676D2B5-492B-47FF-9AD8-CB546CA0CEEF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -737,6 +749,8 @@ Global
{9CDDE537-2D55-4A7E-947D-7FA3EC87B72A} = {C8D79D8A-D92E-4AE3-BA54-0C927BEBCEBF}
{277079D0-B557-4280-A698-37EDADA10066} = {C8D79D8A-D92E-4AE3-BA54-0C927BEBCEBF}
{4CF22591-6F77-486F-BBFE-6173536D0625} = {30938473-53C1-4981-BFAB-B6309147FF24}
{24CC83C2-F144-4EB9-9C7E-48406B1E2932} = {8D316DA2-FC23-4F5A-ABE0-725C6E9653EC}
{4676D2B5-492B-47FF-9AD8-CB546CA0CEEF} = {24CC83C2-F144-4EB9-9C7E-48406B1E2932}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F09EFCEA-59F6-4A16-9CB6-7E865A3F62D8}
Expand Down
17 changes: 15 additions & 2 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,27 @@ stages:
- template: tools/templates/sdl.yml
- stage: pack
displayName: 'Package and Sign Nuget'
dependsOn: build
dependsOn:
- build
jobs:
- template: tools/templates/nuget.yml
parameters:
sign: ${{ startsWith(variables['Build.SourceBranch'], 'refs/heads/') }}
- stage: iiot_deployment
displayName: 'Publish Microsoft.Azure.IIoT.Deployment'
dependsOn:
- build
jobs:
- template: tools/templates/iiot_deployment_win.yml
parameters:
sign: ${{ startsWith(variables['Build.SourceBranch'], 'refs/heads/') }}
- template: tools/templates/iiot_deployment_linux.yml
- template: tools/templates/iiot_deployment_mac.yml
- stage: images
displayName: 'Create and Push Images'
dependsOn: pack
dependsOn:
- pack
- iiot_deployment
jobs:
- template: tools/templates/acrbuild.yml
parameters:
Expand Down
3 changes: 3 additions & 0 deletions deploy/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(common.props))" Condition="'$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), common.props))' != ''" />
</Project>
285 changes: 285 additions & 0 deletions deploy/src/Microsoft.Azure.IIoT.Deployment/AuthenticationManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------

namespace Microsoft.Azure.IIoT.Deployment {

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent.Authentication;
using Microsoft.Identity.Client;
using Microsoft.Rest;

class AuthenticationManager {

// ClientId of AzureIndustrialIoTIAI Application
public const string AzureIndustrialIoTIAIClientID = "fb2ca262-60d8-4167-ac33-1998d6d5c50b";

public static readonly string[] MicrosoftGraphIAIScopes = new string[] {
"https://graph.microsoft.com/Directory.AccessAsUser.All"
};

public static readonly string[] AzureManagementIAIScopes = new string[] {
"https://management.azure.com/user_impersonation"
};

public static readonly string[] KeyVaultIAIScopes = new string[] {
"https://vault.azure.net/user_impersonation"
};



private readonly AzureEnvironment _azureEnvironment;
private readonly IPublicClientApplication _publicClientApplication;

private IAccount _account;
private Guid _tenantId;


public AuthenticationManager(
AzureEnvironment azureEnvironment,
string tenant
) {
_azureEnvironment = azureEnvironment;
var azureCloudInstance = ToAzureCloudInstance(azureEnvironment);

_publicClientApplication = PublicClientApplicationBuilder
.Create(AzureIndustrialIoTIAIClientID)
.WithAuthority(azureCloudInstance, tenant)
//.WithAuthority(azureCloudInstance, AadAuthorityAudience.AzureAdMultipleOrgs)
.WithDefaultRedirectUri()
.Build();
}

public async Task AuthenticateAsync(
CancellationToken cancellationToken = default
) {
AuthenticationResult microsoftGraphAuthenticatoinResult;

var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);

if (isWindows) {
// We will use interactive authentication flow for Windows.
// https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/Acquiring-tokens-interactively

// ToDo: Add timeout.
microsoftGraphAuthenticatoinResult = await _publicClientApplication
.AcquireTokenInteractive(MicrosoftGraphIAIScopes)
.WithExtraScopesToConsent(AzureManagementIAIScopes)
.WithExtraScopesToConsent(KeyVaultIAIScopes)
//.WithPrompt(Prompt.SelectAccount)
.ExecuteAsync(cancellationToken);
}
else {
// We will use device code flow authentication for Unix systems.
// https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/Device-Code-Flow

// ToDo: Add timeout.
microsoftGraphAuthenticatoinResult = await _publicClientApplication
.AcquireTokenWithDeviceCode(
MicrosoftGraphIAIScopes,
deviceCodeResult => {
// This will print the message on the console which tells the user where to go sign-in using
// a separate browser and the code to enter once they sign in.
// The AcquireTokenWithDeviceCode() method will poll the server after firing this
// device code callback to look for the successful login of the user via that browser.
// This background polling (whose interval and timeout data is also provided as fields in the
// deviceCodeCallback class) will occur until:
// * The user has successfully logged in via browser and entered the proper code
// * The timeout specified by the server for the lifetime of this code (typically ~15 minutes) has been reached
// * The developing application calls the Cancel() method on a CancellationToken sent into the method.
// If this occurs, an OperationCanceledException will be thrown (see catch below for more details).
Console.WriteLine(deviceCodeResult.Message);
return Task.FromResult(0);
})
.ExecuteAsync();
}

// Extract account and tenant ID from microsoftGraphAuthenticatoinResult
_account = microsoftGraphAuthenticatoinResult.Account;
_tenantId = new Guid(microsoftGraphAuthenticatoinResult.TenantId);

// Validate that we have received Tokens.
await AcquireMicrosoftGraphTokenAsync(cancellationToken);
await AcquireAzureManagementTokenAsync(cancellationToken);
await AcquireKeyVaultTokenAsync(cancellationToken);
}

public async Task<AuthenticationResult> AcquireMicrosoftGraphTokenAsync(
CancellationToken cancellationToken = default
) {
// Fetch AccessToken from cache
// https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/AcquireTokenSilentAsync-using-a-cached-token
var authenticationResult = await _publicClientApplication
.AcquireTokenSilent(MicrosoftGraphIAIScopes, _account)
.ExecuteAsync(cancellationToken);

return authenticationResult;
}

public async Task<AuthenticationResult> AcquireAzureManagementTokenAsync(
CancellationToken cancellationToken = default
) {
// Fetch AccessToken from cache
// https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/AcquireTokenSilentAsync-using-a-cached-token
var authenticationResult = await _publicClientApplication
.AcquireTokenSilent(AzureManagementIAIScopes, _account)
.ExecuteAsync(cancellationToken);

return authenticationResult;
}

public async Task<AuthenticationResult> AcquireKeyVaultTokenAsync(
CancellationToken cancellationToken = default
) {
// Fetch AccessToken from cache
// https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/AcquireTokenSilentAsync-using-a-cached-token
var authenticationResult = await _publicClientApplication
.AcquireTokenSilent(KeyVaultIAIScopes, _account)
.ExecuteAsync(cancellationToken);

return authenticationResult;
}

public async Task<TokenCredentials> GetMicrosoftGraphTokenCredentialsAsync(
CancellationToken cancellationToken = default
) {
var microsoftGraphAuthenticatoinResult = await AcquireMicrosoftGraphTokenAsync(cancellationToken);
var microsoftGraphTokenCredentials = GenerateTokenCredentials(microsoftGraphAuthenticatoinResult);
return microsoftGraphTokenCredentials;
}

public TokenCredentials GetMicrosoftGraphDelegatingTokenCredentials() {
var microsoftGraphTokenCredentials = GenerateDelegatingTokenCredentials(AcquireMicrosoftGraphTokenAsync);
return microsoftGraphTokenCredentials;
}

public async Task<TokenCredentials> GetAzureManagementTokenCredentialsAsync(
CancellationToken cancellationToken = default
) {
var azureManagementAuthenticatoinResult = await AcquireAzureManagementTokenAsync(cancellationToken);
var azureManagementTokenCredentials = GenerateTokenCredentials(azureManagementAuthenticatoinResult);
return azureManagementTokenCredentials;
}

public TokenCredentials GetAzureManagementDelegatingTokenCredentials() {
var azureManagementTokenCredentials = GenerateDelegatingTokenCredentials(AcquireAzureManagementTokenAsync);
return azureManagementTokenCredentials;
}

public async Task<TokenCredentials> GetKeyVaultTokenCredentialsAsync(
CancellationToken cancellationToken = default
) {
var keyVaultAuthenticatoinResult = await AcquireKeyVaultTokenAsync(cancellationToken);
var keyVaultTokenCredentials = GenerateTokenCredentials(keyVaultAuthenticatoinResult);
return keyVaultTokenCredentials;
}

public TokenCredentials GetKeyVaultDelegatingTokenCredentials() {
var keyVaultTokenCredentials = GenerateDelegatingTokenCredentials(AcquireKeyVaultTokenAsync);
return keyVaultTokenCredentials;
}

public IAccount GetAccount() {
return _account;
}

public Guid GetTenantId() {
return _tenantId;
}

public async Task<AzureCredentials> GetAzureCredentialsAsync(
CancellationToken cancellationToken = default
) {
var azureManagementTokenCredentials = await GetAzureManagementTokenCredentialsAsync(cancellationToken);
var microsoftGraphTokenCredentials = await GetMicrosoftGraphTokenCredentialsAsync(cancellationToken);

var azureCredentials = new AzureCredentials(
azureManagementTokenCredentials,
microsoftGraphTokenCredentials,
_tenantId.ToString(),
_azureEnvironment
);

return azureCredentials;
}

public AzureCredentials GetDelegatingAzureCredentials() {
var azureManagementTokenCredentials = GetAzureManagementDelegatingTokenCredentials();
var microsoftGraphTokenCredentials = GetMicrosoftGraphDelegatingTokenCredentials();

var azureCredentials = new AzureCredentials(
azureManagementTokenCredentials,
microsoftGraphTokenCredentials,
_tenantId.ToString(),
_azureEnvironment
);

return azureCredentials;
}

public static AzureCloudInstance ToAzureCloudInstance(AzureEnvironment azureEnvironment) {
if (azureEnvironment.Equals(AzureEnvironment.AzureGlobalCloud)) {
return AzureCloudInstance.AzurePublic;
}
else if (azureEnvironment.Equals(AzureEnvironment.AzureChinaCloud)) {
return AzureCloudInstance.AzureChina;
}
else if (azureEnvironment.Equals(AzureEnvironment.AzureGermanCloud)) {
return AzureCloudInstance.AzureGermany;
}
else if (azureEnvironment.Equals(AzureEnvironment.AzureUSGovernment)) {
return AzureCloudInstance.AzureUsGovernment;
}
else {
throw new SystemException("Unknown AzureEnvironment: " + azureEnvironment.Name);
}
}

public static TokenCredentials GenerateTokenCredentials(
AuthenticationResult authenticationResult
) {
ITokenProvider tokenProvider = new StringTokenProvider(authenticationResult.AccessToken, "Bearer");

var tokenCredentials = new TokenCredentials(
tokenProvider,
authenticationResult.TenantId,
authenticationResult.Account.Username
);

return tokenCredentials;
}

public ITokenProvider GenerateDelegatingTokenProvider(
Func<CancellationToken, Task<AuthenticationResult>> authenticationResultProvider
) {
async Task<string> accessTokenProvider(CancellationToken cancellationToken) {
var authenticationResult = await authenticationResultProvider(cancellationToken);
return authenticationResult.AccessToken;
};

ITokenProvider tokenProvider = new DelegatingTokenProvider(accessTokenProvider);

return tokenProvider;
}

public TokenCredentials GenerateDelegatingTokenCredentials(
Func<CancellationToken, Task<AuthenticationResult>> authenticationResultProvider
) {
var tokenProvider = GenerateDelegatingTokenProvider(authenticationResultProvider);

var tokenCredentials = new TokenCredentials(
tokenProvider,
_tenantId.ToString(),
_account.Username
);

return tokenCredentials;
}
}
}