Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/8626 #378

Merged
merged 16 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions plugins/dotnet-auth-core-identity/.amplicationrc.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"settings": {},
"systemSettings": {
"requireAuthenticationEntity": "true"
"settings": {
"seedUserEmail": "[email protected]",
"seedUserPassword": "P@ssw0rd!"
}
}
6 changes: 3 additions & 3 deletions plugins/dotnet-auth-core-identity/package.json
Original file line number Diff line number Diff line change
@@ -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": {},
Expand All @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -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())
Expand Down
Original file line number Diff line number Diff line change
@@ -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({
Expand All @@ -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<IdentityUser>();
var hashed = password.HashPassword(user, passwordValue);
user.PasswordHash = hashed;
var userStore = new UserStore<IdentityUser>(context);
await userStore.CreateAsync(user);
var _roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();

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}
};`;
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@ export async function createStaticFileFileMap(
destPath: string,
filePath: string,
context: dotnetTypes.DsgContext,
classReference?: ClassReference
classReferences?: ClassReference[]
): Promise<FileMap<CodeBlock>> {
const fileMap = new FileMap<CodeBlock>(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<CodeBlock> = {
path: destPath,
code: new CodeBlock({
code: fileContent,
references: classReference && [classReference],
references: classReferences && classReferences,
}),
};

Expand Down
72 changes: 23 additions & 49 deletions plugins/dotnet-auth-core-identity/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ class AuthCorePlugin implements dotnetTypes.AmplicationPlugin {
CreateControllerBaseModuleFile: {
after: this.afterCreateControllerBaseModule,
},
CreateEntityModel: {
after: this.afterCreateEntityModel,
},
CreateResourceDbContextFile: {
after: this.afterCreateResourceDbContextFile,
},
Expand Down Expand Up @@ -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`;
Expand All @@ -129,51 +128,30 @@ class AuthCorePlugin implements dotnetTypes.AmplicationPlugin {
return files;
}

afterCreateEntityModel(
context: dotnetTypes.DsgContext,
eventParams: dotnet.CreateEntityModelParams,
files: FileMap<Class>
): FileMap<Class> {
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<Class>
): FileMap<Class> {
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`
);

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;
Expand All @@ -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();
Expand Down Expand Up @@ -377,29 +355,25 @@ class AuthCorePlugin implements dotnetTypes.AmplicationPlugin {
files: FileMap<Class>
): FileMap<Class> {
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(
CsharpSupport.method({
name: "SeedDevUser",
access: "public",
isAsync: true,
body: CreateSeedDevelopmentDataBody(resourceName, authEntity, entities),
body: CreateSeedDevelopmentDataBody(resourceName, context),
type: MethodType.STATIC,
parameters: [
CsharpSupport.parameter({
name: "serviceProvider",
type: CsharpSupport.Types.reference(
CsharpSupport.classReference({
name: "IServiceProvider",
namespace: "",
namespace: `${resourceName}.Infrastructure.Models`,
})
),
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using GraphQL;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.OpenApi.Models;
Expand All @@ -12,14 +11,14 @@ public static void AddApiAuthentication(this IServiceCollection services)
{
services.AddAuthorization();
services
.AddIdentityApiEndpoints<User>()
.AddIdentityApiEndpoints<IdentityUser>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<MyServiceContext>();
.AddEntityFrameworkStores<ServiceNameDbContext>();
}

public static void UseApiAuthentication(this WebApplication app)
{
app.MapGroup($"/auth").MapIdentityApi<User>();
app.MapGroup($"/auth").MapIdentityApi<IdentityUser>();
app.UseAuthorization();
}

Expand All @@ -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 };
});

Expand Down
4 changes: 4 additions & 0 deletions plugins/dotnet-auth-core-identity/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface Settings {
seedUserEmail: string;
seedUserPassword: string;
}
Loading
Loading