diff --git a/services/api/src/core/authorization/client/decorators/requires-organization-permissions.decorator.ts b/services/api/src/core/authorization/client/decorators/requires-organization-permissions.decorator.ts new file mode 100644 index 00000000..741e829d --- /dev/null +++ b/services/api/src/core/authorization/client/decorators/requires-organization-permissions.decorator.ts @@ -0,0 +1,8 @@ +import { SetMetadata } from "@nestjs/common"; + +import { OrganizationRole } from "../../organization-roles.enum"; + +export const REQUIRED_ORGANIZATION_ROLES_KEY = "required-organization-roles"; + +export const RequiresOrganizationPermissions = (roles: OrganizationRole[]) => + SetMetadata(REQUIRED_ORGANIZATION_ROLES_KEY, roles); diff --git a/services/api/src/core/authorization/client/decorators/roles.decorator.ts b/services/api/src/core/authorization/client/decorators/roles.decorator.ts deleted file mode 100644 index 697e21dc..00000000 --- a/services/api/src/core/authorization/client/decorators/roles.decorator.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { SetMetadata } from "@nestjs/common"; - -import { OrganizationMembershipRole } from "src/features/organizations/infrastructure/entities/organization-membership.entity"; - -export const ROLES_KEY = "roles"; -export const RequiresPermissions = (roles: OrganizationMembershipRole[]) => - SetMetadata(ROLES_KEY, roles); diff --git a/services/api/src/core/authorization/client/guards/roles.guard.ts b/services/api/src/core/authorization/client/guards/roles.guard.ts index 7efad888..1b60c727 100644 --- a/services/api/src/core/authorization/client/guards/roles.guard.ts +++ b/services/api/src/core/authorization/client/guards/roles.guard.ts @@ -2,9 +2,9 @@ import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common"; import { Reflector } from "@nestjs/core"; import { CurrentOrganizationService } from "src/features/organizations"; -import { OrganizationMembershipRole as Role } from "src/features/organizations/infrastructure/entities/organization-membership.entity"; -import { ROLES_KEY } from "../decorators/roles.decorator"; +import { OrganizationRole } from "../../organization-roles.enum"; +import { REQUIRED_ORGANIZATION_ROLES_KEY } from "../decorators/roles.decorator"; @Injectable() export class RolesGuard implements CanActivate { @@ -14,10 +14,10 @@ export class RolesGuard implements CanActivate { ) {} async canActivate(context: ExecutionContext): Promise { - const requiredRoles = this.reflector.getAllAndOverride(ROLES_KEY, [ - context.getHandler(), - context.getClass(), - ]); + const requiredRoles = this.reflector.getAllAndOverride( + REQUIRED_ORGANIZATION_ROLES_KEY, + [context.getHandler(), context.getClass()], + ); if (!requiredRoles) { return true; diff --git a/services/api/src/core/authorization/index.ts b/services/api/src/core/authorization/index.ts index fdd6cab9..2b39926c 100644 --- a/services/api/src/core/authorization/index.ts +++ b/services/api/src/core/authorization/index.ts @@ -1,2 +1,3 @@ -export { RequiresPermissions } from "./client/decorators/roles.decorator"; +export { RequiresOrganizationPermissions } from "./client/decorators/requires-organization-permissions.decorator"; export { RolesGuard } from "./client/guards/roles.guard"; +export { OrganizationRole } from "./organization-roles.enum"; diff --git a/services/api/src/core/authorization/organization-roles.enum.ts b/services/api/src/core/authorization/organization-roles.enum.ts new file mode 100644 index 00000000..37bef6f4 --- /dev/null +++ b/services/api/src/core/authorization/organization-roles.enum.ts @@ -0,0 +1,4 @@ +export enum OrganizationRole { + Admin = "admin", + Member = "member", +} diff --git a/services/api/src/features/organizations/application/contracts/dtos/current-organization.dto.ts b/services/api/src/features/organizations/application/contracts/dtos/current-organization.dto.ts index fed12ba5..f675ee06 100644 --- a/services/api/src/features/organizations/application/contracts/dtos/current-organization.dto.ts +++ b/services/api/src/features/organizations/application/contracts/dtos/current-organization.dto.ts @@ -1,6 +1,7 @@ -import { OrganizationMembershipRole } from "../../../infrastructure/entities/organization-membership.entity"; +import { OrganizationRole } from "src/core/authorization"; + import { OrganizationDetails } from "./organization.dto"; export class CurrentOrganizationDetails extends OrganizationDetails { - role!: OrganizationMembershipRole; + role!: OrganizationRole; } diff --git a/services/api/src/features/organizations/application/contracts/dtos/organization-member.dto.ts b/services/api/src/features/organizations/application/contracts/dtos/organization-member.dto.ts index 9d013038..290052fe 100644 --- a/services/api/src/features/organizations/application/contracts/dtos/organization-member.dto.ts +++ b/services/api/src/features/organizations/application/contracts/dtos/organization-member.dto.ts @@ -1,4 +1,4 @@ -import { OrganizationMembershipRole } from "../../../infrastructure/entities/organization-membership.entity"; +import { OrganizationRole } from "src/core/authorization"; export class OrganizationMemberDetails { joinedAt!: string; @@ -6,5 +6,5 @@ export class OrganizationMemberDetails { id!: number; imageUrl?: string; profileId!: number; - role!: OrganizationMembershipRole; + role!: OrganizationRole; } diff --git a/services/api/src/features/organizations/client/controllers/current-organization.controller.ts b/services/api/src/features/organizations/client/controllers/current-organization.controller.ts index 64d56c2c..96d7e025 100644 --- a/services/api/src/features/organizations/client/controllers/current-organization.controller.ts +++ b/services/api/src/features/organizations/client/controllers/current-organization.controller.ts @@ -2,7 +2,7 @@ import { Body, Controller, Get, Post, Query } from "@nestjs/common"; import { CommandBus, QueryBus } from "@nestjs/cqrs"; import { ApiTags } from "@nestjs/swagger"; -import { RequiresPermissions } from "src/core/authorization"; +import { RequiresOrganizationPermissions } from "src/core/authorization"; import { AddMemberToOrganizationCommand } from "../../application/contracts/commands/add-member-to-organization.command"; import { UpdateMemberRoleCommand } from "../../application/contracts/commands/update-member-role.command"; @@ -11,7 +11,7 @@ import { CurrentOrganizationDetails } from "../../application/contracts/dtos/cur import { OrganizationMemberDetails } from "../../application/contracts/dtos/organization-member.dto"; import { GetOrganizationMembersQuery } from "../../application/contracts/queries/get-organization-members.query"; import { GetOrganizationQuery } from "../../application/contracts/queries/get-organization.query"; -import { permissions } from "../../permissions"; +import { organizationPermissions } from "../../organization.permissions"; @Controller("organizations/current") @ApiTags("organizations") @@ -19,7 +19,9 @@ export class CurrentOrganizationController { constructor(private commandBus: CommandBus, private queryBus: QueryBus) {} @Get() - @RequiresPermissions(permissions.CurrentOrganization.Read) + @RequiresOrganizationPermissions( + organizationPermissions.CurrentOrganization.Read, + ) async getCurrentOrganization( @Query() query: GetOrganizationQuery, ): Promise { @@ -27,7 +29,9 @@ export class CurrentOrganizationController { } @Get("/members") - @RequiresPermissions(permissions.CurrentOrganization.Members.Read) + @RequiresOrganizationPermissions( + organizationPermissions.CurrentOrganization.Members.Read, + ) async getCurrentOrganizationMembers( @Query() query: GetOrganizationMembersQuery, ): Promise { @@ -35,7 +39,9 @@ export class CurrentOrganizationController { } @Post("/members") - @RequiresPermissions(permissions.CurrentOrganization.Members.Add) + @RequiresOrganizationPermissions( + organizationPermissions.CurrentOrganization.Members.Add, + ) async addMemberToOrganization( @Body() command: AddMemberToOrganizationCommand, ): Promise { @@ -43,7 +49,9 @@ export class CurrentOrganizationController { } @Post("/members/role") - @RequiresPermissions(permissions.CurrentOrganization.Members.UpdateRole) + @RequiresOrganizationPermissions( + organizationPermissions.CurrentOrganization.Members.UpdateRole, + ) async changeMemberRole( @Body() command: UpdateMemberRoleCommand, ): Promise { @@ -51,7 +59,9 @@ export class CurrentOrganizationController { } @Post() - @RequiresPermissions(permissions.CurrentOrganization.Update) + @RequiresOrganizationPermissions( + organizationPermissions.CurrentOrganization.Update, + ) async updateCurrentOrganization( @Body() command: UpdateOrganizationCommand, ): Promise { diff --git a/services/api/src/features/organizations/infrastructure/entities/organization-membership.entity.ts b/services/api/src/features/organizations/infrastructure/entities/organization-membership.entity.ts index 059f2982..3edfdf98 100644 --- a/services/api/src/features/organizations/infrastructure/entities/organization-membership.entity.ts +++ b/services/api/src/features/organizations/infrastructure/entities/organization-membership.entity.ts @@ -6,15 +6,11 @@ import { PrimaryGeneratedColumn, } from "typeorm"; +import { OrganizationRole } from "src/core/authorization"; import { Profile } from "src/features/profiles/infrastructure/entities/profile.entity"; import { Organization } from "./organization.entity"; -export enum OrganizationMembershipRole { - Admin = "admin", - Member = "member", -} - @Entity() export class OrganizationMembership { @PrimaryGeneratedColumn() @@ -37,8 +33,8 @@ export class OrganizationMembership { @Column({ type: "enum", - enum: OrganizationMembershipRole, - default: OrganizationMembershipRole.Member, + enum: OrganizationRole, + default: OrganizationRole.Member, }) - role!: OrganizationMembershipRole; + role!: OrganizationRole; } diff --git a/services/api/src/features/organizations/organization.permissions.ts b/services/api/src/features/organizations/organization.permissions.ts new file mode 100644 index 00000000..5497034e --- /dev/null +++ b/services/api/src/features/organizations/organization.permissions.ts @@ -0,0 +1,13 @@ +import { OrganizationRole } from "src/core/authorization"; + +export const organizationPermissions = { + CurrentOrganization: { + Read: [OrganizationRole.Admin, OrganizationRole.Member], + Members: { + Add: [OrganizationRole.Admin], + Read: [OrganizationRole.Admin, OrganizationRole.Member], + UpdateRole: [OrganizationRole.Admin], + }, + Update: [OrganizationRole.Admin], + }, +}; diff --git a/services/api/src/features/organizations/permissions.ts b/services/api/src/features/organizations/permissions.ts deleted file mode 100644 index 51c451fc..00000000 --- a/services/api/src/features/organizations/permissions.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { OrganizationMembershipRole as Role } from "./infrastructure/entities/organization-membership.entity"; - -export const permissions = { - CurrentOrganization: { - Read: [Role.Admin, Role.Member], - Members: { - Add: [Role.Admin], - Read: [Role.Admin, Role.Member], - UpdateRole: [Role.Admin], - }, - Update: [Role.Admin], - }, -};