Skip to content

Commit

Permalink
CIAM updates (#2422) (#2432)
Browse files Browse the repository at this point in the history
* CIAM updates (#2422)

* Updates for CIAM

* Updates for code modifier configs

* Update version to 2.0.4
  • Loading branch information
zahalzel committed May 30, 2023
1 parent 623e5d8 commit 8b9eb07
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 171 deletions.
2 changes: 1 addition & 1 deletion eng/Versions.MSIdentity.props
Expand Up @@ -6,7 +6,7 @@
<UsingToolNetFrameworkReferenceAssemblies>true</UsingToolNetFrameworkReferenceAssemblies>
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>2.0.3</VersionPrefix>
<VersionPrefix>2.0.4</VersionPrefix>
<PreReleaseVersionLabel>rtm</PreReleaseVersionLabel>
<IsServicingBuild Condition="'$(PreReleaseVersionLabel)' == 'servicing'">true</IsServicingBuild>
<!--
Expand Down
Expand Up @@ -87,6 +87,11 @@ public class ApplicationParameters
/// </summary>
public bool IsB2C { get; set; }

/// <summary>
/// Is authenticated with CIAM.
/// </summary>
public bool IsCiam { get; set; }

/// <summary>
/// Sign-up/sign-in policy in the case of B2C.
/// </summary>
Expand Down
Expand Up @@ -29,6 +29,7 @@ public static class PropertyNames
// https://github.com/dotnet/aspnetcore/blob/6bc4b79f4ee7af00edcbb435e5ee4c1de349a110/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/appsettings.json
public static class DefaultProperties
{
public const string Authority = "https://qualified.domain.name/";
public const string Domain = "qualified.domain.name";
public const string TenantId = "22222222-2222-2222-2222-222222222222";
public const string ClientId = "11111111-1111-1111-11111111111111111";
Expand All @@ -53,6 +54,7 @@ public class AzureAdBlock
public bool IsBlazorWasm;
public bool IsWebApi;
public bool IsB2C;
public bool IsCIAM;
public bool HasClientSecret;

public string? ClientId;
Expand All @@ -76,18 +78,23 @@ public AzureAdBlock(ApplicationParameters applicationParameters, JObject? existi
IsBlazorWasm = applicationParameters.IsBlazorWasm;
IsWebApi = applicationParameters.IsWebApi.GetValueOrDefault();
IsB2C = applicationParameters.IsB2C;
IsCIAM = applicationParameters.IsCiam;
HasClientSecret = applicationParameters.CallsDownstreamApi || applicationParameters.CallsMicrosoftGraph;

Domain = !string.IsNullOrEmpty(applicationParameters.Domain) ? applicationParameters.Domain : existingBlock?.GetValue(PropertyNames.Domain)?.ToString() ?? DefaultProperties.Domain;
if (IsCIAM)
{
Domain = Domain.Replace("onmicrosoft.com", "ciamlogin.com");
}

TenantId = !string.IsNullOrEmpty(applicationParameters.TenantId) ? applicationParameters.TenantId : existingBlock?.GetValue(PropertyNames.TenantId)?.ToString() ?? DefaultProperties.TenantId;
ClientId = !string.IsNullOrEmpty(applicationParameters.ClientId) ? applicationParameters.ClientId : existingBlock?.GetValue(PropertyNames.ClientId)?.ToString() ?? DefaultProperties.ClientId;
Instance = !string.IsNullOrEmpty(applicationParameters.Instance) ? applicationParameters.Instance : existingBlock?.GetValue(PropertyNames.Instance)?.ToString() ?? DefaultProperties.Instance;
CallbackPath = !string.IsNullOrEmpty(applicationParameters.CallbackPath) ? applicationParameters.CallbackPath : existingBlock?.GetValue(PropertyNames.CallbackPath)?.ToString() ?? DefaultProperties.CallbackPath;
Scopes = !string.IsNullOrEmpty(applicationParameters.CalledApiScopes) ? applicationParameters.CalledApiScopes : existingBlock?.GetValue(PropertyNames.Scopes)?.ToString()
?? (applicationParameters.CallsDownstreamApi ? DefaultProperties.ApiScopes : applicationParameters.CallsMicrosoftGraph ? DefaultProperties.MicrosoftGraphScopes : null);
SignUpSignInPolicyId = !string.IsNullOrEmpty(applicationParameters.SusiPolicy) ? applicationParameters.SusiPolicy : existingBlock?.GetValue(PropertyNames.SignUpSignInPolicyId)?.ToString() ?? DefaultProperties.SignUpSignInPolicyId;
// TODO determine the SusiPolicy from the graph beta
Authority = IsB2C ? $"{Instance}{Domain}/{SignUpSignInPolicyId}" : $"{Instance}{Domain}";
Authority = IsCIAM ? $"https://{Domain}/" : IsB2C ? $"{Instance}{Domain}/{SignUpSignInPolicyId}" : $"{Instance}{Domain}";
ClientSecret = existingBlock?.GetValue(PropertyNames.ClientSecret)?.ToString() ?? DefaultProperties.ClientSecret;
ClientCertificates = existingBlock?.GetValue(PropertyNames.ClientCertificates)?.ToObject<string[]>();
}
Expand All @@ -99,6 +106,15 @@ public AzureAdBlock(ApplicationParameters applicationParameters, JObject? existi
ValidateAuthority = !IsB2C
};

public dynamic CIAMSettings => new
{
Authority = Authority ?? DefaultProperties.Authority,
ClientId = ClientId ?? DefaultProperties.ClientId,
ClientSecret = ClientSecret ?? DefaultProperties.ClientSecret,
ClientCertificates = ClientCertificates ?? Array.Empty<string>(),
CallbackPath = CallbackPath ?? DefaultProperties.CallbackPath
};

public dynamic WebAppSettings => new
{
Instance = Instance ?? DefaultProperties.Instance,
Expand Down Expand Up @@ -140,6 +156,11 @@ public JObject ToJObject()
return JObject.FromObject(BlazorSettings);
}

if (IsCIAM)
{
return JObject.FromObject(CIAMSettings);
}

var jObject = IsWebApi ? JObject.FromObject(WebApiSettings) : JObject.FromObject(WebAppSettings);

if (IsB2C)
Expand Down
Expand Up @@ -250,7 +250,7 @@
]
},
{
"FileName": "LoginDisplay.razor",
"FileName": "blazorserver_LoginDisplay.razor",
"AddFilePath": "Shared/LoginDisplay.razor"
},
{
Expand All @@ -268,4 +268,4 @@
]
}
]
}
}
Expand Up @@ -193,7 +193,7 @@
"AddFilePath": "Pages/Authentication.razor"
},
{
"FileName": "LoginDisplay.razor",
"FileName": "blazorwasm_LoginDisplay.razor",
"AddFilePath": "Shared/LoginDisplay.razor"
},
{
Expand Down
Expand Up @@ -140,25 +140,23 @@
"LeadingTrivia": {
"Newline": true
}
},
},
{
"CodeChangeType": "Lambda",
"Parent": "WebApplication.CreateBuilder.Services.AddAuthorization",
"Block": "options.FallbackPolicy = options.DefaultPolicy",
"Parameter": "options",
"LeadingTrivia":
{
"Newline":true,
"LeadingTrivia": {
"Newline": true,
"NumberOfSpaces": 4
}
},
{
"Parent": "WebApplication.CreateBuilder.Services.AddRazorPages",
"CodeChangeType": "MemberAccess",
"Block": "AddMicrosoftIdentityUI()",
"LeadingTrivia":
{
"Newline":true,
"LeadingTrivia": {
"Newline": true,
"NumberOfSpaces": 4
}
},
Expand Down Expand Up @@ -187,10 +185,10 @@
},
{
"FileName": "Index.cshtml.cs",
"Options" : [ "MicrosoftGraph", "DownstreamApi" ],
"Options": [ "MicrosoftGraph", "DownstreamApi" ],
"ClassProperties": [
{
"Block" : "private readonly GraphServiceClient _graphServiceClient",
"Block": "private readonly GraphServiceClient _graphServiceClient",
"Options": [ "MicrosoftGraph" ]
},
{
Expand All @@ -209,50 +207,49 @@
}
],
"Methods": {
"OnGet":
{
"EditType" : {
"Block": "async Task",
"Options": ["MicrosoftGraph", "DownstreamApi"]
"OnGet": {
"EditType": {
"Block": "async Task",
"Options": [ "MicrosoftGraph", "DownstreamApi" ]
},
"CodeChanges": [
{
"Block": "var user = await _graphServiceClient.Me.Request().GetAsync();",
"LeadingTrivia": {
"NumberOfSpaces": 12
},
"Options" : [ "MicrosoftGraph"]
"Options": [ "MicrosoftGraph" ]
},
{
"Block": "ViewData[\"GraphApiResult\"] = user.DisplayName;",
"LeadingTrivia": {
"NumberOfSpaces": 12
},
"Options" : [ "MicrosoftGraph"]
"Options": [ "MicrosoftGraph" ]
},
{
"Block" : "using var response = await _downstreamWebApi.CallWebApiForUserAsync(\"DownstreamApi\").ConfigureAwait(false);",
"Block": "using var response = await _downstreamWebApi.CallWebApiForUserAsync(\"DownstreamApi\").ConfigureAwait(false);",
"LeadingTrivia": {
"NumberOfSpaces": 12
},
"Options" : [ "DownstreamApi"]
"Options": [ "DownstreamApi" ]
},
{
"Block": "\n\n if (response.StatusCode == System.Net.HttpStatusCode.OK)\n {\n var apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false);\n ViewData[\"ApiResult\"] = apiResult;\n }\n else\n {\n var error = await response.Content.ReadAsStringAsync().ConfigureAwait(false);\n throw new HttpRequestException($\"Invalid status code in the HttpResponseMessage: {response.StatusCode}: {error}\");\n }",
"Options": [ "DownstreamApi" ]
}
]
},
"IndexModel" : {
"Parameters" : [ "ILogger<IndexModel>" ],
"AddParameters" : [
"IndexModel": {
"Parameters": [ "ILogger<IndexModel>" ],
"AddParameters": [
{
"Block":"GraphServiceClient graphServiceClient",
"Options": [ "MicrosoftGraph"]
"Block": "GraphServiceClient graphServiceClient",
"Options": [ "MicrosoftGraph" ]
},
{
"Block":"IDownstreamWebApi downstreamWebApi",
"Options": [ "DownstreamApi"]
"Block": "IDownstreamWebApi downstreamWebApi",
"Options": [ "DownstreamApi" ]
}
],
"CodeChanges": [
Expand All @@ -273,7 +270,7 @@
]
}
},
"Usings" : [
"Usings": [
"Microsoft.Identity.Web",
"System.Net"
],
Expand Down Expand Up @@ -301,6 +298,30 @@
]
}
}
},
{
"FileName": "_Layout.cshtml",
"Methods": {
"Global": {
"CodeChanges": [
{
"MultiLineBlock": [
"</ul>",
" <partial name=\"_LoginPartial\" />",
" </div>"
],
"ReplaceSnippet": [
"</ul>",
" </div>"
]
}
]
}
}
},
{
"FileName": "LoginPartial.cshtml",
"AddFilePath": "Pages/Shared/_LoginPartial.cshtml"
}
]
}
Expand Up @@ -88,26 +88,21 @@ public async Task AddAuthCodeAsync()
var filteredFiles = codeModifierConfig.Files.Where(f => ProjectModifierHelper.FilterOptions(f.Options, options));
foreach (var file in filteredFiles)
{
await HandleCodeFileAsync(file, project, options, codeModifierConfig.Identifier);
await HandleCodeFileAsync(file, project, options);
}

_consoleLogger.LogJsonMessage(State.Success, output: _output.ToString().TrimEnd());
}

