This commit is contained in:
Jasper Hedegaard Bojsen 2023-10-30 12:53:21 +01:00
Родитель aa93be8e60
Коммит e5c701c071
17 изменённых файлов: 120 добавлений и 120 удалений

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

@ -9,7 +9,7 @@ permissions:
contents: read
env:
AZURE_WEBAPP_NAME: 'admin-api-asdk-test-5yb3' # set this to your application's name
AZURE_WEBAPP_NAME: 'admin-api-asdk-test-83sx' # set this to your application's name
AZURE_WEBAPP_PACKAGE_PATH: . # set this to the path to your web app project, defaults to the repository root
DOTNET_VERSION: 7.x.x
PROJECT_DIR: ./src/Saas.Admin/Saas.Admin.Service

2
.github/workflows/saas-app-deploy.yml поставляемый
Просмотреть файл

@ -9,7 +9,7 @@ permissions:
contents: read
env:
AZURE_WEBAPP_NAME: 'saas-app-asdk-test-aiuq' # set this to your application's name
AZURE_WEBAPP_NAME: 'saas-app-asdk-test-83sx' # set this to your application's name
AZURE_WEBAPP_PACKAGE_PATH: . # set this to the path to your web app project, defaults to the repository root
DOTNET_VERSION: 7.x.x
PROJECT_DIR: ./src/Saas.Application/Saas.Application.Web

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

@ -9,7 +9,7 @@ permissions:
contents: read
env:
AZURE_WEBAPP_NAME: 'signupadmin-app-asdk-test-aiuq' # set this to your application's name
AZURE_WEBAPP_NAME: 'signupadmin-app-asdk-test-83sx' # set this to your application's name
AZURE_WEBAPP_PACKAGE_PATH: . # set this to the path to your web app project, defaults to the repository root
DOTNET_VERSION: 7.x.x
PROJECT_DIR: ./src/Saas.SignupAdministration/Saas.SignupAdministration.Web

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

@ -12,23 +12,10 @@
<ItemGroup>
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.2.2" />
<PackageReference Include="Azure.Identity" Version="1.10.3" />
<PackageReference Include="Azure.Security.KeyVault.Certificates" Version="4.5.1" />
<PackageReference Include="Dawn.Guard" Version="1.12.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.21.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="7.0.13" />
<PackageReference Include="Microsoft.Extensions.Configuration.AzureAppConfiguration" Version="6.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.13" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.13">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Identity.Web" Version="2.15.3" />
<PackageReference Include="Microsoft.Identity.Web.UI" Version="2.15.3" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.11" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">

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

@ -10,7 +10,7 @@ permissions:
env:
APP_NAME: admin-api
AZURE_WEBAPP_NAME: admin-api-asdk-test-aiuq # set this to your application's name
AZURE_WEBAPP_NAME: admin-api-asdk-test-83sx # set this to your application's name
AZURE_WEBAPP_PACKAGE_PATH: . # set this to the path to your web app project, defaults to the repository root
DOTNET_VERSION: 7.x.x
PROJECT_DIR: ./src/Saas.Admin/Saas.Admin.Service

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

@ -9,17 +9,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.2.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.AzureAppConfiguration" Version="6.1.1" />
<PackageReference Include="Azure.Identity" Version="1.10.3" />
<PackageReference Include="Humanizer" Version="2.14.1" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.21.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.13" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.13">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Identity.Web" Version="2.15.3" />
<PackageReference Include="Microsoft.Identity.Web.UI" Version="2.15.3" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.11" />
</ItemGroup>

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

@ -10,7 +10,7 @@ permissions:
env:
APP_NAME: saas-app
AZURE_WEBAPP_NAME: saas-app-asdk-test-aiuq # set this to your application's name
AZURE_WEBAPP_NAME: saas-app-asdk-test-83sx # set this to your application's name
AZURE_WEBAPP_PACKAGE_PATH: . # set this to the path to your web app project, defaults to the repository root
DOTNET_VERSION: 7.x.x
PROJECT_DIR: ./src/Saas.Application/Saas.Application.Web

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

