using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Localization; using OpenIddict.Abstractions; using Volo.Abp; using Volo.Abp.Authorization.Permissions; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.OpenIddict.Applications; using Volo.Abp.OpenIddict.Scopes; using Volo.Abp.PermissionManagement; using Volo.Abp.Uow; namespace KonSoft.Shared.DbMigrator.OpenIddict; /* Creates initial data that is needed to property run the application * and make client-to-server communication possible. */ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDependency { private readonly IAbpApplicationManager _applicationManager; private readonly IConfiguration _configuration; private readonly IOpenIddictApplicationRepository _openIddictApplicationRepository; private readonly IOpenIddictScopeRepository _openIddictScopeRepository; private readonly IPermissionDataSeeder _permissionDataSeeder; private readonly IOpenIddictScopeManager _scopeManager; private readonly IStringLocalizer L; public OpenIddictDataSeedContributor( IConfiguration configuration, IOpenIddictApplicationRepository openIddictApplicationRepository, IAbpApplicationManager applicationManager, IOpenIddictScopeRepository openIddictScopeRepository, IOpenIddictScopeManager scopeManager, IPermissionDataSeeder permissionDataSeeder, IStringLocalizer l) { _configuration = configuration; _openIddictApplicationRepository = openIddictApplicationRepository; _applicationManager = applicationManager; _openIddictScopeRepository = openIddictScopeRepository; _scopeManager = scopeManager; _permissionDataSeeder = permissionDataSeeder; L = l; } [UnitOfWork] public virtual async Task SeedAsync(DataSeedContext context) { await CreateScopesAsync(); await CreateApplicationsAsync(); } private async Task CreateScopesAsync() { if (await _openIddictScopeRepository.FindByNameAsync("Admin") == null) { await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor { Name = "Admin", DisplayName = "Admin API", Resources = { "Admin" } }); } if (await _openIddictScopeRepository.FindByNameAsync("Dispatch") == null) { await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor { Name = "Dispatch", DisplayName = "Dispatch API", Resources = { "Dispatch" } }); } if (await _openIddictScopeRepository.FindByNameAsync("Payment") == null) { await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor { Name = "Payment", DisplayName = "Payment API", Resources = { "Payment" } }); } if (await _openIddictScopeRepository.FindByNameAsync("Report") == null) { await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor { Name = "Report", DisplayName = "Report API", Resources = { "Report" } }); } if (await _openIddictScopeRepository.FindByNameAsync("TenantManagement") == null) { await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor { Name = "TenantManagement", DisplayName = "TenantManagement API", Resources = { "TenantManagement" } }); } } private async Task CreateApplicationsAsync() { var commonScopes = new List { OpenIddictConstants.Permissions.Scopes.Address, OpenIddictConstants.Permissions.Scopes.Email, OpenIddictConstants.Permissions.Scopes.Phone, OpenIddictConstants.Permissions.Scopes.Profile, OpenIddictConstants.Permissions.Scopes.Roles, "Admin", "Dispatch", "Payment", "Report", "TenantManagement" }; var apps = new List(); _configuration.GetSection("OpenIddict:Applications").Bind(apps); foreach (var openIddictApplication in apps) { await CreateApplicationAsync(openIddictApplication.Name, openIddictApplication.Type, openIddictApplication.ConsentType, openIddictApplication.DisplayName, openIddictApplication.Secret, openIddictApplication.GrantTypes, commonScopes, openIddictApplication.PostLogoutRedirectUri); } } private async Task CreateApplicationAsync( [NotNull] string name, [NotNull] string type, [NotNull] string consentType, string displayName, string? secret, List grantTypes, List scopes, string? clientUri = null, string? redirectUri = null, string? postLogoutRedirectUri = null, List? permissions = null) { if (!string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) { throw new BusinessException(L["NoClientSecretCanBeSetForPublicApplications"]); } if (string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase)) { throw new BusinessException(L["TheClientSecretIsRequiredForConfidentialApplications"]); } var client = await _openIddictApplicationRepository.FindByClientIdAsync(name); var application = new AbpApplicationDescriptor { ClientId = name, ClientType = type, ClientSecret = secret, ConsentType = consentType, DisplayName = displayName, ClientUri = clientUri }; Check.NotNullOrEmpty(grantTypes, nameof(grantTypes)); Check.NotNullOrEmpty(scopes, nameof(scopes)); if (new[] { OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.Implicit }.All( grantTypes.Contains)) { application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken); if (string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) { application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken); application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeToken); } } if (!redirectUri.IsNullOrWhiteSpace() || !postLogoutRedirectUri.IsNullOrWhiteSpace()) { application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Logout); } var buildInGrantTypes = new[] { OpenIddictConstants.GrantTypes.Implicit, OpenIddictConstants.GrantTypes.Password, OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.ClientCredentials, OpenIddictConstants.GrantTypes.DeviceCode, OpenIddictConstants.GrantTypes.RefreshToken }; foreach (var grantType in grantTypes) { if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode) { application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode); application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Code); } if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode || grantType == OpenIddictConstants.GrantTypes.Implicit) { application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Authorization); } if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode || grantType == OpenIddictConstants.GrantTypes.ClientCredentials || grantType == OpenIddictConstants.GrantTypes.Password || grantType == OpenIddictConstants.GrantTypes.RefreshToken || grantType == OpenIddictConstants.GrantTypes.DeviceCode) { application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Token); application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Revocation); application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Introspection); } if (grantType == OpenIddictConstants.GrantTypes.ClientCredentials) { application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.ClientCredentials); } if (grantType == OpenIddictConstants.GrantTypes.Implicit) { application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Implicit); } if (grantType == OpenIddictConstants.GrantTypes.Password) { application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Password); } if (grantType == OpenIddictConstants.GrantTypes.RefreshToken) { application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.RefreshToken); } if (grantType == OpenIddictConstants.GrantTypes.DeviceCode) { application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.DeviceCode); application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Device); } if (grantType == OpenIddictConstants.GrantTypes.Implicit) { application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdToken); if (string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) { application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken); application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Token); } } if (!buildInGrantTypes.Contains(grantType)) { application.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.GrantType + grantType); } } var buildInScopes = new[] { OpenIddictConstants.Permissions.Scopes.Address, OpenIddictConstants.Permissions.Scopes.Email, OpenIddictConstants.Permissions.Scopes.Phone, OpenIddictConstants.Permissions.Scopes.Profile, OpenIddictConstants.Permissions.Scopes.Roles }; foreach (var scope in scopes) { if (buildInScopes.Contains(scope)) { application.Permissions.Add(scope); } else { application.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.Scope + scope); } } if (redirectUri != null) { if (!redirectUri.IsNullOrEmpty()) { if (!Uri.TryCreate(redirectUri, UriKind.Absolute, out var uri) || !uri.IsWellFormedOriginalString()) { throw new BusinessException(L["InvalidRedirectUri", redirectUri]); } if (application.RedirectUris.All(x => x != uri)) { application.RedirectUris.Add(uri); } } } if (postLogoutRedirectUri != null) { if (!postLogoutRedirectUri.IsNullOrEmpty()) { if (!Uri.TryCreate(postLogoutRedirectUri, UriKind.Absolute, out var uri) || !uri.IsWellFormedOriginalString()) { throw new BusinessException(L["InvalidPostLogoutRedirectUri", postLogoutRedirectUri]); } if (application.PostLogoutRedirectUris.All(x => x != uri)) { application.PostLogoutRedirectUris.Add(uri); } } } if (permissions != null) { await _permissionDataSeeder.SeedAsync( ClientPermissionValueProvider.ProviderName, name, permissions ); } if (client == null) { await _applicationManager.CreateAsync(application); return; } if (!HasSameRedirectUris(client, application)) { client.RedirectUris = JsonSerializer.Serialize(application.RedirectUris.Select(q => q.ToString().TrimEnd('/'))); client.PostLogoutRedirectUris = JsonSerializer.Serialize(application.PostLogoutRedirectUris.Select(q => q.ToString().TrimEnd('/'))); await _applicationManager.UpdateAsync(client.ToModel()); } if (!HasSameScopes(client, application)) { client.Permissions = JsonSerializer.Serialize(application.Permissions.Select(q => q.ToString())); await _applicationManager.UpdateAsync(client.ToModel()); } } private bool HasSameRedirectUris(OpenIddictApplication existingClient, AbpApplicationDescriptor application) { return existingClient.RedirectUris == JsonSerializer.Serialize(application.RedirectUris.Select(q => q.ToString().TrimEnd('/'))); } private bool HasSameScopes(OpenIddictApplication existingClient, AbpApplicationDescriptor application) { return existingClient.Permissions == JsonSerializer.Serialize(application.Permissions.Select(q => q.ToString().TrimEnd('/'))); } }