internal static string GetCodeFileString(CodeFile file, string identifier) // todo make all code files strings
internal static string GetCodeFileString(CodeFile file)
{
// Resource files cannot contain '-' (dash) or '.' (period)
var codeFilePropertyName = $"add_{identifier.Replace('-', '_')}_{file.FileName.Replace('.', '_')}";
var codeFilePropertyName = $"add_{file.FileName.Replace('.', '_')}";
var property = AppProvisioningTool.Properties.FirstOrDefault(
p => p.Name.Equals(codeFilePropertyName));

if (property is null)
{
throw new FormatException($"Resource property for {file.FileName} could not be found. ");
}
p => p.Name.Equals(codeFilePropertyName))
?? throw new FormatException($"Resource property for {file.FileName} could not be found. ");

var codeFileString = property.GetValue(typeof(Resources))?.ToString();

if (string.IsNullOrEmpty(codeFileString))
{
throw new FormatException($"CodeFile string for {file.FileName} was empty.");
Expand All @@ -116,7 +111,7 @@ public async Task AddAuthCodeAsync()
return codeFileString;
}

internal static ClassDeclarationSyntax ModifyMethods(string fileName, ClassDeclarationSyntax classNode, DocumentBuilder documentBuilder, Dictionary<string, Method> methods, CodeChangeOptions options, StringBuilder output)
internal static ClassDeclarationSyntax ModifyMethods(string fileName, ClassDeclarationSyntax classNode, Dictionary<string, Method> methods, CodeChangeOptions options, StringBuilder output)
{
foreach ((string methodName, Method methodChanges) in methods)
{
Expand Down Expand Up @@ -283,13 +278,13 @@ internal async Task ModifyCsFile(CodeFile file, CodeAnalysis.Project project, Co
}
}

private async Task HandleCodeFileAsync(CodeFile file, CodeAnalysis.Project project, CodeChangeOptions options, string identifier)
private async Task HandleCodeFileAsync(CodeFile file, CodeAnalysis.Project project, CodeChangeOptions options)
{
try
{
if (!string.IsNullOrEmpty(file.AddFilePath))
{
AddFile(file, identifier);
AddFile(file);
_output.AppendLine(string.Format(Resources.AddedCodeFile, file.AddFilePath));
}
else
Expand Down Expand Up @@ -325,15 +320,15 @@ private async Task HandleCodeFileAsync(CodeFile file, CodeAnalysis.Project proje
/// <param name="file"></param>
/// <param name="identifier"></param>
/// <exception cref="FormatException"></exception>
private void AddFile(CodeFile file, string identifier)
private void AddFile(CodeFile file)
{
var filePath = Path.Combine(_toolOptions.ProjectPath, file.AddFilePath);
if (File.Exists(filePath))
{
return; // File exists, don't need to create
}

var codeFileString = GetCodeFileString(file, identifier);
var codeFileString = GetCodeFileString(file);

var fileDir = Path.GetDirectoryName(filePath);
if (!string.IsNullOrEmpty(fileDir))
Expand Down Expand Up @@ -396,7 +391,7 @@ private void AddFile(CodeFile file, string identifier)
//add class attributes
modifiedClassDeclarationSyntax = documentBuilder.AddClassAttributes(modifiedClassDeclarationSyntax, options);
//add code snippets/changes.
modifiedClassDeclarationSyntax = ModifyMethods(file.FileName, modifiedClassDeclarationSyntax, documentBuilder, file.Methods, options, _output);
modifiedClassDeclarationSyntax = ModifyMethods(file.FileName, modifiedClassDeclarationSyntax, file.Methods, options, _output);

//replace class node with all the updates.
#pragma warning disable CS8631 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type.
Expand Down

0 comments on commit 8b9eb07

Please sign in to comment.