@ -16,24 +16,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.2.2" />
<PackageReference Include="Azure.Identity" Version="1.10.3" />
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.5.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.21.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Certificate" Version="7.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.13" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.13" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.13" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.13">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.AzureAppConfiguration" Version="6.1.1" />
<PackageReference Include="Microsoft.Graph" Version="4.54.0" />
<PackageReference Include="Microsoft.Identity.Web" Version="2.15.3" />
<PackageReference Include="Microsoft.Identity.Web.MicrosoftGraph" Version="2.15.3" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.11" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="7.0.13" />
</ItemGroup>

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

@ -1,5 +1,6 @@
using Microsoft.Extensions.Options;
using Microsoft.Graph;
using Microsoft.Graph.Models;
using Saas.Permissions.Service.Exceptions;
using Saas.Permissions.Service.Interfaces;
using Saas.Permissions.Service.Models;
@ -57,22 +58,24 @@ public class GraphAPIService : IGraphAPIService
try
{
var graphUsers = await _graphServiceClient.Users
.Request()
.Filter($"identities/any(id: id/issuer eq '{_permissionOptions.Domain}' and id/issuerAssignedId eq '{userEmail}')")
.Select("id, identitied, displayName")
.GetAsync();
.GetAsync(requestionConfiguration =>
{
requestionConfiguration.QueryParameters.Filter = $"identities/any(id: id/issuer eq '{_permissionOptions.Domain}' and id/issuerAssignedId eq '{userEmail}')";
requestionConfiguration.QueryParameters.Select = new string[] { "id, identities, displayName" };
});
if (graphUsers.Count > 1)
if (graphUsers?.Value?.Count > 1)
{
throw new UserNotFoundException($"More than one user with the email {userEmail} exists in the Identity provider");
}
if (graphUsers.Count == 0)
if (graphUsers?.Value?.Count == 0 || graphUsers?.Value is null)
{
throw new UserNotFoundException($"The user with the email {userEmail} was not found in the Identity Provider");
}
// Ok to just return first, because at this point we've verified we have exactly 1 user in the graphUsers object.
return ToUserObjects(graphUsers).First();
return ToUserObjects(graphUsers.Value).First();
}
catch (Exception ex)
{
@ -84,29 +87,34 @@ public class GraphAPIService : IGraphAPIService
// Enriches the user object with data from Microsoft Graph.
public async Task<IEnumerable<Models.User>> GetUsersByIds(ICollection<Guid> userIds)
{
// Build graph query: "id in ('id1', 'id2')"
// https://docs.microsoft.com/en-us/graph/aad-advanced-queries?tabs=csharp
StringBuilder filter = new();
filter.Append("id in (");
filter.Append(string.Join(",", userIds.Select(id => $"'{id}'")));
filter.Append(')');
List<Models.User> userList = new();
try
{
var graphUsers = await _graphServiceClient.Users
.Request()
.Filter(filter.ToString())
.GetAsync();
.GetAsync(requestConfiguration =>
{
requestConfiguration.QueryParameters.Filter = MakeUserFilter();
});
userList.AddRange(ToUserObjects(graphUsers));
while (graphUsers.NextPageRequest is not null)
if (graphUsers?.Value is null)
{
graphUsers = await graphUsers.NextPageRequest.GetAsync();
userList.AddRange(ToUserObjects(graphUsers));
};
return userList;
}
PageIterator<Microsoft.Graph.Models.User, UserCollectionResponse> pageIterator
= PageIterator<Microsoft.Graph.Models.User, UserCollectionResponse>
.CreatePageIterator(
_graphServiceClient,
graphUsers,
(msg) =>
{
userList.Add(ToUserObject(msg));
return true;
});
await pageIterator.IterateAsync();
return userList;
}
@ -115,17 +123,31 @@ public class GraphAPIService : IGraphAPIService
_logError(_logger, ex);
throw;
}
string MakeUserFilter ()
{
// Build graph query: "id in ('id1', 'id2')"
// https://docs.microsoft.com/en-us/graph/aad-advanced-queries?tabs=csharp
StringBuilder filter = new();
filter.Append("id in (");
filter.Append(string.Join(",", userIds.Select(id => $"'{id}'")));
filter.Append(')');
return filter.ToString();
}
}
private async Task<ServicePrincipal?> GetServicePrincipalAsync(string clientId)
{
try
{
var servicePrincipal = await _graphServiceClient.ServicePrincipals.Request()
.Filter($"appId eq '{clientId}'")
.GetAsync();
var servicePrincipal = await _graphServiceClient.ServicePrincipals
.GetAsync(requestConfiguration =>
{
requestConfiguration.QueryParameters.Filter = $"appId eq '{clientId}'";
});
return servicePrincipal.SingleOrDefault();
return servicePrincipal?.Value?.SingleOrDefault();
}
catch (Exception ex)
{
@ -139,17 +161,38 @@ public class GraphAPIService : IGraphAPIService
try
{
var userAppRoleAssignments = await _graphServiceClient.Users[userId].AppRoleAssignments
.Request()
.Filter($"resourceId eq {servicePrincipal.Id}")
.GetAsync();
.GetAsync(requestConfiguration =>
{
requestConfiguration.Equals($"resourceId eq {servicePrincipal.Id}");
}) ?? throw new ArgumentException($"App role not found for \"{servicePrincipal.AppId}\".");
var appRoleIds = userAppRoleAssignments.Select(a => a.AppRoleId);
var appRoleIds = userAppRoleAssignments?.Value?
.Where(appRole => appRole is not null)
.Where(appRole => appRole.AppRoleId is not null)
.Select(appRole => appRole.AppRoleId);
var appRoles = servicePrincipal.AppRoles
.Where(a => appRoleIds.Contains(a.Id))
.Select(a => a.Value);
if (appRoleIds is null || !appRoleIds.Any())
{
throw new ArgumentException($"App role not found for \"{servicePrincipal.AppId}\".");
}
return appRoles.ToArray();
var appRoles = servicePrincipal.AppRoles?
.Where(appRole => appRole?.Id is not null)
.Where(appRole => appRoleIds.Contains(appRole.Id));
if (appRoles is null || !appRoles.Any())
{
throw new ArgumentException($"App role not found for \"{servicePrincipal.AppId}\".");
}
var roleClaimsArray = appRoles
.Select(appRole => appRole.Value ?? string.Empty)
.Where(x => !string.IsNullOrEmpty(x))
.ToArray();
return roleClaimsArray is not null && roleClaimsArray.Any()
? roleClaimsArray
: throw new ArgumentException($"App role not found for \"{servicePrincipal.AppId}\".");
}
catch (Exception ex)
{
@ -158,12 +201,19 @@ public class GraphAPIService : IGraphAPIService
}
}
private static IEnumerable<Models.User> ToUserObjects(IGraphServiceUsersCollectionPage graphUsers) =>
private static IEnumerable<Models.User> ToUserObjects(IEnumerable<Microsoft.Graph.Models.User> graphUsers) =>
graphUsers.Select(graphUser => new Models.User()
{
UserId = graphUser.Id,
DisplayName = graphUser.DisplayName
});
private static Models.User ToUserObject(Microsoft.Graph.Models.User graphUser) =>
new()
{
UserId = graphUser.Id,
DisplayName = graphUser.DisplayName
};
}

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

