public async Task ApplySecurityContractDefinitionAsync(SecurityContract securityContract, Guid updatedById, bool dryRun = false) { // Start transactions to allow complete rollback in case of an error BeginAllTransactions(); try { SecurityContractDryRunResult securityContractDryRunResult = new SecurityContractDryRunResult(); // First apply all of the application(micro-service) definitions that present within the Security Contract. // All the components of a security contract are optional, so check for this here. if (securityContract.Applications != null && securityContract.Applications.Count > 0) { foreach (var applicationSecurityContractDefinition in securityContract.Applications) { await securityContractApplicationService.ApplyResourceServerDefinitionAsync(applicationSecurityContractDefinition, updatedById, dryRun, securityContractDryRunResult); } } // Apply any clients that may be defined within the security contract. if (securityContract.Clients != null && securityContract.Clients.Count > 0) { foreach (var clientSecurityContractDefinition in securityContract.Clients) { await clientService.ApplyClientDefinitionAsync(clientSecurityContractDefinition, dryRun, securityContractDryRunResult); } } // Apply any default configurations that may be defined within the security contract. if (securityContract.DefaultConfigurations != null && securityContract.DefaultConfigurations.Count > 0) { foreach (var defaultConfiguration in securityContract.DefaultConfigurations) { await securityContractDefaultConfigurationService.ApplyDefaultConfigurationDefinitionAsync(defaultConfiguration, updatedById, dryRun, securityContractDryRunResult); } } if (!dryRun) { // All successful CommitAllTransactions(); } else { var securityContractDryRunException = new SecurityContractDryRunException { ValidationErrors = securityContractDryRunResult.ValidationErrors, ValidationWarnings = securityContractDryRunResult.ValidationWarnings }; throw securityContractDryRunException; } } catch { RollbackAllTransactions(); throw; } }
public async Task <Oauth2Client> ApplyClientDefinitionAsync(Oauth2ClientSubmit oauth2ClientSubmit, bool dryRun, SecurityContractDryRunResult securityContractDryRunResult) { logger.Debug($"[client.clientId: '{oauth2ClientSubmit.ClientId}']: Applying client definition for client: '{oauth2ClientSubmit.ClientId}'."); IdentityServer4.EntityFramework.Entities.Client client = await identityClientRepository.GetByClientIdAsync(oauth2ClientSubmit.ClientId); bool newClient = false; if (client == null) { client = new IdentityServer4.EntityFramework.Entities.Client(); newClient = true; } client.AllowOfflineAccess = oauth2ClientSubmit.AllowedOfflineAccess; client.ClientId = oauth2ClientSubmit.ClientId; client.ClientName = oauth2ClientSubmit.Name; // The following properties of clients are not externally configurable, but we do need to add them th clients to get the desired behaviour. client.UpdateAccessTokenClaimsOnRefresh = true; client.AlwaysSendClientClaims = true; client.AlwaysIncludeUserClaimsInIdToken = true; client.RequireConsent = false; if (oauth2ClientSubmit.AccessTokenLifetime > 0) { client.AccessTokenLifetime = oauth2ClientSubmit.AccessTokenLifetime; } if (oauth2ClientSubmit.IdentityTokenLifetime > 0) { client.IdentityTokenLifetime = oauth2ClientSubmit.IdentityTokenLifetime; } client.RefreshTokenExpiration = (int)TokenExpiration.Absolute; client.RefreshTokenUsage = (int)TokenUsage.OneTimeOnly; ApplyClientAllowedScopes(client, oauth2ClientSubmit); ApplyClientAllowedGrantTypes(client, oauth2ClientSubmit); ApplyClientSecrets(client, oauth2ClientSubmit); ApplyClientRedirectUris(client, oauth2ClientSubmit); ApplyClientPostLogoutRedirectUris(client, oauth2ClientSubmit); ApplyClientAllowedCorsOrigins(client, oauth2ClientSubmit, dryRun, securityContractDryRunResult); if (newClient) { logger.Debug($"[client.clientId: '{oauth2ClientSubmit.ClientId}']: Client '{oauth2ClientSubmit.ClientId}' does not exist. Creating it."); return(mapper.Map <Oauth2Client>(await identityClientRepository.CreateAsync(client))); } logger.Debug($"[client.clientId: '{oauth2ClientSubmit.ClientId}']: Client '{oauth2ClientSubmit.ClientId}' already exists. Updating it."); return(mapper.Map <Oauth2Client>(await identityClientRepository.UpdateAsync(client))); }
private void ApplyClientAllowedCorsOrigins(IdentityServer4.EntityFramework.Entities.Client client, Oauth2ClientSubmit oauth2ClientSubmit, bool dryRun, SecurityContractDryRunResult securityContractDryRunResult) { client.AllowedCorsOrigins = new List <ClientCorsOrigin>(); if (oauth2ClientSubmit.AllowedCorsOrigins != null && oauth2ClientSubmit.AllowedCorsOrigins.Any()) { foreach (var corsOrigin in oauth2ClientSubmit.AllowedCorsOrigins) { if (string.IsNullOrWhiteSpace(corsOrigin)) { var errMessage = $"[client.clientId: '{oauth2ClientSubmit.ClientId}']: Empty or null 'allowedCorsOrigin' element declared for client: '{oauth2ClientSubmit.ClientId}'"; if (dryRun) { securityContractDryRunResult.ValidationErrors.Add(errMessage); } else { throw new InvalidFormatException(errMessage); } } client.AllowedCorsOrigins.Add(new ClientCorsOrigin { Client = client, Origin = corsOrigin }); } } }
private async Task <ApplicationModel> CreateApplication(SecurityContractApplication applicationSecurityContractDefinition, Guid updatedByGuid, bool dryRun, SecurityContractDryRunResult securityContractDryRunResult) { await CreateApiResourceForApplicationOnIdentityServer(applicationSecurityContractDefinition); // Create the A3S representation of the resource. ApplicationModel application = new ApplicationModel { Name = applicationSecurityContractDefinition.Fullname, ChangedBy = updatedByGuid, ApplicationFunctions = new List <ApplicationFunctionModel>(), ApplicationDataPolicies = new List <ApplicationDataPolicyModel>() }; if (applicationSecurityContractDefinition.ApplicationFunctions != null) { foreach (var function in applicationSecurityContractDefinition.ApplicationFunctions) { // Application functions should be unique, check that another one does not exist prior to attempting to add it to the application. var existingApplicationFunction = await applicationFunctionRepository.GetByNameAsync(function.Name); if (existingApplicationFunction != null) { var errorMessage = $"[applications.fullname: '{applicationSecurityContractDefinition.Fullname}'].[applicationFunctions.name: '{function.Name}']: Cannot create application function '{function.Name}', as there is already an application function with this nam assigned to another application."; if (dryRun) { securityContractDryRunResult.ValidationErrors.Add(errorMessage); // Attempting to add the function anyway would result in a uniqueness contraint violation and break the transaction. continue; } throw new ItemNotProcessableException(errorMessage); } logger.Error($"Adding function {function.Name} to application."); application.ApplicationFunctions.Add(CreateNewApplicationFunctionFromSecurityContractApplicationFunction(function, updatedByGuid, applicationSecurityContractDefinition.Fullname, dryRun, securityContractDryRunResult)); } } // Set an initial value to the un-saved model. ApplicationModel newApplication = await applicationRepository.CreateAsync(application); return(await SynchroniseApplicationDataPoliciesWithSecurityContract(newApplication, applicationSecurityContractDefinition, updatedByGuid, dryRun, securityContractDryRunResult)); }
private void AssignExistingPermissionToApplicationFunction(PermissionModel existingPermission, string applicationName, ApplicationFunctionModel applicationFunction, SecurityContractPermission permission, bool dryRun, SecurityContractDryRunResult securityContractDryRunResult, Guid updatedByGuid) { if (ExistingPermissionIsAssignedToAnotherApplication(existingPermission, applicationName)) { var errorMessage = $"[applications.fullname: '{applicationName}'].[applicationFunctions.name: '{applicationFunction.Name}'].[permissions.name: '{permission.Name}']: Permission name exists, but is not assigned to application '{applicationName}'. Cannot assign it to application '{applicationName}', as permissions can only be assigned to a single application"; if (dryRun) { securityContractDryRunResult.ValidationErrors.Add(errorMessage); return; } throw new ItemNotProcessableException(errorMessage); } logger.Debug($"[applications.fullname: '{applicationName}'].[applicationFunctions.name: '{applicationFunction.Name}'].[permissions.name: '{permission.Name}']: Permission '{permission.Name}' already assigned to application '{applicationName}'. Updating it."); var applicationFunctionPermission = applicationFunction.ApplicationFunctionPermissions.Find(fp => fp.Permission.Name == permission.Name); // This check will be true if the permission was assigned to another function attached to the same application. Prevent this! if (applicationFunctionPermission == null) { var errorMessage = $"[applications.fullname: '{applicationName}'].[applicationFunctions.name: '{applicationFunction.Name}'].[permissions.name: '{permission.Name}']: Permission '{permission.Name}' already assigned to another application function within application '{applicationName}'. This is prohibited."; if (dryRun) { securityContractDryRunResult.ValidationErrors.Add(errorMessage); return; } throw new ItemNotProcessableException(errorMessage); } // Still check if the permission is to be updated. if (applicationFunctionPermission.Permission.Description != permission.Description) { applicationFunctionPermission.Permission.Description = permission.Description; applicationFunctionPermission.Permission.ChangedBy = updatedByGuid; } }
public async Task <ApplicationModel> ApplyResourceServerDefinitionAsync(SecurityContractApplication applicationSecurityContractDefinition, Guid updatedById, bool dryRun, SecurityContractDryRunResult securityContractDryRunResult) { logger.Debug($"[applications.fullname: '{applicationSecurityContractDefinition.Fullname}']: Applying application security contract definition for application: '{applicationSecurityContractDefinition.Fullname}'"); // Attempt to load any existing application by name, as the name is essentially the API primary key. var application = await applicationRepository.GetByNameAsync(applicationSecurityContractDefinition.Fullname); if (application == null) { logger.Debug($"[applications.fullname: '{applicationSecurityContractDefinition.Fullname}']: Application '{applicationSecurityContractDefinition.Fullname}' not found in database. Creating new application."); return(await CreateApplication(applicationSecurityContractDefinition, updatedById, dryRun, securityContractDryRunResult)); } logger.Debug($"[applications.fullname: '{applicationSecurityContractDefinition.Fullname}']: Application '{applicationSecurityContractDefinition.Fullname}' already exists. Updating it."); return(await UpdateExistingApplication(application, applicationSecurityContractDefinition, updatedById, dryRun, securityContractDryRunResult)); }
private ApplicationFunctionModel CreateNewApplicationFunctionFromSecurityContractApplicationFunction(SecurityContractFunction functionResource, Guid updatedByGuid, string applicationName, bool dryRun, SecurityContractDryRunResult securityContractDryRunResult) { logger.Debug($"[applications.fullname: '{applicationName}'].[applicationFunctions.name: '{functionResource.Name}']: Adding function '{functionResource.Name}' to application '{applicationName}'."); ApplicationFunctionModel newFunction = new ApplicationFunctionModel { Name = functionResource.Name, Description = functionResource.Description, ChangedBy = updatedByGuid }; newFunction.ApplicationFunctionPermissions = new List <ApplicationFunctionPermissionModel>(); if (functionResource.Permissions != null) { foreach (var permission in functionResource.Permissions) { AddSecurityContractPermissionToApplicationFunctionAndUpdatePermissionIfChanged(newFunction, permission, updatedByGuid, applicationName, dryRun, securityContractDryRunResult); } } return(newFunction); }
private void AddSecurityContractPermissionToApplicationFunctionAndUpdatePermissionIfChanged(ApplicationFunctionModel applicationFunction, SecurityContractPermission permission, Guid updatedByGuid, string applicationName, bool dryRun, SecurityContractDryRunResult securityContractDryRunResult) { logger.Debug($"[applications.fullname: '{applicationName}'].[applicationFunctions.name: '{applicationFunction.Name}'].[permissions.name: '{permission.Name}']: Attempting to assign permission '{permission.Name}' to function: {applicationFunction.Name}."); // Check if there is an existing permission within the database. Add this one if found, but only if it is assigned to the current application, else create a new one and add it. var existingPermission = permissionRepository.GetByName(permission.Name, true); if (existingPermission != null) { AssignExistingPermissionToApplicationFunction(existingPermission, applicationName, applicationFunction, permission, dryRun, securityContractDryRunResult, updatedByGuid); } else { AssignNewPermissionToApplicationFunction(applicationName, applicationFunction, permission, updatedByGuid); } }
private async Task <ApplicationModel> SynchroniseFunctionsFromResourceServerDefinitionToApplication(ApplicationModel application, SecurityContractApplication applicationSecurityContractDefinition, Guid updatedByGuid, bool dryRun, SecurityContractDryRunResult securityContractDryRunResult) { if (applicationSecurityContractDefinition.ApplicationFunctions == null) { return(application); } foreach (var functionResource in applicationSecurityContractDefinition.ApplicationFunctions) { await SynchroniseSpecificFunctionFromResourceServerDefinitionToApplication(functionResource, application, applicationSecurityContractDefinition, updatedByGuid, dryRun, securityContractDryRunResult); } return(await applicationRepository.UpdateAsync(application)); }
private async Task SynchroniseSpecificFunctionFromResourceServerDefinitionToApplication(SecurityContractFunction functionResource, ApplicationModel application, SecurityContractApplication applicationSecurityContractDefinition, Guid updatedByGuid, bool dryRun, SecurityContractDryRunResult securityContractDryRunResult) { var applicationFunction = application.ApplicationFunctions.Find(af => af.Name == functionResource.Name); if (applicationFunction == null) { logger.Debug($"[applications.fullname: '{application.Name}'].[applicationFunctions.name: '{functionResource.Name}']: Application function with name '{functionResource.Name}' does not exist. Creating it."); // We now know this application does not have a function with the name assigned. However, another one might, check for this. var existingApplicationFunction = await applicationFunctionRepository.GetByNameAsync(functionResource.Name); if (existingApplicationFunction != null) { var errorMessage = $"[applications.fullname: '{application.Name}'].[applicationFunctions.name: '{functionResource.Name}']: Application function with name '{functionResource.Name}' already exists in another application. Cannot assign it to application: '{application.Name}'"; if (dryRun) { securityContractDryRunResult.ValidationErrors.Add(errorMessage); return; } throw new ItemNotProcessableException(errorMessage); } application.ApplicationFunctions.Add(CreateNewApplicationFunctionFromSecurityContractApplicationFunction(functionResource, updatedByGuid, applicationSecurityContractDefinition.Fullname, dryRun, securityContractDryRunResult)); } else { logger.Debug($"[applications.fullname: '{application.Name}'].[applicationFunctions.name: '{functionResource.Name}']: Application function with name '{functionResource.Name}' already exists. Updating it."); // Edit an existing function. applicationFunction.Name = functionResource.Name; applicationFunction.Description = functionResource.Description; applicationFunction.ChangedBy = updatedByGuid; if (functionResource.Permissions != null) { DetectAndUnassignPermissionsRemovedFromFunctions(applicationFunction, functionResource); // Add any new permissions to the function. foreach (var permission in functionResource.Permissions) { AddSecurityContractPermissionToApplicationFunctionAndUpdatePermissionIfChanged(applicationFunction, permission, updatedByGuid, applicationSecurityContractDefinition.Fullname, dryRun, securityContractDryRunResult); } } else { // Remove any possible permissions that are assigned to the application function. applicationFunction.ApplicationFunctionPermissions.Clear(); } } }
private async Task <ApplicationModel> SynchroniseFunctions(ApplicationModel application, SecurityContractApplication applicationSecurityContractDefinition, Guid updatedByGuid, bool dryRun, SecurityContractDryRunResult securityContractDryRunResult) { await DetectApplicationFunctionsRemovedFromSecurityContractAndRemoveFromApplication(application, applicationSecurityContractDefinition); await permissionRepository.DeletePermissionsNotAssignedToApplicationFunctionsAsync(); await SynchroniseFunctionsFromResourceServerDefinitionToApplication(application, applicationSecurityContractDefinition, updatedByGuid, dryRun, securityContractDryRunResult); return(application); }
private async Task <ApplicationModel> UpdateExistingApplication(ApplicationModel application, SecurityContractApplication applicationSecurityContractDefinition, Guid updatedById, bool dryRun, SecurityContractDryRunResult securityContractDryRunResult) { var updatedApplication = await SynchroniseFunctions(application, applicationSecurityContractDefinition, updatedById, dryRun, securityContractDryRunResult); await SynchroniseApplicationDataPoliciesWithSecurityContract(application, applicationSecurityContractDefinition, updatedById, dryRun, securityContractDryRunResult); return(updatedApplication); }
private async Task AddSpecificApplicationDataPolyFromSecurityContractToApplication(ApplicationModel application, SecurityContractApplicationDataPolicy dataPolicyToAdd, Guid updatedById, bool dryRun, SecurityContractDryRunResult securityContractDryRunResult) { logger.Debug($"[applications.fullname: '{application.Name}'].[dataPolicies.name: '{dataPolicyToAdd.Name}']: Adding data policy '{dataPolicyToAdd.Name}' to application '{application.Name}'."); var existingDataPolicy = application.ApplicationDataPolicies.Find(adp => adp.Name == dataPolicyToAdd.Name); if (existingDataPolicy == null) { //check that the data policy does not exist within other applications. var dataPolicyAttachedToOtherApplication = await applicationDataPolicyRepository.GetByNameAsync(dataPolicyToAdd.Name); if (dataPolicyAttachedToOtherApplication != null) { var errorMessage = $"[applications.fullname: '{application.Name}'].[dataPolicies.name: '{dataPolicyToAdd.Name}']: Data policy with name alreay exists in another application. Not adding it!"; if (dryRun) { securityContractDryRunResult.ValidationErrors.Add(errorMessage); return; } throw new ItemNotProcessableException(errorMessage); } logger.Debug($"[applications.fullname: '{application.Name}'].[dataPolicies.name: '{dataPolicyToAdd.Name}']: Data policy '{dataPolicyToAdd.Name}' was not assigned to application '{application.Name}'. Adding it."); application.ApplicationDataPolicies.Add(new ApplicationDataPolicyModel { Name = dataPolicyToAdd.Name, Description = dataPolicyToAdd.Description, ChangedBy = updatedById }); } else { logger.Debug($"[applications.fullname: '{application.Name}'].[dataPolicies.name: '{dataPolicyToAdd.Name}']: Data policy '{dataPolicyToAdd.Name}' is currently assigned to application '{application.Name}'. Updating it."); // Bind possible changes to the editable components of the data policy. existingDataPolicy.Description = dataPolicyToAdd.Description; existingDataPolicy.ChangedBy = updatedById; } }
private async Task <ApplicationModel> AddApplicationDataPoliciesFromSecurityContractToApplication(ApplicationModel application, SecurityContractApplication applicationSecurityContractDefinition, Guid updatedById, bool dryRun, SecurityContractDryRunResult securityContractDryRunResult) { if (applicationSecurityContractDefinition.DataPolicies != null && applicationSecurityContractDefinition.DataPolicies.Any()) { foreach (var dataPolicyToAdd in applicationSecurityContractDefinition.DataPolicies) { await AddSpecificApplicationDataPolyFromSecurityContractToApplication(application, dataPolicyToAdd, updatedById, dryRun, securityContractDryRunResult); } } else { logger.Debug($"[applications.fullname: '{application.Name}'].[dataPolicies]: No application data policies defined for application '{application.Name}'."); } return(await applicationRepository.UpdateAsync(application)); }
private async Task <ApplicationModel> SynchroniseApplicationDataPoliciesWithSecurityContract(ApplicationModel application, SecurityContractApplication applicationSecurityContractDefinition, Guid updatedById, bool dryRun, SecurityContractDryRunResult securityContractDryRunResult) { await RemoveApplicationDataPoliciesCurrentlyAssignedToApplicationThatAreNoLongerInSecurityContract(application, applicationSecurityContractDefinition); return(await AddApplicationDataPoliciesFromSecurityContractToApplication(application, applicationSecurityContractDefinition, updatedById, dryRun, securityContractDryRunResult)); }