diff --git a/package-lock.json b/package-lock.json index aee72fc0..f702d7df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15481,12 +15481,12 @@ }, "plugins/dotnet-auth-core-identity": { "name": "@amplication/plugin-dotnet-auth-core-identity", - "version": "0.0.1", + "version": "0.0.3", "license": "Apache-2.0", "devDependencies": { - "@amplication/code-gen-types": "2.0.33-beta.23", + "@amplication/code-gen-types": "2.0.34", "@amplication/code-gen-utils": "^0.0.9", - "@amplication/csharp-ast": "0.0.3-beta.2", + "@amplication/csharp-ast": "0.0.3", "@babel/parser": "^7.18.11", "@babel/types": "^7.18.10", "@types/lodash": "^4.14.182", @@ -15508,9 +15508,9 @@ } }, "plugins/dotnet-auth-core-identity/node_modules/@amplication/code-gen-types": { - "version": "2.0.33-beta.23", - "resolved": "https://registry.npmjs.org/@amplication/code-gen-types/-/code-gen-types-2.0.33-beta.23.tgz", - "integrity": "sha512-CtmkKPDzRF/px4DKQtS4LWdbsNDTTMt2UXgInpq7WCtM9RXIjKehucvmSAIIhKkXR0H8GdILOYOIpSCPZlgmbw==", + "version": "2.0.34", + "resolved": "https://registry.npmjs.org/@amplication/code-gen-types/-/code-gen-types-2.0.34.tgz", + "integrity": "sha512-RLjFuooQoCRomZ6KBHw+Kp25OsvpJ1eAwLi+jO/6S/9QkAGZ+jHWhUUoSiyI8pmWYvi7T2VI8b6d5K8s1Nzh9w==", "dev": true, "dependencies": { "ast-types": "^0.14.2", @@ -15524,9 +15524,9 @@ } }, "plugins/dotnet-auth-core-identity/node_modules/@amplication/csharp-ast": { - "version": "0.0.3-beta.2", - "resolved": "https://registry.npmjs.org/@amplication/csharp-ast/-/csharp-ast-0.0.3-beta.2.tgz", - "integrity": "sha512-2Qkz0IFvKeYmy0jK/nyeYzzHW/LrWDCibVJT6RLytt+Zy944wk6W6NH8bOWyEsEGkPZNcjK0zD03MFrRgV4TiA==", + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@amplication/csharp-ast/-/csharp-ast-0.0.3.tgz", + "integrity": "sha512-29tSgnmM7QH1ZGj5Kt1Ijp8phL3r2h3REvi9ThTGIn2TC6DnwJr4dzStwSt8vM613WOxqrF+V0GSJLITJ8jD4w==", "dev": true, "dependencies": { "tslib": "^2.3.0" diff --git a/plugins/dotnet-auth-core-identity/.amplicationrc.json b/plugins/dotnet-auth-core-identity/.amplicationrc.json index 2d3af765..73e15e87 100644 --- a/plugins/dotnet-auth-core-identity/.amplicationrc.json +++ b/plugins/dotnet-auth-core-identity/.amplicationrc.json @@ -1,6 +1,6 @@ { - "settings": {}, - "systemSettings": { - "requireAuthenticationEntity": "true" + "settings": { + "seedUserEmail": "test@email.com", + "seedUserPassword": "P@ssw0rd!" } } diff --git a/plugins/dotnet-auth-core-identity/package.json b/plugins/dotnet-auth-core-identity/package.json index 2ffeb9ae..db051672 100644 --- a/plugins/dotnet-auth-core-identity/package.json +++ b/plugins/dotnet-auth-core-identity/package.json @@ -1,6 +1,6 @@ { "name": "@amplication/plugin-dotnet-auth-core-identity", - "version": "0.0.1", + "version": "0.0.3", "description": "Add Authentication and Authorization to your .NET Services", "main": "dist/index.js", "nx": {}, @@ -12,9 +12,9 @@ "author": "Mor Hagbi", "license": "Apache-2.0", "devDependencies": { - "@amplication/code-gen-types": "2.0.33-beta.23", + "@amplication/code-gen-types": "2.0.34", "@amplication/code-gen-utils": "^0.0.9", - "@amplication/csharp-ast": "0.0.3-beta.2", + "@amplication/csharp-ast": "0.0.3", "@babel/parser": "^7.18.11", "@babel/types": "^7.18.10", "@types/lodash": "^4.14.182", diff --git a/plugins/dotnet-auth-core-identity/src/core/create-app-services.ts b/plugins/dotnet-auth-core-identity/src/core/create-app-services.ts index 702f84c2..bb8afca6 100644 --- a/plugins/dotnet-auth-core-identity/src/core/create-app-services.ts +++ b/plugins/dotnet-auth-core-identity/src/core/create-app-services.ts @@ -1,6 +1,12 @@ import { CodeBlock } from "@amplication/csharp-ast"; export function createAppServices(builderServicesBlocks: CodeBlock[]): void { + builderServicesBlocks.push( + new CodeBlock({ + code: `app.UseApiAuthentication();`, + }) + ); + builderServicesBlocks.push( new CodeBlock({ code: `using (var scope = app.Services.CreateScope()) diff --git a/plugins/dotnet-auth-core-identity/src/core/create-seed-development-data.ts b/plugins/dotnet-auth-core-identity/src/core/create-seed-development-data.ts index 76f3af67..a1107c84 100644 --- a/plugins/dotnet-auth-core-identity/src/core/create-seed-development-data.ts +++ b/plugins/dotnet-auth-core-identity/src/core/create-seed-development-data.ts @@ -1,17 +1,15 @@ -import { Entity, EnumDataType } from "@amplication/code-gen-types"; +import { dotnetTypes } from "@amplication/code-gen-types"; import { CodeBlock, CsharpSupport } from "@amplication/csharp-ast"; -import { camelCase } from "lodash"; -import { pascalCase } from "pascal-case"; +import { getPluginSettings } from "../utils"; export function CreateSeedDevelopmentDataBody( resourceName: string, - authEntity: Entity, - entities: Entity[] + context: dotnetTypes.DsgContext ): CodeBlock { - const { name, pluralName } = authEntity; - const entityNameToCamelCase = camelCase(name); - const entityNamePluralize = pascalCase(pluralName); - const entityFirstLetter = entityNameToCamelCase.slice(0, 1); + const { seedUserEmail, seedUserPassword } = getPluginSettings( + context.pluginInstallations + ); + return new CodeBlock({ references: [ CsharpSupport.classReference({ @@ -34,66 +32,30 @@ export function CreateSeedDevelopmentDataBody( .Where(x => x.Value != null) .Select(x => x.Value.ToString()) .ToArray(); - - ${authEntityDto(authEntity, entities)} - - - if (!context.${entityNamePluralize}.Any(${entityFirstLetter} => ${entityFirstLetter}.UserName == ${entityNameToCamelCase}.UserName)) - { - var password = new PasswordHasher<${name}>(); - var hashed = password.HashPassword(${entityNameToCamelCase}, "password"); - ${entityNameToCamelCase}.PasswordHash = hashed; - var userStore = new UserStore<${name}>(context); - await userStore.CreateAsync(${entityNameToCamelCase}); + + var usernameValue = "${seedUserEmail}"; + var passwordValue = "${seedUserPassword}"; + var user = new IdentityUser + { + Email = usernameValue, + UserName = usernameValue, + NormalizedUserName = usernameValue.ToUpperInvariant(), + NormalizedEmail = usernameValue.ToUpperInvariant(), + }; + + var password = new PasswordHasher(); + var hashed = password.HashPassword(user, passwordValue); + user.PasswordHash = hashed; + var userStore = new UserStore(context); + await userStore.CreateAsync(user); var _roleManager = serviceProvider.GetRequiredService>(); foreach (var role in amplicationRoles) { - await userStore.AddToRoleAsync(${entityNameToCamelCase}, _roleManager.NormalizeKey(role)); + await userStore.AddToRoleAsync(user, _roleManager.NormalizeKey(role)); } - } + await context.SaveChangesAsync();`, }); } - -const authEntityDto = (authEntity: Entity, entities: Entity[]): string => { - const { fields } = authEntity; - let codeBlock = ""; - - for (const field of fields) { - const fieldNamePascalCase = pascalCase(field.name); - - if (field.dataType == EnumDataType.Lookup) { - const relatedEntity = entities.find( - (entity) => entity.id === field.properties?.relatedEntityId - ); - - const relatedEntityFieldName = pascalCase(field.name); - - if (field.properties?.allowMultipleSelection) { - // the "many" side of the relation - codeBlock = - codeBlock + - `${fieldNamePascalCase} = model.${relatedEntityFieldName}.Select(x => new ${relatedEntity?.name}IdDto {Id = x.Id}).ToList(),\n`; - } else { - if (field.properties.fkHolderName === authEntity.name) { - break; - } else { - // the "one" side of the relation - codeBlock = - codeBlock + - `${fieldNamePascalCase} = new ${relatedEntity?.name}IdDto { Id = model.${fieldNamePascalCase}Id},\n`; - } - } - } else { - codeBlock = - codeBlock + `${fieldNamePascalCase} = model.${fieldNamePascalCase},\n`; - } - } - - return `var ${camelCase(authEntity.name)} = new ${authEntity.name} - { - ${codeBlock} - };`; -}; diff --git a/plugins/dotnet-auth-core-identity/src/core/create-static-file-map.ts b/plugins/dotnet-auth-core-identity/src/core/create-static-file-map.ts index 07df4f12..8d1df962 100644 --- a/plugins/dotnet-auth-core-identity/src/core/create-static-file-map.ts +++ b/plugins/dotnet-auth-core-identity/src/core/create-static-file-map.ts @@ -7,20 +7,20 @@ export async function createStaticFileFileMap( destPath: string, filePath: string, context: dotnetTypes.DsgContext, - classReference?: ClassReference + classReferences?: ClassReference[] ): Promise> { const fileMap = new FileMap(context.logger); if (!context.resourceInfo) return fileMap; const resourceName = pascalCase(context.resourceInfo.name); let fileContent = await readFile(filePath, "utf-8"); - fileContent = fileContent.replace("ServiceName", resourceName); + fileContent = fileContent.replaceAll("ServiceName", resourceName); const file: IFile = { path: destPath, code: new CodeBlock({ code: fileContent, - references: classReference && [classReference], + references: classReferences && classReferences, }), }; diff --git a/plugins/dotnet-auth-core-identity/src/index.ts b/plugins/dotnet-auth-core-identity/src/index.ts index 029ac447..3416c2f8 100644 --- a/plugins/dotnet-auth-core-identity/src/index.ts +++ b/plugins/dotnet-auth-core-identity/src/index.ts @@ -33,9 +33,6 @@ class AuthCorePlugin implements dotnetTypes.AmplicationPlugin { CreateControllerBaseModuleFile: { after: this.afterCreateControllerBaseModule, }, - CreateEntityModel: { - after: this.afterCreateEntityModel, - }, CreateResourceDbContextFile: { after: this.afterCreateResourceDbContextFile, }, @@ -106,10 +103,12 @@ class AuthCorePlugin implements dotnetTypes.AmplicationPlugin { destPath, filePath, context, - CsharpSupport.classReference({ - name: `${resourceName}`, - namespace: `${resourceName}.Infrastructure`, - }) + [ + CsharpSupport.classReference({ + name: `${resourceName}DbContext`, + namespace: `${resourceName}.Infrastructure`, + }), + ] ); const rolesManagerDestPath = `${eventParams.basePath}/src/Infrastructure/RolesManager.cs`; @@ -129,41 +128,12 @@ class AuthCorePlugin implements dotnetTypes.AmplicationPlugin { return files; } - afterCreateEntityModel( - context: dotnetTypes.DsgContext, - eventParams: dotnet.CreateEntityModelParams, - files: FileMap - ): FileMap { - const { apisDir, entity } = eventParams; - const { resourceInfo } = context; - const authEntityName = resourceInfo?.settings.authEntityName; - - if (entity.name !== authEntityName) return files; - - const modelFile = files.get(`${apisDir}${authEntityName}.cs`); - - if (!modelFile) return files; - - modelFile.code.parentClassReference = CsharpSupport.classReference({ - name: "IdentityUser", - namespace: "", - }); - - return files; - } - afterCreateResourceDbContextFile( context: dotnetTypes.DsgContext, eventParams: dotnet.CreateResourceDbContextFileParams, files: FileMap ): FileMap { - const { resourceDbContextPath, entities, resourceName } = eventParams; - const { resourceInfo } = context; - const authEntityName = resourceInfo?.settings.authEntityName; - - const authEntityCheck = entities.find((e) => e.name === authEntityName); - - if (!authEntityCheck) return files; + const { resourceDbContextPath, resourceName } = eventParams; const modelFile = files.get( `${resourceDbContextPath}${resourceName}DbContext.cs` @@ -171,9 +141,17 @@ class AuthCorePlugin implements dotnetTypes.AmplicationPlugin { if (!modelFile) return files; - modelFile.code.parentClassReference = CsharpSupport.classReference({ - name: `IdentityDbContext<${authEntityName}>`, - namespace: "", + modelFile.code.parentClassReference = CsharpSupport.genericClassReference({ + reference: CsharpSupport.classReference({ + name: `IdentityDbContext`, + namespace: "Microsoft.AspNetCore.Identity.EntityFrameworkCore", + }), + innerType: CsharpSupport.Types.reference( + CsharpSupport.classReference({ + name: `IdentityUser`, + namespace: "Microsoft.AspNetCore.Identity", + }) + ), }); return files; @@ -191,7 +169,7 @@ class AuthCorePlugin implements dotnetTypes.AmplicationPlugin { const roleNames = roles?.map((role) => role.name).join(","); const controllerBaseFile = files.get( - `${apisDir}/${entity.name}/base/${pascalPluralName}ControllerBase.cs` + `${apisDir}/${entity.name}/Base/${pascalPluralName}ControllerBase.cs` ); const methods = controllerBaseFile?.code.getMethods(); @@ -377,13 +355,9 @@ class AuthCorePlugin implements dotnetTypes.AmplicationPlugin { files: FileMap ): FileMap { const { seedFilePath, resourceName } = eventParams; - const { resourceInfo, entities } = context; + const { entities } = context; - const authEntity = entities?.find( - (e) => e.name === resourceInfo?.settings.authEntityName - ); - - if (!authEntity || !entities) return files; + if (!entities) return files; const seedFile = files.get(seedFilePath); seedFile?.code.addMethod( @@ -391,7 +365,7 @@ class AuthCorePlugin implements dotnetTypes.AmplicationPlugin { name: "SeedDevUser", access: "public", isAsync: true, - body: CreateSeedDevelopmentDataBody(resourceName, authEntity, entities), + body: CreateSeedDevelopmentDataBody(resourceName, context), type: MethodType.STATIC, parameters: [ CsharpSupport.parameter({ @@ -399,7 +373,7 @@ class AuthCorePlugin implements dotnetTypes.AmplicationPlugin { type: CsharpSupport.Types.reference( CsharpSupport.classReference({ name: "IServiceProvider", - namespace: "", + namespace: `${resourceName}.Infrastructure.Models`, }) ), }), diff --git a/plugins/dotnet-auth-core-identity/src/static/common/auth/ProgramAuthExtensions.cs b/plugins/dotnet-auth-core-identity/src/static/common/auth/ProgramAuthExtensions.cs index ed600f00..96dcb19a 100644 --- a/plugins/dotnet-auth-core-identity/src/static/common/auth/ProgramAuthExtensions.cs +++ b/plugins/dotnet-auth-core-identity/src/static/common/auth/ProgramAuthExtensions.cs @@ -1,4 +1,3 @@ -using GraphQL; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.OpenApi.Models; @@ -12,14 +11,14 @@ public static void AddApiAuthentication(this IServiceCollection services) { services.AddAuthorization(); services - .AddIdentityApiEndpoints() + .AddIdentityApiEndpoints() .AddRoles() - .AddEntityFrameworkStores(); + .AddEntityFrameworkStores(); } public static void UseApiAuthentication(this WebApplication app) { - app.MapGroup($"/auth").MapIdentityApi(); + app.MapGroup($"/auth").MapIdentityApi(); app.UseAuthorization(); } @@ -34,7 +33,7 @@ this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions options { tag = controllerActionDescriptor.ControllerName; } - tag = tag ?? api.RelativePath?.Split('/')?.FirstOrDefault()?.ToPascalCase(); + tag = tag ?? api.RelativePath?.Split('/')?.FirstOrDefault(); return new[] { tag }; }); diff --git a/plugins/dotnet-auth-core-identity/src/types.ts b/plugins/dotnet-auth-core-identity/src/types.ts new file mode 100644 index 00000000..9f465a1d --- /dev/null +++ b/plugins/dotnet-auth-core-identity/src/types.ts @@ -0,0 +1,4 @@ +export interface Settings { + seedUserEmail: string; + seedUserPassword: string; +} diff --git a/plugins/dotnet-auth-core-identity/src/utils.ts b/plugins/dotnet-auth-core-identity/src/utils.ts new file mode 100644 index 00000000..a94bfe69 --- /dev/null +++ b/plugins/dotnet-auth-core-identity/src/utils.ts @@ -0,0 +1,21 @@ +import { PluginInstallation } from "@amplication/code-gen-types"; +import { name as PackageName } from "../package.json"; +import { Settings } from "./types"; +import defaultSettings from "../.amplicationrc.json"; + +export const getPluginSettings = ( + pluginInstallations: PluginInstallation[] +): Settings => { + const plugin = pluginInstallations.find( + (plugin) => plugin.npm === PackageName + ); + + const userSettings = plugin?.settings ?? {}; + + const settings: Settings = { + ...defaultSettings.settings, + ...userSettings, + }; + + return settings; +};