@ -2,6 +2,7 @@
using Microsoft.Graph;
using Saas.Shared.Options;
using Saas.Permissions.Service.Interfaces;
using Microsoft.Kiota.Abstractions.Authentication;
namespace Saas.Permissions.Service.Services;
@ -21,10 +22,6 @@ public class GraphApiClientFactory : IGraphApiClientFactory
_httpClient = httpClient;
}
public GraphServiceClient Create() =>
new(_httpClient, _msGraphOptions.BaseUrl)
{
AuthenticationProvider = _authenticationProvider
};
public GraphServiceClient Create() =>
new(_httpClient, _authenticationProvider, _msGraphOptions.BaseUrl);
}

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

@ -1,6 +1,6 @@

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Graph;
using Microsoft.Kiota.Abstractions.Authentication;
using Saas.Identity.Crypto;
using Saas.Identity.Interface;
using Saas.Identity.Provider;

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

@ -1,9 +1,9 @@

using Microsoft.Extensions.Logging;
using Microsoft.Graph;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Abstractions.Authentication;
using Saas.Shared.Interface;
using Saas.Shared.Options;
using System.Net.Http.Headers;
namespace Saas.Identity.Provider;
public class SaasGraphClientCredentialsProvider<TOptions> : IAuthenticationProvider
@ -26,12 +26,28 @@ public class SaasGraphClientCredentialsProvider<TOptions> : IAuthenticationProvi
_authProvider = authProvider;
}
public async Task AuthenticateRequestAsync(HttpRequestMessage requestMessage)
//public async Task AuthenticateRequestAsync(HttpRequestMessage requestMessage)
//{
// try
// {
// requestMessage.Headers.Authorization =
// new AuthenticationHeaderValue("bearer", await _authProvider.GetAccessTokenAsync());
// }
// catch (Exception ex)
// {
// _logError(_logger, ex);
// throw;
// }
//}
public async Task AuthenticateRequestAsync(
RequestInformation request,
Dictionary<string, object>? additionalAuthenticationContext = null,
CancellationToken cancellationToken = default)
{
try
{
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("bearer", await _authProvider.GetAccessTokenAsync());
request.Headers.Add("Authorization", $"bearer { await _authProvider.GetAccessTokenAsync() }");
}
catch (Exception ex)
{

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

@ -9,7 +9,8 @@
<ItemGroup>
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.5.0" />
<PackageReference Include="Microsoft.Graph" Version="4.54.0" />
<PackageReference Include="Microsoft.Identity.Web" Version="2.15.3" />
<PackageReference Include="Microsoft.Graph" Version="5.32.0" />
</ItemGroup>
<ItemGroup>

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

@ -8,7 +8,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Identity.Web" Version="2.15.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>

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

@ -1,27 +0,0 @@
//using Microsoft.AspNetCore.Authentication.OpenIdConnect;
//namespace Microsoft.Identity.Web.UI.Areas.MicrosoftIdentity.Controllers;
//// https://damienbod.com/2022/05/16/using-multiple-azure-b2c-user-flows-from-asp-net-core/
//[AllowAnonymous]
//[Route("MicrosoftIdentity/[controller]/[action]")]
//public class AccountSignUpController : Controller
//{
// [HttpGet("{scheme?}")]
// public IActionResult SignUpPolicy(
// [FromRoute] string scheme,
// [FromQuery] string redirectUri)
// {
// scheme ??= OpenIdConnectDefaults.AuthenticationScheme;
// string redirect = !string.IsNullOrEmpty(redirectUri) && Url.IsLocalUrl(redirectUri)
// ? redirectUri
// : Url.Content("~/");
// AuthenticationProperties properties = new() { RedirectUri = redirect };
// properties.Items[Constants.Policy] = "B2C_1A_SIGNUP_SIGNIN";
// return Challenge(properties, scheme);
// }
//}

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

@ -62,7 +62,7 @@
<footer class="border-top footer text-muted">
<div class="container">
<i class="fab fa-microsoft"></i> Microsoft - &copy; 2022 - Azure SaaS - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
<i class="fab fa-microsoft"></i> Microsoft - &copy; 2023 - Azure SaaS - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>

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

@ -10,7 +10,7 @@ permissions:
env:
APP_NAME: signupadmin-app
AZURE_WEBAPP_NAME: signupadmin-app-asdk-test-aiuq # set this to your application's name
AZURE_WEBAPP_NAME: signupadmin-app-asdk-test-83sx # set this to your application's name
AZURE_WEBAPP_PACKAGE_PATH: . # set this to the path to your web app project, defaults to the repository root
DOTNET_VERSION: 7.x.x
PROJECT_DIR: ./src/Saas.SignupAdministration/Saas.SignupAdministration.Web