From 6db71cdf2dc73cc22685565ae0a163838aa452d2 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Sun, 15 Sep 2024 14:46:25 +0100 Subject: [PATCH 01/78] initial pass at having roles data driven --- src/common/enums/authorization.policy.type.ts | 1 + src/common/enums/community.role.ts | 6 +- .../dto/role.manager.dto.create.ts | 9 + ...ole.manager.dto.update.application.form.ts | 16 + src/domain/access/role-manager/index.ts | 2 + .../role-manager/role.manager.entity.ts | 59 +++ .../role-manager/role.manager.interface.ts | 19 + .../role-manager/role.manager.module.ts | 46 ++ .../role.manager.resolver.fields.ts | 23 + .../role.manager.resolver.mutations.spec.ts | 31 ++ .../role.manager.resolver.mutations.ts | 49 ++ .../role.manager.service.authorization.ts | 483 ++++++++++++++++++ .../role-manager/role.manager.service.spec.ts | 30 ++ .../role-manager/role.manager.service.ts | 324 ++++++++++++ .../role/contributor.role.policy.interface.ts | 19 + .../access/role/contributor.role.policy.ts | 13 + src/domain/access/role/dto/role.dto.create.ts | 14 + src/domain/access/role/role.entity.ts | 40 ++ src/domain/access/role/role.interface.ts | 34 ++ src/domain/access/role/role.module.ts | 12 + .../access/role/role.resolver.fields.ts | 51 ++ src/domain/access/role/role.service.ts | 80 +++ ...lout.contribution.service.authorization.ts | 4 +- .../collaboration.service.authorization.ts | 8 +- .../post/post.service.authorization.ts | 4 +- .../application/application.entity.ts | 8 + .../community.policy.resolver.fields.ts | 8 +- .../community.policy.service.ts | 22 +- ....lifecycle.application.options.provider.ts | 9 +- ...e.lifecycle.invitation.options.provider.ts | 11 +- .../community.role.resolver.fields.ts | 28 +- .../community.role.resolver.mutations.ts | 10 +- .../community-role/community.role.service.ts | 68 +-- ...unity.role.dto.role.assign.organization.ts | 6 +- .../community.role.dto.role.assign.user.ts | 6 +- .../community.role.dto.role.assign.virtual.ts | 6 +- ...unity.role.dto.role.remove.organization.ts | 6 +- .../community.role.dto.role.remove.user.ts | 6 +- .../community.role.dto.role.remove.virtual.ts | 6 +- .../community.service.authorization.ts | 12 +- .../community/community/community.service.ts | 4 +- .../community/invitation/invitation.entity.ts | 8 + .../space/space.service.authorization.ts | 14 +- src/domain/space/space/space.service.ts | 10 +- .../admin.communication.service.ts | 6 +- .../invitation/platform.invitation.entity.ts | 8 + .../api/conversion/conversion.service.ts | 40 +- .../roles/util/group.credentials.by.entity.ts | 12 +- 48 files changed, 1534 insertions(+), 157 deletions(-) create mode 100644 src/domain/access/role-manager/dto/role.manager.dto.create.ts create mode 100644 src/domain/access/role-manager/dto/role.manager.dto.update.application.form.ts create mode 100644 src/domain/access/role-manager/index.ts create mode 100644 src/domain/access/role-manager/role.manager.entity.ts create mode 100644 src/domain/access/role-manager/role.manager.interface.ts create mode 100644 src/domain/access/role-manager/role.manager.module.ts create mode 100644 src/domain/access/role-manager/role.manager.resolver.fields.ts create mode 100644 src/domain/access/role-manager/role.manager.resolver.mutations.spec.ts create mode 100644 src/domain/access/role-manager/role.manager.resolver.mutations.ts create mode 100644 src/domain/access/role-manager/role.manager.service.authorization.ts create mode 100644 src/domain/access/role-manager/role.manager.service.spec.ts create mode 100644 src/domain/access/role-manager/role.manager.service.ts create mode 100644 src/domain/access/role/contributor.role.policy.interface.ts create mode 100644 src/domain/access/role/contributor.role.policy.ts create mode 100644 src/domain/access/role/dto/role.dto.create.ts create mode 100644 src/domain/access/role/role.entity.ts create mode 100644 src/domain/access/role/role.interface.ts create mode 100644 src/domain/access/role/role.module.ts create mode 100644 src/domain/access/role/role.resolver.fields.ts create mode 100644 src/domain/access/role/role.service.ts diff --git a/src/common/enums/authorization.policy.type.ts b/src/common/enums/authorization.policy.type.ts index ed55eca853..8e72829e39 100644 --- a/src/common/enums/authorization.policy.type.ts +++ b/src/common/enums/authorization.policy.type.ts @@ -22,6 +22,7 @@ export enum AuthorizationPolicyType { ROOM = 'room', AI_PERSONA = 'ai-persona', APPLICATION = 'application', + ROLE_MANAGER = 'role-manager', COMMUNITY = 'community', COMMUNITY_GUIDELINES = 'community-guidelines', INVITATION = 'invitation', diff --git a/src/common/enums/community.role.ts b/src/common/enums/community.role.ts index 6e59558f9a..9b0b9a53db 100644 --- a/src/common/enums/community.role.ts +++ b/src/common/enums/community.role.ts @@ -1,11 +1,11 @@ import { registerEnumType } from '@nestjs/graphql'; -export enum CommunityRole { +export enum CommunityRoleType { MEMBER = 'member', LEAD = 'lead', ADMIN = 'admin', } -registerEnumType(CommunityRole, { - name: 'CommunityRole', +registerEnumType(CommunityRoleType, { + name: 'CommunityRoleType', }); diff --git a/src/domain/access/role-manager/dto/role.manager.dto.create.ts b/src/domain/access/role-manager/dto/role.manager.dto.create.ts new file mode 100644 index 0000000000..4d2bc7b142 --- /dev/null +++ b/src/domain/access/role-manager/dto/role.manager.dto.create.ts @@ -0,0 +1,9 @@ +import { CreateRoleInput } from '@domain/access/role/dto/role.dto.create'; +import { CreateFormInput } from '@domain/common/form/dto/form.dto.create'; +import { IRoleManager } from '../role.manager.interface'; + +export class CreateRoleManagerInput { + parentRoleManager!: IRoleManager; + roles!: CreateRoleInput[]; + applicationForm!: CreateFormInput; +} diff --git a/src/domain/access/role-manager/dto/role.manager.dto.update.application.form.ts b/src/domain/access/role-manager/dto/role.manager.dto.update.application.form.ts new file mode 100644 index 0000000000..c3b6ab4ad3 --- /dev/null +++ b/src/domain/access/role-manager/dto/role.manager.dto.update.application.form.ts @@ -0,0 +1,16 @@ +import { InputType, Field } from '@nestjs/graphql'; +import { ValidateNested } from 'class-validator'; +import { Type } from 'class-transformer'; +import { UpdateFormInput } from '@domain/common/form/dto/form.dto.update'; +import { UUID } from '@domain/common/scalars'; + +@InputType() +export class UpdateRoleManagerApplicationFormInput { + @Field(() => UUID, { nullable: false }) + roleManagerID!: string; + + @Field(() => UpdateFormInput, { nullable: false }) + @ValidateNested() + @Type(() => UpdateFormInput) + formData!: UpdateFormInput; +} diff --git a/src/domain/access/role-manager/index.ts b/src/domain/access/role-manager/index.ts new file mode 100644 index 0000000000..7b51db2598 --- /dev/null +++ b/src/domain/access/role-manager/index.ts @@ -0,0 +1,2 @@ +export * from './role.manager.entity'; +export * from './role.manager.interface'; diff --git a/src/domain/access/role-manager/role.manager.entity.ts b/src/domain/access/role-manager/role.manager.entity.ts new file mode 100644 index 0000000000..0b08ce5493 --- /dev/null +++ b/src/domain/access/role-manager/role.manager.entity.ts @@ -0,0 +1,59 @@ +import { Entity, JoinColumn, ManyToOne, OneToMany, OneToOne } from 'typeorm'; +import { IGroupable } from '@src/common/interfaces/groupable.interface'; +import { AuthorizableEntity } from '@domain/common/entity/authorizable-entity'; +import { Role } from '../role/role.entity'; +import { Form } from '@domain/common/form/form.entity'; +import { PlatformInvitation } from '@platform/invitation/platform.invitation.entity'; +import { IRoleManager } from './role.manager.interface'; +import { Application } from '@domain/community/application/application.entity'; +import { Invitation } from '@domain/community/invitation/invitation.entity'; + +@Entity() +export class RoleManager + extends AuthorizableEntity + implements IRoleManager, IGroupable +{ + @OneToOne(() => Form, { + eager: false, + cascade: true, + onDelete: 'SET NULL', + }) + @JoinColumn() + applicationForm?: Form; + + @OneToMany(() => Role, role => role.manager, { + eager: false, + cascade: true, + }) + roles?: Role[]; + + @OneToMany(() => Application, application => application.roleManager, { + eager: false, + cascade: true, + }) + applications?: Application[]; + + @OneToMany(() => Invitation, invitation => invitation.roleManager, { + eager: false, + cascade: true, + }) + invitations?: Invitation[]; + + @OneToMany( + () => PlatformInvitation, + platformInvitation => platformInvitation.roleManager, + { + eager: false, + cascade: true, + } + ) + platformInvitations?: PlatformInvitation[]; + + // The parent roleManager can have many child communities; the relationship is controlled by the child. + @ManyToOne(() => RoleManager, { + eager: false, + cascade: false, + onDelete: 'SET NULL', + }) + parentRoleManager?: RoleManager; +} diff --git a/src/domain/access/role-manager/role.manager.interface.ts b/src/domain/access/role-manager/role.manager.interface.ts new file mode 100644 index 0000000000..f0cffd3135 --- /dev/null +++ b/src/domain/access/role-manager/role.manager.interface.ts @@ -0,0 +1,19 @@ +import { ObjectType } from '@nestjs/graphql'; +import { IAuthorizable } from '@domain/common/entity/authorizable-entity'; +import { IForm } from '@domain/common/form/form.interface'; +import { IPlatformInvitation } from '@platform/invitation'; +import { IApplication } from '@domain/community/application/application.interface'; +import { IInvitation } from '@domain/community/invitation/invitation.interface'; +import { IRole } from '../role/role.interface'; + +@ObjectType('RoleManager') +export abstract class IRoleManager extends IAuthorizable { + roles?: IRole[]; + applications?: IApplication[]; + invitations?: IInvitation[]; + platformInvitations?: IPlatformInvitation[]; + + applicationForm?: IForm; + + parentRoleManager?: IRoleManager; +} diff --git a/src/domain/access/role-manager/role.manager.module.ts b/src/domain/access/role-manager/role.manager.module.ts new file mode 100644 index 0000000000..4b3222d851 --- /dev/null +++ b/src/domain/access/role-manager/role.manager.module.ts @@ -0,0 +1,46 @@ +import { AuthorizationModule } from '@core/authorization/authorization.module'; +import { AgentModule } from '@domain/agent/agent/agent.module'; +import { AuthorizationPolicyModule } from '@domain/common/authorization-policy/authorization.policy.module'; +import { CommunicationModule } from '@domain/communication/communication/communication.module'; +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { TrustRegistryAdapterModule } from '@services/external/trust-registry/trust.registry.adapter/trust.registry.adapter.module'; +import { RoleManager } from './role.manager.entity'; +import { RoleManagerResolverFields } from './role.manager.resolver.fields'; +import { RoleManagerResolverMutations } from './role.manager.resolver.mutations'; +import { RoleManagerService } from './role.manager.service'; +import { RoleManagerAuthorizationService } from './role.manager.service.authorization'; +import { FormModule } from '@domain/common/form/form.module'; +import { StorageAggregatorResolverModule } from '@services/infrastructure/storage-aggregator-resolver/storage.aggregator.resolver.module'; +import { LicenseEngineModule } from '@core/license-engine/license.engine.module'; +import { PlatformInvitationModule } from '@platform/invitation/platform.invitation.module'; +import { InvitationModule } from '@domain/community/invitation/invitation.module'; +import { ApplicationModule } from '@domain/community/application/application.module'; +import { VirtualContributorModule } from '@domain/community/virtual-contributor/virtual.contributor.module'; + +@Module({ + imports: [ + AuthorizationModule, + AuthorizationPolicyModule, + AgentModule, + CommunicationModule, + LicenseEngineModule, + AgentModule, + StorageAggregatorResolverModule, + FormModule, + InvitationModule, + ApplicationModule, + PlatformInvitationModule, + VirtualContributorModule, + TypeOrmModule.forFeature([RoleManager]), + TrustRegistryAdapterModule, + ], + providers: [ + RoleManagerService, + RoleManagerAuthorizationService, + RoleManagerResolverMutations, + RoleManagerResolverFields, + ], + exports: [RoleManagerService, RoleManagerAuthorizationService], +}) +export class RoleManagerModule {} diff --git a/src/domain/access/role-manager/role.manager.resolver.fields.ts b/src/domain/access/role-manager/role.manager.resolver.fields.ts new file mode 100644 index 0000000000..e1e93a2e06 --- /dev/null +++ b/src/domain/access/role-manager/role.manager.resolver.fields.ts @@ -0,0 +1,23 @@ +import { GraphqlGuard } from '@core/authorization'; +import { UseGuards } from '@nestjs/common'; +import { Parent, ResolveField, Resolver } from '@nestjs/graphql'; +import { Profiling } from '@src/common/decorators'; +import { RoleManagerService } from './role.manager.service'; +import { IForm } from '@domain/common/form/form.interface'; +import { IRoleManager } from './role.manager.interface'; +import { RoleManager } from './role.manager.entity'; + +@Resolver(() => IRoleManager) +export class RoleManagerResolverFields { + constructor(private roleManagerService: RoleManagerService) {} + + @UseGuards(GraphqlGuard) + @ResolveField('applicationForm', () => IForm, { + nullable: false, + description: 'The Form used for Applications to this roleManager.', + }) + @Profiling.api + async applicationForm(@Parent() roleManager: RoleManager): Promise { + return await this.roleManagerService.getApplicationForm(roleManager); + } +} diff --git a/src/domain/access/role-manager/role.manager.resolver.mutations.spec.ts b/src/domain/access/role-manager/role.manager.resolver.mutations.spec.ts new file mode 100644 index 0000000000..5bef36c77e --- /dev/null +++ b/src/domain/access/role-manager/role.manager.resolver.mutations.spec.ts @@ -0,0 +1,31 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { RoleManagerResolverMutations } from './role.manager.resolver.mutations'; +import { MockCacheManager } from '@test/mocks/cache-manager.mock'; +import { MockWinstonProvider } from '@test/mocks/winston.provider.mock'; +import { MockNotificationsService } from '@test/mocks/notifications.service.mock'; +import { defaultMockerFactory } from '@test/utils/default.mocker.factory'; + +describe('RoleManagerResolver', () => { + let resolver: RoleManagerResolverMutations; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + RoleManagerResolverMutations, + MockCacheManager, + MockWinstonProvider, + MockNotificationsService, + ], + }) + .useMocker(defaultMockerFactory) + .compile(); + + resolver = module.get( + RoleManagerResolverMutations + ); + }); + + it('should be defined', () => { + expect(resolver).toBeDefined(); + }); +}); diff --git a/src/domain/access/role-manager/role.manager.resolver.mutations.ts b/src/domain/access/role-manager/role.manager.resolver.mutations.ts new file mode 100644 index 0000000000..4214704341 --- /dev/null +++ b/src/domain/access/role-manager/role.manager.resolver.mutations.ts @@ -0,0 +1,49 @@ +import { UseGuards } from '@nestjs/common'; +import { Args, Mutation, Resolver } from '@nestjs/graphql'; +import { RoleManagerService } from './role.manager.service'; +import { CurrentUser, Profiling } from '@src/common/decorators'; +import { GraphqlGuard } from '@core/authorization'; +import { AgentInfo } from '@core/authentication.agent.info/agent.info'; +import { AuthorizationPrivilege } from '@common/enums'; +import { AuthorizationService } from '@core/authorization/authorization.service'; +import { AgentService } from '@domain/agent/agent/agent.service'; +import { UpdateRoleManagerApplicationFormInput } from './dto/role.manager.dto.update.application.form'; +import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service'; +import { IRoleManager } from './role.manager.interface'; + +@Resolver() +export class RoleManagerResolverMutations { + constructor( + private authorizationService: AuthorizationService, + private authorizationPolicyService: AuthorizationPolicyService, + private roleManagerService: RoleManagerService, + private agentService: AgentService + ) {} + + @UseGuards(GraphqlGuard) + @Mutation(() => IRoleManager, { + description: 'Update the Application Form used by this RoleManager.', + }) + @Profiling.api + async updateRoleManagerApplicationForm( + @CurrentUser() agentInfo: AgentInfo, + @Args('applicationFormData') + applicationFormData: UpdateRoleManagerApplicationFormInput + ): Promise { + const roleManager = await this.roleManagerService.getRoleManagerOrFail( + applicationFormData.roleManagerID + ); + + await this.authorizationService.grantAccessOrFail( + agentInfo, + roleManager.authorization, + AuthorizationPrivilege.UPDATE, + `update roleManager application form: ${roleManager.id}` + ); + + return await this.roleManagerService.updateApplicationForm( + roleManager, + applicationFormData.formData + ); + } +} diff --git a/src/domain/access/role-manager/role.manager.service.authorization.ts b/src/domain/access/role-manager/role.manager.service.authorization.ts new file mode 100644 index 0000000000..c2117b3eff --- /dev/null +++ b/src/domain/access/role-manager/role.manager.service.authorization.ts @@ -0,0 +1,483 @@ +import { Injectable } from '@nestjs/common'; +import { RoleManagerService } from './role.manager.service'; +import { + AuthorizationCredential, + AuthorizationPrivilege, + LogContext, +} from '@common/enums'; +import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service'; +import { IAuthorizationPolicy } from '@domain/common/authorization-policy/authorization.policy.interface'; +import { AuthorizationPolicyRuleVerifiedCredential } from '@core/authorization/authorization.policy.rule.verified.credential'; +import { IAuthorizationPolicyRuleCredential } from '@core/authorization/authorization.policy.rule.credential.interface'; +import { + CREDENTIAL_RULE_TYPES_COMMUNITY_READ_GLOBAL_REGISTERED, + CREDENTIAL_RULE_COMMUNITY_SELF_REMOVAL, + CREDENTIAL_RULE_TYPES_ACCESS_VIRTUAL_CONTRIBUTORS, + CREDENTIAL_RULE_TYPES_COMMUNITY_ADD_MEMBERS, + CREDENTIAL_RULE_TYPES_COMMUNITY_INVITE_MEMBERS, + POLICY_RULE_COMMUNITY_ADD_VC, + POLICY_RULE_COMMUNITY_INVITE_MEMBER, + CREDENTIAL_RULE_COMMUNITY_VIRTUAL_CONTRIBUTOR_REMOVAL, + CREDENTIAL_RULE_SPACE_HOST_ASSOCIATES_JOIN, + CREDENTIAL_RULE_TYPES_SPACE_COMMUNITY_JOIN_GLOBAL_REGISTERED, + CREDENTIAL_RULE_TYPES_SPACE_COMMUNITY_APPLY_GLOBAL_REGISTERED, + CREDENTIAL_RULE_SUBSPACE_PARENT_MEMBER_APPLY, + CREDENTIAL_RULE_SUBSPACE_PARENT_MEMBER_JOIN, + CREDENTIAL_RULE_COMMUNITY_ADD_MEMBER, +} from '@common/constants'; +import { RelationshipNotFoundException } from '@common/exceptions/relationship.not.found.exception'; +import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; +import { LicenseEngineService } from '@core/license-engine/license.engine.service'; +import { LicensePrivilege } from '@common/enums/license.privilege'; +import { AuthorizationPolicyRulePrivilege } from '@core/authorization/authorization.policy.rule.privilege'; +import { IAgent } from '@domain/agent'; +import { PlatformInvitationAuthorizationService } from '@platform/invitation/platform.invitation.service.authorization'; +import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; +import { EntityNotInitializedException } from '@common/exceptions/entity.not.initialized.exception'; +import { ApplicationAuthorizationService } from '@domain/community/application/application.service.authorization'; +import { InvitationAuthorizationService } from '@domain/community/invitation/invitation.service.authorization'; +import { VirtualContributorService } from '@domain/community/virtual-contributor/virtual.contributor.service'; +import { CommunityMembershipPolicy } from '@common/enums/community.membership.policy'; +import { CommunityRoleType } from '@common/enums/community.role'; +import { IRoleManager } from './role.manager.interface'; + +@Injectable() +export class RoleManagerAuthorizationService { + constructor( + private licenseEngineService: LicenseEngineService, + private roleManagerService: RoleManagerService, + private authorizationPolicyService: AuthorizationPolicyService, + private applicationAuthorizationService: ApplicationAuthorizationService, + private invitationAuthorizationService: InvitationAuthorizationService, + private virtualContributorService: VirtualContributorService, + private platformInvitationAuthorizationService: PlatformInvitationAuthorizationService + ) {} + + async applyAuthorizationPolicy( + roleManagerID: string, + parentAuthorization: IAuthorizationPolicy, + levelZeroSpaceAgent: IAgent, + spaceSettings: ISpaceSettings, + spaceMembershipAllowed: boolean, + isSubspace: boolean + ): Promise { + const roleManager = await this.roleManagerService.getRoleManagerOrFail( + roleManagerID, + { + relations: { + roles: true, + applications: true, + invitations: true, + platformInvitations: true, + }, + } + ); + if ( + !roleManager.roles || + !roleManager.applications || + !roleManager.invitations || + !roleManager.platformInvitations + ) { + throw new RelationshipNotFoundException( + `Unable to load child entities for roleManager authorization: ${roleManager.id} `, + LogContext.COMMUNITY + ); + } + const updatedAuthorizations: IAuthorizationPolicy[] = []; + + roleManager.authorization = + this.authorizationPolicyService.inheritParentAuthorization( + roleManager.authorization, + parentAuthorization + ); + + roleManager.authorization = this.appendPrivilegeRules( + roleManager.authorization + ); + + roleManager.authorization = await this.extendAuthorizationPolicy( + roleManager, + roleManager.authorization, + parentAuthorization?.anonymousReadAccess, + levelZeroSpaceAgent, + spaceSettings + ); + roleManager.authorization = this.appendVerifiedCredentialRules( + roleManager.authorization + ); + if (spaceMembershipAllowed) { + roleManager.authorization = + this.extendRoleManagerAuthorizationPolicySpace( + roleManager, + roleManager.authorization, + spaceSettings + ); + } + if (isSubspace) { + roleManager.authorization = this.extendAuthorizationPolicySubspace( + roleManager, + roleManager.authorization, + spaceSettings + ); + } + + // always false + roleManager.authorization.anonymousReadAccess = false; + + updatedAuthorizations.push(roleManager.authorization); + + for (const application of roleManager.applications) { + const applicationAuthReset = + await this.applicationAuthorizationService.applyAuthorizationPolicy( + application, + roleManager.authorization + ); + application.authorization = applicationAuthReset; + } + + for (const invitation of roleManager.invitations) { + const invitationReset = + await this.invitationAuthorizationService.applyAuthorizationPolicy( + invitation, + roleManager.authorization + ); + invitation.authorization = invitationReset; + } + + for (const externalInvitation of roleManager.platformInvitations) { + const platformInvitationAuthorization = + await this.platformInvitationAuthorizationService.applyAuthorizationPolicy( + externalInvitation, + roleManager.authorization + ); + updatedAuthorizations.push(platformInvitationAuthorization); + } + + return updatedAuthorizations; + } + + private extendRoleManagerAuthorizationPolicySpace( + roleManager: IRoleManager, + roleManagerAuthorization: IAuthorizationPolicy | undefined, + spaceSettings: ISpaceSettings + ): IAuthorizationPolicy { + if (!roleManagerAuthorization) + throw new EntityNotInitializedException( + `Authorization definition not found for: ${roleManager.id}`, + LogContext.SPACES + ); + + const newRules: IAuthorizationPolicyRuleCredential[] = []; + + const membershipPolicy = spaceSettings.membership.policy; + switch (membershipPolicy) { + case CommunityMembershipPolicy.APPLICATIONS: + const anyUserCanApply = + this.authorizationPolicyService.createCredentialRuleUsingTypesOnly( + [AuthorizationPrivilege.COMMUNITY_APPLY], + [AuthorizationCredential.GLOBAL_REGISTERED], + CREDENTIAL_RULE_TYPES_SPACE_COMMUNITY_APPLY_GLOBAL_REGISTERED + ); + anyUserCanApply.cascade = false; + newRules.push(anyUserCanApply); + break; + case CommunityMembershipPolicy.OPEN: + const anyUserCanJoin = + this.authorizationPolicyService.createCredentialRuleUsingTypesOnly( + [AuthorizationPrivilege.COMMUNITY_JOIN], + [AuthorizationCredential.GLOBAL_REGISTERED], + CREDENTIAL_RULE_TYPES_SPACE_COMMUNITY_JOIN_GLOBAL_REGISTERED + ); + anyUserCanJoin.cascade = false; + newRules.push(anyUserCanJoin); + break; + } + + // Associates of trusted organizations can join + const trustedOrganizationIDs: string[] = []; + for (const trustedOrganizationID of trustedOrganizationIDs) { + const hostOrgMembersCanJoin = + this.authorizationPolicyService.createCredentialRule( + [AuthorizationPrivilege.COMMUNITY_JOIN], + [ + { + type: AuthorizationCredential.ORGANIZATION_ASSOCIATE, + resourceID: trustedOrganizationID, + }, + ], + CREDENTIAL_RULE_SPACE_HOST_ASSOCIATES_JOIN + ); + hostOrgMembersCanJoin.cascade = false; + newRules.push(hostOrgMembersCanJoin); + } + + return this.authorizationPolicyService.appendCredentialAuthorizationRules( + roleManagerAuthorization, + newRules + ); + } + + private async extendAuthorizationPolicy( + roleManager: IRoleManager, + authorization: IAuthorizationPolicy | undefined, + allowGlobalRegisteredReadAccess: boolean | undefined, + levelZeroSpaceAgent: IAgent, + spaceSettings: ISpaceSettings + ): Promise { + const newRules: IAuthorizationPolicyRuleCredential[] = []; + + const globalAdminAddMembers = + this.authorizationPolicyService.createCredentialRuleUsingTypesOnly( + [AuthorizationPrivilege.COMMUNITY_ADD_MEMBER], + [ + AuthorizationCredential.GLOBAL_ADMIN, + AuthorizationCredential.GLOBAL_SUPPORT, + ], + CREDENTIAL_RULE_TYPES_COMMUNITY_ADD_MEMBERS + ); + newRules.push(globalAdminAddMembers); + + const inviteMembersCriterias: ICredentialDefinition[] = + this.roleManagerService.getCredentialsForRoleWithParents( + roleManager, + CommunityRoleType.ADMIN, + spaceSettings + ); + if (spaceSettings.membership.allowSubspaceAdminsToInviteMembers) { + // use the member credential to create subspace admin credential + const subspaceAdminCredential: ICredentialDefinition = + this.roleManagerService.getCredentialForRole( + roleManager, + CommunityRoleType.MEMBER + ); + subspaceAdminCredential.type = + AuthorizationCredential.SPACE_SUBSPACE_ADMIN; + inviteMembersCriterias.push(subspaceAdminCredential); + } + const spaceAdminsInvite = + this.authorizationPolicyService.createCredentialRule( + [ + AuthorizationPrivilege.COMMUNITY_INVITE, + AuthorizationPrivilege.COMMUNITY_ADD_MEMBER_VC_FROM_ACCOUNT, + ], + inviteMembersCriterias, + CREDENTIAL_RULE_TYPES_COMMUNITY_INVITE_MEMBERS + ); + spaceAdminsInvite.cascade = false; + newRules.push(spaceAdminsInvite); + + if (allowGlobalRegisteredReadAccess) { + const globalRegistered = + this.authorizationPolicyService.createCredentialRuleUsingTypesOnly( + [AuthorizationPrivilege.READ], + [AuthorizationCredential.GLOBAL_REGISTERED], + CREDENTIAL_RULE_TYPES_COMMUNITY_READ_GLOBAL_REGISTERED + ); + newRules.push(globalRegistered); + } + + const accessVirtualContributors = + await this.licenseEngineService.isAccessGranted( + LicensePrivilege.SPACE_VIRTUAL_CONTRIBUTOR_ACCESS, + levelZeroSpaceAgent + ); + if (accessVirtualContributors) { + const criterias: ICredentialDefinition[] = + this.roleManagerService.getCredentialsForRoleWithParents( + roleManager, + CommunityRoleType.ADMIN, + spaceSettings + ); + criterias.push({ + type: AuthorizationCredential.GLOBAL_ADMIN, + resourceID: '', + }); + const accessVCsRule = + this.authorizationPolicyService.createCredentialRule( + [AuthorizationPrivilege.ACCESS_VIRTUAL_CONTRIBUTOR], + criterias, + CREDENTIAL_RULE_TYPES_ACCESS_VIRTUAL_CONTRIBUTORS + ); + accessVCsRule.cascade = true; // TODO: ideally make this not cascade so it is more specific + newRules.push(accessVCsRule); + } + + // + const updatedAuthorization = + this.authorizationPolicyService.appendCredentialAuthorizationRules( + authorization, + newRules + ); + + return updatedAuthorization; + } + + private extendAuthorizationPolicySubspace( + roleManager: IRoleManager, + authorization: IAuthorizationPolicy | undefined, + spaceSettings: ISpaceSettings + ): IAuthorizationPolicy { + if (!authorization) + throw new EntityNotInitializedException( + 'Authorization definition not found', + LogContext.SPACES + ); + + const newRules: IAuthorizationPolicyRuleCredential[] = []; + + const parentRoleManagerCredential = + this.roleManagerService.getDirectParentCredentialForRole( + roleManager, + CommunityRoleType.MEMBER + ); + + // Allow member of the parent roleManager to Apply + if (parentRoleManagerCredential) { + const membershipSettings = spaceSettings.membership; + switch (membershipSettings.policy) { + case CommunityMembershipPolicy.APPLICATIONS: + const spaceMemberCanApply = + this.authorizationPolicyService.createCredentialRule( + [AuthorizationPrivilege.COMMUNITY_APPLY], + [parentRoleManagerCredential], + CREDENTIAL_RULE_SUBSPACE_PARENT_MEMBER_APPLY + ); + spaceMemberCanApply.cascade = false; + newRules.push(spaceMemberCanApply); + break; + case CommunityMembershipPolicy.OPEN: + const spaceMemberCanJoin = + this.authorizationPolicyService.createCredentialRule( + [AuthorizationPrivilege.COMMUNITY_JOIN], + [parentRoleManagerCredential], + CREDENTIAL_RULE_SUBSPACE_PARENT_MEMBER_JOIN + ); + spaceMemberCanJoin.cascade = false; + newRules.push(spaceMemberCanJoin); + break; + } + } + + const adminCredentials = + this.roleManagerService.getCredentialsForRoleWithParents( + roleManager, + CommunityRoleType.ADMIN, + spaceSettings + ); + + const addMembers = this.authorizationPolicyService.createCredentialRule( + [AuthorizationPrivilege.COMMUNITY_ADD_MEMBER], + adminCredentials, + CREDENTIAL_RULE_COMMUNITY_ADD_MEMBER + ); + addMembers.cascade = false; + newRules.push(addMembers); + + this.authorizationPolicyService.appendCredentialAuthorizationRules( + authorization, + newRules + ); + + return authorization; + } + + private appendVerifiedCredentialRules( + authorization: IAuthorizationPolicy + ): IAuthorizationPolicy { + const verifiedCredentialRules: AuthorizationPolicyRuleVerifiedCredential[] = + []; + + return this.authorizationPolicyService.appendVerifiedCredentialAuthorizationRules( + authorization, + verifiedCredentialRules + ); + } + + public extendAuthorizationPolicyForSelfRemoval( + roleManager: IRoleManager, + userToBeRemovedID: string + ): IAuthorizationPolicy { + const newRules: IAuthorizationPolicyRuleCredential[] = []; + + const userSelfRemovalRule = + this.authorizationPolicyService.createCredentialRule( + [AuthorizationPrivilege.GRANT], + [ + { + type: AuthorizationCredential.USER_SELF_MANAGEMENT, + resourceID: userToBeRemovedID, + }, + ], + CREDENTIAL_RULE_COMMUNITY_SELF_REMOVAL + ); + newRules.push(userSelfRemovalRule); + + const clonedRoleManagerAuthorization = + this.authorizationPolicyService.cloneAuthorizationPolicy( + roleManager.authorization + ); + + const updatedAuthorization = + this.authorizationPolicyService.appendCredentialAuthorizationRules( + clonedRoleManagerAuthorization, + newRules + ); + + return updatedAuthorization; + } + + public async extendAuthorizationPolicyForVirtualContributorRemoval( + roleManager: IRoleManager, + virtualContributorToBeRemoved: string + ): Promise { + const newRules: IAuthorizationPolicyRuleCredential[] = []; + + const accountHostCredentials = + await this.virtualContributorService.getAccountHostCredentials( + virtualContributorToBeRemoved + ); + + const userSelfRemovalRule = + this.authorizationPolicyService.createCredentialRule( + [AuthorizationPrivilege.GRANT], + accountHostCredentials, + CREDENTIAL_RULE_COMMUNITY_VIRTUAL_CONTRIBUTOR_REMOVAL + ); + newRules.push(userSelfRemovalRule); + + const clonedRoleManagerAuthorization = + this.authorizationPolicyService.cloneAuthorizationPolicy( + roleManager.authorization + ); + + const updatedAuthorization = + this.authorizationPolicyService.appendCredentialAuthorizationRules( + clonedRoleManagerAuthorization, + newRules + ); + + return updatedAuthorization; + } + + private appendPrivilegeRules( + authorization: IAuthorizationPolicy + ): IAuthorizationPolicy { + const createVCPrivilege = new AuthorizationPolicyRulePrivilege( + [AuthorizationPrivilege.COMMUNITY_ADD_MEMBER_VC_FROM_ACCOUNT], + AuthorizationPrivilege.GRANT, + POLICY_RULE_COMMUNITY_ADD_VC + ); + + // If you are able to add a member, then you are also logically able to invite a member + const invitePrivilege = new AuthorizationPolicyRulePrivilege( + [AuthorizationPrivilege.COMMUNITY_INVITE], + AuthorizationPrivilege.COMMUNITY_ADD_MEMBER, + POLICY_RULE_COMMUNITY_INVITE_MEMBER + ); + + return this.authorizationPolicyService.appendPrivilegeAuthorizationRules( + authorization, + [createVCPrivilege, invitePrivilege] + ); + } +} diff --git a/src/domain/access/role-manager/role.manager.service.spec.ts b/src/domain/access/role-manager/role.manager.service.spec.ts new file mode 100644 index 0000000000..a2cf419046 --- /dev/null +++ b/src/domain/access/role-manager/role.manager.service.spec.ts @@ -0,0 +1,30 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { RoleManager } from './role.manager.entity'; +import { RoleManagerService } from './role.manager.service'; +import { MockCacheManager } from '@test/mocks/cache-manager.mock'; +import { MockWinstonProvider } from '@test/mocks/winston.provider.mock'; +import { defaultMockerFactory } from '@test/utils/default.mocker.factory'; +import { repositoryProviderMockFactory } from '@test/utils/repository.provider.mock.factory'; + +describe('RoleManagerService', () => { + let service: RoleManagerService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + RoleManagerService, + repositoryProviderMockFactory(RoleManager), + MockCacheManager, + MockWinstonProvider, + ], + }) + .useMocker(defaultMockerFactory) + .compile(); + + service = module.get(RoleManagerService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/domain/access/role-manager/role.manager.service.ts b/src/domain/access/role-manager/role.manager.service.ts new file mode 100644 index 0000000000..7afd953df9 --- /dev/null +++ b/src/domain/access/role-manager/role.manager.service.ts @@ -0,0 +1,324 @@ +import { Inject, Injectable, LoggerService } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { + EntityNotFoundException, + RelationshipNotFoundException, +} from '@common/exceptions'; +import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; +import { FindOneOptions, Repository } from 'typeorm'; +import { AuthorizationPolicy } from '@domain/common/authorization-policy'; +import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service'; +import { LogContext } from '@common/enums/logging.context'; +import { IForm } from '@domain/common/form/form.interface'; +import { FormService } from '@domain/common/form/form.service'; +import { UpdateFormInput } from '@domain/common/form/dto/form.dto.update'; +import { CreateRoleManagerInput } from './dto/role.manager.dto.create'; +import { PlatformInvitationService } from '@platform/invitation/platform.invitation.service'; +import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type'; +import { ApplicationService } from '@domain/community/application/application.service'; +import { InvitationService } from '@domain/community/invitation/invitation.service'; +import { RoleManager } from './role.manager.entity'; +import { IRoleManager } from './role.manager.interface'; +import { RoleService } from '../role/role.service'; +import { CommunityRoleType } from '@common/enums/community.role'; +import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; +import { IRole } from '../role/role.interface'; +import { AuthorizationCredential } from '@common/enums/authorization.credential'; +import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; + +@Injectable() +export class RoleManagerService { + constructor( + private authorizationPolicyService: AuthorizationPolicyService, + private applicationService: ApplicationService, + private invitationService: InvitationService, + private platformInvitationService: PlatformInvitationService, + private formService: FormService, + private roleService: RoleService, + @InjectRepository(RoleManager) + private roleManagerRepository: Repository, + @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService + ) {} + + async createRoleManager( + roleManagerData: CreateRoleManagerInput + ): Promise { + const roleManager: IRoleManager = new RoleManager(); + roleManager.authorization = new AuthorizationPolicy( + AuthorizationPolicyType.ROLE_MANAGER + ); + roleManager.roles = []; + roleManager.applications = []; + roleManager.invitations = []; + roleManager.platformInvitations = []; + + roleManager.parentRoleManager = roleManagerData.parentRoleManager; + + for (const roleData of roleManagerData.roles) { + const role = this.roleService.createRole(roleData); + roleManager.roles.push(role); + } + + roleManager.applicationForm = this.formService.createForm( + roleManagerData.applicationForm + ); + + return roleManager; + } + + async getRoleManagerOrFail( + roleManagerID: string, + options?: FindOneOptions + ): Promise { + const roleManager = await this.roleManagerRepository.findOne({ + where: { id: roleManagerID }, + ...options, + }); + if (!roleManager) + throw new EntityNotFoundException( + `Unable to find RoleManager with ID: ${roleManagerID}`, + LogContext.COMMUNITY + ); + return roleManager; + } + + async removeRoleManager(roleManagerID: string): Promise { + // Note need to load it in with all contained entities so can remove fully + const roleManager = await this.getRoleManagerOrFail(roleManagerID, { + relations: { + roles: true, + applications: true, + invitations: true, + platformInvitations: true, + applicationForm: true, + }, + }); + if ( + !roleManager.roles || + !roleManager.applications || + !roleManager.invitations || + !roleManager.platformInvitations || + !roleManager.applicationForm + ) { + throw new RelationshipNotFoundException( + `Unable to load child entities for roleManager for deletion: ${roleManager.id} `, + LogContext.COMMUNITY + ); + } + + for (const role of roleManager.roles) { + await this.roleService.removeRole(role); + } + + if (roleManager.authorization) + await this.authorizationPolicyService.delete(roleManager.authorization); + + // Remove all applications + for (const application of roleManager.applications) { + await this.applicationService.deleteApplication({ + ID: application.id, + }); + } + + // Remove all invitations + for (const invitation of roleManager.invitations) { + await this.invitationService.deleteInvitation({ + ID: invitation.id, + }); + } + + for (const externalInvitation of roleManager.platformInvitations) { + await this.platformInvitationService.deletePlatformInvitation({ + ID: externalInvitation.id, + }); + } + + await this.formService.removeForm(roleManager.applicationForm); + + await this.roleManagerRepository.remove(roleManager as RoleManager); + return true; + } + + async save(roleManager: IRoleManager): Promise { + return await this.roleManagerRepository.save(roleManager); + } + + async getParentRoleManager( + roleManager: IRoleManager + ): Promise { + const roleManagerWithParent = await this.getRoleManagerOrFail( + roleManager.id, + { + relations: { parentRoleManager: true }, + } + ); + + const parentRoleManager = roleManagerWithParent?.parentRoleManager; + if (parentRoleManager) { + return await this.getRoleManagerOrFail(parentRoleManager.id); + } + return undefined; + } + + async updateApplicationForm( + roleManager: IRoleManager, + formData: UpdateFormInput + ): Promise { + const applicationForm = await this.getApplicationForm(roleManager); + roleManager.applicationForm = await this.formService.updateForm( + applicationForm, + formData + ); + return await this.save(roleManager); + } + + async getApplicationForm(roleManager: IRoleManager): Promise { + const roleManagerForm = await this.getRoleManagerOrFail(roleManager.id, { + relations: { applicationForm: true }, + }); + const applicationForm = roleManagerForm.applicationForm; + if (!applicationForm) { + throw new EntityNotFoundException( + `Unable to find Application Form for RoleManager with ID: ${roleManager.id}`, + LogContext.COMMUNITY + ); + } + return applicationForm; + } + + // Update the Community policy to have the right resource ID + public updateRoleResourceID( + roleManager: IRoleManager, + resourceID: string + ): IRoleManager { + const roleDefinitions = this.getRoleDefinitions(roleManager); + for (const roleDefinition of roleDefinitions) { + const credential = this.roleService.getCredentialForRole(roleDefinition); + credential.resourceID = resourceID; + } + + return roleManager; + } + + public inheritParentCredentials(roleManager: IRoleManager): IRoleManager { + const roleManagerParent = roleManager.parentRoleManager; + if (!roleManagerParent) { + throw new RelationshipNotFoundException( + `Unable to inherit parent credentials for role Manager ${roleManager.id}`, + LogContext.ROLES + ); + } + const roleDefinitions = this.getRoleDefinitions(roleManager); + + for (const roleDefinition of roleDefinitions) { + const parentRoleDefinition = this.getRoleDefinition( + roleManagerParent, + roleDefinition.type + ); + const parentCredentials: ICredentialDefinition[] = []; + const parentDirectCredential = + this.roleService.getCredentialForRole(parentRoleDefinition); + const parentParentCredentials = + this.roleService.getParentCredentialsForRole(parentRoleDefinition); + + parentCredentials.push(parentDirectCredential); + parentParentCredentials.forEach(c => parentCredentials?.push(c)); + + roleDefinition.parentCredentials = JSON.stringify(parentCredentials); + } + + return roleManager; + } + + getDirectParentCredentialForRole( + roleManager: IRoleManager, + roleType: CommunityRoleType + ): ICredentialDefinition | undefined { + const parentCredentials = this.getParentCredentialsForRole( + roleManager, + roleType + ); + + // First entry is the immediate parent + if (parentCredentials.length === 0) { + return undefined; + } + const directParentCredential = parentCredentials[0]; + return directParentCredential; + } + + public getParentCredentialsForRole( + roleManager: IRoleManager, + roleType: CommunityRoleType + ): ICredentialDefinition[] { + const roleDefinition = this.getRoleDefinition(roleManager, roleType); + return this.roleService.getParentCredentialsForRole(roleDefinition); + } + + public getCredentialsForRoleWithParents( + roleManager: IRoleManager, + roleType: CommunityRoleType, + spaceSettings: ISpaceSettings + ): ICredentialDefinition[] { + const result = this.getCredentialsForRole( + roleManager, + roleType, + spaceSettings + ); + return result.concat( + this.getParentCredentialsForRole(roleManager, roleType) + ); + } + + public getCredentialsForRole( + roleManager: IRoleManager, + roleType: CommunityRoleType, + spaceSettings: ISpaceSettings // TODO: would like not to have this here; for later + ): ICredentialDefinition[] { + const result = [this.getCredentialForRole(roleManager, roleType)]; + if ( + roleType === CommunityRoleType.ADMIN && + spaceSettings.privacy.allowPlatformSupportAsAdmin + ) { + result.push({ + type: AuthorizationCredential.GLOBAL_SUPPORT, + resourceID: '', + }); + } + return result; + } + + public getCredentialForRole( + roleManager: IRoleManager, + roleType: CommunityRoleType + ): ICredentialDefinition { + const roleDefinition = this.getRoleDefinition(roleManager, roleType); + return this.roleService.getCredentialForRole(roleDefinition); + } + + public getRoleDefinitions(roleManager: IRoleManager): IRole[] { + const roleDefinitions = roleManager.roles; + if (!roleDefinitions) { + throw new RelationshipNotFoundException( + `Unable to load roles for RoleManager: ${roleManager.id}`, + LogContext.COMMUNITY + ); + } + return roleDefinitions; + } + + public getRoleDefinition( + roleManager: IRoleManager, + roleType: CommunityRoleType + ): IRole { + const roleDefinitions = this.getRoleDefinitions(roleManager); + const role = roleDefinitions.find(rd => rd.type === roleType); + if (!role) { + throw new RelationshipNotFoundException( + `Unable to find Role for type ${roleType} for RoleManager: ${roleManager.id}`, + LogContext.COMMUNITY + ); + } + return role; + } +} diff --git a/src/domain/access/role/contributor.role.policy.interface.ts b/src/domain/access/role/contributor.role.policy.interface.ts new file mode 100644 index 0000000000..37bfa8d977 --- /dev/null +++ b/src/domain/access/role/contributor.role.policy.interface.ts @@ -0,0 +1,19 @@ +import { Field, ObjectType } from '@nestjs/graphql'; + +@ObjectType('ContributorRolePolicy') +export abstract class IContributorRolePolicy { + @Field(() => Boolean, { + description: 'Is this role enabled for this Contributor', + }) + enabled!: boolean; + + @Field(() => Number, { + description: 'Minimum number of Contributors in this role', + }) + minimum!: number; + + @Field(() => Number, { + description: 'Maximum number of Contributors in this role', + }) + maximum!: number; +} diff --git a/src/domain/access/role/contributor.role.policy.ts b/src/domain/access/role/contributor.role.policy.ts new file mode 100644 index 0000000000..381cb91d0e --- /dev/null +++ b/src/domain/access/role/contributor.role.policy.ts @@ -0,0 +1,13 @@ +import { IContributorRolePolicy } from './contributor.role.policy.interface'; + +export class ContributorRolePolicy implements IContributorRolePolicy { + minimum: number; + maximum: number; + enabled: boolean; + + constructor() { + this.minimum = -1; + this.maximum = -1; + this.enabled = false; + } +} diff --git a/src/domain/access/role/dto/role.dto.create.ts b/src/domain/access/role/dto/role.dto.create.ts new file mode 100644 index 0000000000..4585ff2c69 --- /dev/null +++ b/src/domain/access/role/dto/role.dto.create.ts @@ -0,0 +1,14 @@ +import { CommunityRoleType } from '@common/enums/community.role'; +import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; +import { IContributorRolePolicy } from '../contributor.role.policy.interface'; + +export class CreateRoleInput { + type!: CommunityRoleType; + requireBaseRole!: boolean; + requireParentRole!: boolean; + credentialData!: ICredentialDefinition; + parentCredentialsData!: ICredentialDefinition[]; + userPolicyData!: IContributorRolePolicy; + organizationPolicyData!: IContributorRolePolicy; + virtualContributorPolicyData!: IContributorRolePolicy; +} diff --git a/src/domain/access/role/role.entity.ts b/src/domain/access/role/role.entity.ts new file mode 100644 index 0000000000..6ad554d292 --- /dev/null +++ b/src/domain/access/role/role.entity.ts @@ -0,0 +1,40 @@ +import { BaseAlkemioEntity } from '@domain/common/entity/base-entity'; +import { Column, Entity, ManyToOne } from 'typeorm'; +import { IRole } from './role.interface'; +import { CommunityRoleType } from '@common/enums/community.role'; +import { ENUM_LENGTH } from '@common/constants/entity.field.length.constants'; +import { RoleManager } from '../role-manager'; + +@Entity() +export class Role extends BaseAlkemioEntity implements IRole { + @ManyToOne(() => RoleManager, manager => manager.roles, { + eager: false, + cascade: false, + onDelete: 'CASCADE', + }) + manager?: RoleManager; + + @Column('varchar', { length: ENUM_LENGTH, nullable: false }) + type!: CommunityRoleType; + + @Column('text', { nullable: false }) + credential!: string; + + @Column('text', { nullable: false }) + parentCredentials!: string; + + @Column('boolean', { nullable: false }) + requiresBaseRole!: boolean; + + @Column('boolean', { nullable: false }) + requiresParentRole!: boolean; + + @Column('text', { nullable: false }) + userPolicy!: string; + + @Column('text', { nullable: false }) + organizationPolicy!: string; + + @Column('text', { nullable: false }) + virtualContributorPolicy!: string; +} diff --git a/src/domain/access/role/role.interface.ts b/src/domain/access/role/role.interface.ts new file mode 100644 index 0000000000..3153334f71 --- /dev/null +++ b/src/domain/access/role/role.interface.ts @@ -0,0 +1,34 @@ +import { CommunityRoleType } from '@common/enums/community.role'; +import { IBaseAlkemio } from '@domain/common/entity/base-entity'; +import { Field, ObjectType } from '@nestjs/graphql'; + +@ObjectType('Role') +export abstract class IRole extends IBaseAlkemio { + @Field(() => CommunityRoleType, { + nullable: false, + description: 'The CommunityRole that this role definition is for.', + }) + type!: CommunityRoleType; + + @Field(() => Boolean, { + nullable: false, + description: + 'Flag to indicate if this Role requires the Base role to be held.', + }) + requiresBaseRole!: boolean; + + @Field(() => Boolean, { + nullable: false, + description: + 'Flag to indicate if this Role requires having the same role in the Parent RoleManager.', + }) + requiresParentRole!: boolean; + + credential!: string; + + parentCredentials!: string; + + userPolicy!: string; + organizationPolicy!: string; + virtualContributorPolicy!: string; +} diff --git a/src/domain/access/role/role.module.ts b/src/domain/access/role/role.module.ts new file mode 100644 index 0000000000..4e78c33c65 --- /dev/null +++ b/src/domain/access/role/role.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { Role } from './role.entity'; +import { RoleResolverFields } from './role.resolver.fields'; +import { RoleService } from './role.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([Role])], + providers: [RoleService, RoleResolverFields], + exports: [RoleService], +}) +export class RoleModule {} diff --git a/src/domain/access/role/role.resolver.fields.ts b/src/domain/access/role/role.resolver.fields.ts new file mode 100644 index 0000000000..0d60d61204 --- /dev/null +++ b/src/domain/access/role/role.resolver.fields.ts @@ -0,0 +1,51 @@ +import { Parent, ResolveField, Resolver } from '@nestjs/graphql'; +import { IRole } from './role.interface'; +import { RoleService } from './role.service'; +import { IContributorRolePolicy } from './contributor.role.policy.interface'; +import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; + +@Resolver(() => IRole) +export class RoleResolverFields { + constructor(private roleService: RoleService) {} + + @ResolveField('credential', () => ICredentialDefinition, { + nullable: false, + description: 'The Credential associated with this Role.', + }) + credential(@Parent() role: IRole): ICredentialDefinition { + return this.roleService.getCredentialForRole(role); + } + + @ResolveField('parentCredentials', () => [ICredentialDefinition], { + nullable: false, + description: 'The Credential associated with this Role.', + }) + parentCredentials(@Parent() role: IRole): ICredentialDefinition[] { + return this.roleService.getParentCredentialsForRole(role); + } + + @ResolveField('userPolicy', () => IContributorRolePolicy, { + nullable: false, + description: 'The role policy that applies for Users in this Role.', + }) + userPolicy(@Parent() role: IRole): IContributorRolePolicy { + return this.roleService.getUserPolicy(role); + } + + @ResolveField('organizationPolicy', () => IContributorRolePolicy, { + nullable: false, + description: 'The role policy that applies for Organizations in this Role.', + }) + organizationPolicy(@Parent() role: IRole): IContributorRolePolicy { + return this.roleService.getOrganizationPolicy(role); + } + + @ResolveField('virtualContributorPolicy', () => IContributorRolePolicy, { + nullable: false, + description: + 'The role policy that applies for VirtualContributors in this Role.', + }) + virtualContributorPolicy(@Parent() role: IRole): IContributorRolePolicy { + return this.roleService.getVirtualContributorPolicy(role); + } +} diff --git a/src/domain/access/role/role.service.ts b/src/domain/access/role/role.service.ts new file mode 100644 index 0000000000..3c727412af --- /dev/null +++ b/src/domain/access/role/role.service.ts @@ -0,0 +1,80 @@ +import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; +import { Inject, Injectable, LoggerService } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; +import { Repository } from 'typeorm'; +import { Role } from './role.entity'; +import { IRole } from './role.interface'; +import { CreateRoleInput } from './dto/role.dto.create'; +import { IContributorRolePolicy } from './contributor.role.policy.interface'; + +@Injectable() +export class RoleService { + constructor( + @InjectRepository(Role) + private roleRepository: Repository, + @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService + ) {} + + public createRole(roleData: CreateRoleInput): IRole { + const role = Role.create(roleData); + role.credential = JSON.stringify(roleData.credentialData); + role.parentCredentials = JSON.stringify(roleData.parentCredentialsData); + role.userPolicy = JSON.stringify(roleData.userPolicyData); + role.organizationPolicy = JSON.stringify(roleData.organizationPolicyData); + role.virtualContributorPolicy = JSON.stringify( + roleData.virtualContributorPolicyData + ); + return role; + } + + public async removeRole(role: IRole): Promise { + await this.roleRepository.remove(role as Role); + return true; + } + + public getParentCredentialsForRole(role: IRole): ICredentialDefinition[] { + const parentCredentials: ICredentialDefinition[] = JSON.parse( + role.parentCredentials + ); + return parentCredentials; + } + + public getCredentialsForRoleWithParents( + role: IRole + ): ICredentialDefinition[] { + const result = this.getCredentialsForRole(role); + return result.concat(this.getParentCredentialsForRole(role)); + } + + public getCredentialsForRole(role: IRole): ICredentialDefinition[] { + const result = [this.getCredentialForRole(role)]; + return result; + } + + public getCredentialForRole(role: IRole): ICredentialDefinition { + const result: ICredentialDefinition = JSON.parse(role.credential); + return result; + } + + public getUserPolicy(role: IRole): IContributorRolePolicy { + const result: IContributorRolePolicy = JSON.parse(role.userPolicy); + return result; + } + + public getOrganizationPolicy(role: IRole): IContributorRolePolicy { + const result: IContributorRolePolicy = JSON.parse(role.organizationPolicy); + return result; + } + + public getVirtualContributorPolicy(role: IRole): IContributorRolePolicy { + const result: IContributorRolePolicy = JSON.parse( + role.virtualContributorPolicy + ); + return result; + } + + public save(role: IRole): Promise { + return this.roleRepository.save(role); + } +} diff --git a/src/domain/collaboration/callout-contribution/callout.contribution.service.authorization.ts b/src/domain/collaboration/callout-contribution/callout.contribution.service.authorization.ts index c5fe4f9f75..459201f47e 100644 --- a/src/domain/collaboration/callout-contribution/callout.contribution.service.authorization.ts +++ b/src/domain/collaboration/callout-contribution/callout.contribution.service.authorization.ts @@ -10,7 +10,7 @@ import { EntityNotInitializedException } from '@common/exceptions'; import { LogContext } from '@common/enums/logging.context'; import { CommunityPolicyService } from '@domain/community/community-policy/community.policy.service'; import { IAuthorizationPolicyRuleCredential } from '@core/authorization/authorization.policy.rule.credential.interface'; -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { AuthorizationCredential, AuthorizationPrivilege } from '@common/enums'; import { CREDENTIAL_RULE_CONTRIBUTION_ADMINS_MOVE, @@ -207,7 +207,7 @@ export class CalloutContributionAuthorizationService { this.communityPolicyService.getCredentialsForRoleWithParents( communityPolicy, spaceSettings, - CommunityRole.ADMIN + CommunityRoleType.ADMIN ); credentials.push(...roleCredentials); } diff --git a/src/domain/collaboration/collaboration/collaboration.service.authorization.ts b/src/domain/collaboration/collaboration/collaboration.service.authorization.ts index 9387a838fc..1aa7e9a346 100644 --- a/src/domain/collaboration/collaboration/collaboration.service.authorization.ts +++ b/src/domain/collaboration/collaboration/collaboration.service.authorization.ts @@ -21,7 +21,7 @@ import { CREDENTIAL_RULE_TYPES_CALLOUT_SAVE_AS_TEMPLATE, POLICY_RULE_COLLABORATION_WHITEBOARD_CONTRIBUTORS_CREATE, } from '@common/constants'; -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { TimelineAuthorizationService } from '@domain/timeline/timeline/timeline.service.authorization'; import { InnovationFlowAuthorizationService } from '../innovation-flow/innovation.flow.service.authorization'; import { RelationshipNotFoundException } from '@common/exceptions/relationship.not.found.exception'; @@ -181,7 +181,7 @@ export class CollaborationAuthorizationService { this.communityPolicyService.getCredentialsForRole( policy, spaceSettings, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); // optionally add space members if (spaceSettings.collaboration.inheritMembershipRights) { @@ -189,7 +189,7 @@ export class CollaborationAuthorizationService { this.communityPolicyService.getCredentialsForRoleWithParents( policy, spaceSettings, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); } @@ -229,7 +229,7 @@ export class CollaborationAuthorizationService { const adminCriterias = this.communityPolicyService.getCredentialsForRole( policy, spaceSettings, - CommunityRole.ADMIN + CommunityRoleType.ADMIN ); adminCriterias.push({ type: AuthorizationCredential.GLOBAL_ADMIN, diff --git a/src/domain/collaboration/post/post.service.authorization.ts b/src/domain/collaboration/post/post.service.authorization.ts index 85e9fc624e..82500d66a9 100644 --- a/src/domain/collaboration/post/post.service.authorization.ts +++ b/src/domain/collaboration/post/post.service.authorization.ts @@ -17,7 +17,7 @@ import { } from '@common/constants'; import { ProfileAuthorizationService } from '@domain/common/profile/profile.service.authorization'; import { RoomAuthorizationService } from '@domain/communication/room/room.service.authorization'; -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { RelationshipNotFoundException } from '@common/exceptions/relationship.not.found.exception'; import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; @@ -129,7 +129,7 @@ export class PostAuthorizationService { this.communityPolicyService.getCredentialsForRoleWithParents( communityPolicy, spaceSettings, - CommunityRole.ADMIN + CommunityRoleType.ADMIN ); credentials.push(...roleCredentials); } diff --git a/src/domain/community/application/application.entity.ts b/src/domain/community/application/application.entity.ts index b0121666fb..8a8f9ec696 100644 --- a/src/domain/community/application/application.entity.ts +++ b/src/domain/community/application/application.entity.ts @@ -13,6 +13,7 @@ import { NVP } from '@domain/common/nvp/nvp.entity'; import { User } from '@domain/community/user/user.entity'; import { AuthorizableEntity } from '@domain/common/entity/authorizable-entity'; import { IQuestion } from '@domain/common/question/question.interface'; +import { RoleManager } from '@domain/access/role-manager'; @Entity() export class Application extends AuthorizableEntity implements IApplication { @OneToOne(() => Lifecycle, { @@ -40,4 +41,11 @@ export class Application extends AuthorizableEntity implements IApplication { onDelete: 'CASCADE', }) community?: Community; + + @ManyToOne(() => RoleManager, manager => manager.applications, { + eager: false, + cascade: false, + onDelete: 'CASCADE', + }) + roleManager?: RoleManager; } diff --git a/src/domain/community/community-policy/community.policy.resolver.fields.ts b/src/domain/community/community-policy/community.policy.resolver.fields.ts index 6515f79e62..f409184340 100644 --- a/src/domain/community/community-policy/community.policy.resolver.fields.ts +++ b/src/domain/community/community-policy/community.policy.resolver.fields.ts @@ -1,5 +1,5 @@ import { Parent, ResolveField, Resolver } from '@nestjs/graphql'; -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { ICommunityPolicy } from './community.policy.interface'; import { CommunityPolicyService } from './community.policy.service'; import { ICommunityRolePolicy } from './community.policy.role.interface'; @@ -15,7 +15,7 @@ export class CommunityPolicyResolverFields { member(@Parent() communityPolicy: ICommunityPolicy): ICommunityRolePolicy { return this.communityPolicyService.getCommunityRolePolicy( communityPolicy, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); } @@ -26,7 +26,7 @@ export class CommunityPolicyResolverFields { lead(@Parent() communityPolicy: ICommunityPolicy): ICommunityRolePolicy { return this.communityPolicyService.getCommunityRolePolicy( communityPolicy, - CommunityRole.LEAD + CommunityRoleType.LEAD ); } @@ -37,7 +37,7 @@ export class CommunityPolicyResolverFields { admin(@Parent() communityPolicy: ICommunityPolicy): ICommunityRolePolicy { return this.communityPolicyService.getCommunityRolePolicy( communityPolicy, - CommunityRole.ADMIN + CommunityRoleType.ADMIN ); } } diff --git a/src/domain/community/community-policy/community.policy.service.ts b/src/domain/community/community-policy/community.policy.service.ts index cace846468..c3a4333e01 100644 --- a/src/domain/community/community-policy/community.policy.service.ts +++ b/src/domain/community/community-policy/community.policy.service.ts @@ -1,4 +1,4 @@ -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { LogContext } from '@common/enums/logging.context'; import { EntityNotInitializedException } from '@common/exceptions/entity.not.initialized.exception'; import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; @@ -43,14 +43,14 @@ export class CommunityPolicyService { public getCommunityRolePolicy( policy: ICommunityPolicy, - role: CommunityRole + role: CommunityRoleType ): ICommunityRolePolicy { switch (role) { - case CommunityRole.MEMBER: + case CommunityRoleType.MEMBER: return this.deserializeRolePolicy(policy.member); - case CommunityRole.LEAD: + case CommunityRoleType.LEAD: return this.deserializeRolePolicy(policy.lead); - case CommunityRole.ADMIN: + case CommunityRoleType.ADMIN: return this.deserializeRolePolicy(policy.admin); default: throw new EntityNotInitializedException( @@ -62,7 +62,7 @@ export class CommunityPolicyService { getDirectParentCredentialForRole( policy: ICommunityPolicy, - role: CommunityRole + role: CommunityRoleType ): ICredentialDefinition | undefined { const rolePolicy = this.getCommunityRolePolicy(policy, role); @@ -76,7 +76,7 @@ export class CommunityPolicyService { public getParentCredentialsForRole( policy: ICommunityPolicy, - role: CommunityRole + role: CommunityRoleType ): ICredentialDefinition[] { const rolePolicy = this.getCommunityRolePolicy(policy, role); @@ -86,7 +86,7 @@ export class CommunityPolicyService { public getCredentialsForRoleWithParents( policy: ICommunityPolicy, spaceSettings: ISpaceSettings, - role: CommunityRole + role: CommunityRoleType ): ICredentialDefinition[] { const result = this.getCredentialsForRole(policy, spaceSettings, role); return result.concat(this.getParentCredentialsForRole(policy, role)); @@ -95,11 +95,11 @@ export class CommunityPolicyService { public getCredentialsForRole( policy: ICommunityPolicy, spaceSettings: ISpaceSettings, - role: CommunityRole + role: CommunityRoleType ): ICredentialDefinition[] { const result = [this.getCredentialForRole(policy, role)]; if ( - role === CommunityRole.ADMIN && + role === CommunityRoleType.ADMIN && spaceSettings.privacy.allowPlatformSupportAsAdmin ) { result.push({ @@ -112,7 +112,7 @@ export class CommunityPolicyService { public getCredentialForRole( policy: ICommunityPolicy, - role: CommunityRole + role: CommunityRoleType ): ICredentialDefinition { const rolePolicy = this.getCommunityRolePolicy(policy, role); return rolePolicy.credential; diff --git a/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts b/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts index e394d365e0..8e5c453133 100644 --- a/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts +++ b/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts @@ -12,7 +12,7 @@ import { EntityNotInitializedException } from '@common/exceptions'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { AuthorizationService } from '@core/authorization/authorization.service'; import { AuthorizationPolicy } from '@domain/common/authorization-policy'; -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { CommunityRoleService } from './community.role.service'; @Injectable() @@ -30,9 +30,8 @@ export class CommunityRoleApplicationLifecycleOptionsProvider { agentInfo: AgentInfo ): Promise { const applicationID = applicationEventData.applicationID; - const application = await this.applicationService.getApplicationOrFail( - applicationID - ); + const application = + await this.applicationService.getApplicationOrFail(applicationID); if (!application.lifecycle) throw new EntityNotInitializedException( @@ -80,7 +79,7 @@ export class CommunityRoleApplicationLifecycleOptionsProvider { await this.communityRoleService.assignUserToRole( community, userID, - CommunityRole.MEMBER, + CommunityRoleType.MEMBER, event.agentInfo, true ); diff --git a/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts b/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts index f5a1b26cd7..56f4b6f6c8 100644 --- a/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts +++ b/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts @@ -7,7 +7,7 @@ import { EntityNotInitializedException } from '@common/exceptions'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { AuthorizationService } from '@core/authorization/authorization.service'; import { AuthorizationPolicy } from '@domain/common/authorization-policy'; -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { InvitationService } from '../invitation/invitation.service'; import { InvitationEventInput } from '../invitation/dto/invitation.dto.event'; import { IInvitation } from '../invitation'; @@ -28,9 +28,8 @@ export class CommunityRoleInvitationLifecycleOptionsProvider { agentInfo: AgentInfo ): Promise { const invitationID = invitationEventData.invitationID; - const invitation = await this.invitationService.getInvitationOrFail( - invitationID - ); + const invitation = + await this.invitationService.getInvitationOrFail(invitationID); if (!invitation.lifecycle) throw new EntityNotInitializedException( @@ -114,7 +113,7 @@ export class CommunityRoleInvitationLifecycleOptionsProvider { await this.communityRoleService.assignContributorToRole( community.parentCommunity, contributorID, - CommunityRole.MEMBER, + CommunityRoleType.MEMBER, invitation.contributorType, event.agentInfo, true @@ -123,7 +122,7 @@ export class CommunityRoleInvitationLifecycleOptionsProvider { await this.communityRoleService.assignContributorToRole( community, contributorID, - CommunityRole.MEMBER, + CommunityRoleType.MEMBER, invitation.contributorType, event.agentInfo, true diff --git a/src/domain/community/community-role/community.role.resolver.fields.ts b/src/domain/community/community-role/community.role.resolver.fields.ts index faedfa2ca1..88e13a6966 100644 --- a/src/domain/community/community-role/community.role.resolver.fields.ts +++ b/src/domain/community/community-role/community.role.resolver.fields.ts @@ -11,7 +11,7 @@ import { IUser } from '@domain/community/user/user.interface'; import { IApplication } from '@domain/community/application'; import { AuthorizationPrivilege } from '@common/enums'; import { IOrganization } from '../organization'; -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { PaginationArgs, PaginatedUsers } from '@core/pagination'; import { PaginationInputOutOfBoundException } from '@common/exceptions'; import { UserService } from '../user/user.service'; @@ -59,7 +59,7 @@ export class CommunityResolverFields { return await this.communityRoleService.getUsersWithRole( community, - CommunityRole.MEMBER, + CommunityRoleType.MEMBER, limit ); } @@ -79,7 +79,7 @@ export class CommunityResolverFields { const memberRoleCredentials = this.communityRoleService.getCredentialDefinitionForRole( community, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); const parentCommunity = @@ -88,7 +88,7 @@ export class CommunityResolverFields { const parentCommunityMemberCredentials = parentCommunity ? this.communityRoleService.getCredentialDefinitionForRole( parentCommunity, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ) : undefined; @@ -113,8 +113,8 @@ export class CommunityResolverFields { @Profiling.api async usersInRole( @Parent() community: Community, - @Args('role', { type: () => CommunityRole, nullable: false }) - role: CommunityRole + @Args('role', { type: () => CommunityRoleType, nullable: false }) + role: CommunityRoleType ) { return await this.communityRoleService.getUsersWithRole(community, role); } @@ -129,8 +129,8 @@ export class CommunityResolverFields { @Profiling.api async organizationsInRole( @Parent() community: Community, - @Args('role', { type: () => CommunityRole, nullable: false }) - role: CommunityRole + @Args('role', { type: () => CommunityRoleType, nullable: false }) + role: CommunityRoleType ): Promise { return await this.communityRoleService.getOrganizationsWithRole( community, @@ -147,8 +147,8 @@ export class CommunityResolverFields { @Profiling.api async virtualsInRole( @Parent() community: Community, - @Args('role', { type: () => CommunityRole, nullable: false }) - role: CommunityRole + @Args('role', { type: () => CommunityRoleType, nullable: false }) + role: CommunityRoleType ) { return await this.communityRoleService.getVirtualContributorsWithRole( community, @@ -172,13 +172,13 @@ export class CommunityResolverFields { const memberRoleCredentials = this.communityRoleService.getCredentialDefinitionForRole( community, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); const leadRoleCredential = this.communityRoleService.getCredentialDefinitionForRole( community, - CommunityRole.LEAD + CommunityRoleType.LEAD ); const credentialCriteria = { @@ -243,7 +243,7 @@ export class CommunityResolverFields { } @UseGuards(GraphqlGuard) - @ResolveField('myRoles', () => [CommunityRole], { + @ResolveField('myRoles', () => [CommunityRoleType], { nullable: false, description: 'The roles on this community for the currently logged in user.', @@ -251,7 +251,7 @@ export class CommunityResolverFields { async myRoles( @CurrentUser() agentInfo: AgentInfo, @Parent() community: ICommunity - ): Promise { + ): Promise { return this.communityRoleService.getCommunityRoles(agentInfo, community); } diff --git a/src/domain/community/community-role/community.role.resolver.mutations.ts b/src/domain/community/community-role/community.role.resolver.mutations.ts index f30b70be83..18a5d23d6e 100644 --- a/src/domain/community/community-role/community.role.resolver.mutations.ts +++ b/src/domain/community/community-role/community.role.resolver.mutations.ts @@ -21,7 +21,7 @@ import { AgentBeginVerifiedCredentialOfferOutput } from '@domain/agent/agent/dto import { AlkemioUserClaim } from '@services/external/trust-registry/trust.registry.claim/claim.alkemio.user'; import { RemoveCommunityRoleFromOrganizationInput } from './dto/community.role.dto.role.remove.organization'; import { AssignCommunityRoleToOrganizationInput } from './dto/community.role.dto.role.assign.organization'; -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { AssignCommunityRoleToUserInput } from './dto/community.role.dto.role.assign.user'; import { NotificationAdapter } from '@services/adapters/notification-adapter/notification.adapter'; import { NotificationInputCommunityApplication } from '@services/adapters/notification-adapter/dto/notification.dto.input.community.application'; @@ -95,7 +95,7 @@ export class CommunityRoleResolverMutations { ); let requiredPrivilege = AuthorizationPrivilege.GRANT; - if (roleData.role === CommunityRole.MEMBER) { + if (roleData.role === CommunityRoleType.MEMBER) { requiredPrivilege = AuthorizationPrivilege.COMMUNITY_ADD_MEMBER; } @@ -164,7 +164,7 @@ export class CommunityRoleResolverMutations { ); let requiredPrivilege = AuthorizationPrivilege.GRANT; - if (roleData.role === CommunityRole.MEMBER) { + if (roleData.role === CommunityRoleType.MEMBER) { const sameAccount = await this.communityRoleService.isCommunityAccountMatchingVcAccount( community.id, @@ -348,7 +348,7 @@ export class CommunityRoleResolverMutations { const userIsMemberInParent = await this.communityRoleService.isInRole( agent, community.parentCommunity, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); if (!userIsMemberInParent) { throw new CommunityMembershipException( @@ -664,7 +664,7 @@ export class CommunityRoleResolverMutations { await this.communityRoleService.assignUserToRole( community, agentInfo.userID, - CommunityRole.MEMBER, + CommunityRoleType.MEMBER, agentInfo, true ); diff --git a/src/domain/community/community-role/community.role.service.ts b/src/domain/community/community-role/community.role.service.ts index 6c016149e0..c1577ecd5f 100644 --- a/src/domain/community/community-role/community.role.service.ts +++ b/src/domain/community/community-role/community.role.service.ts @@ -19,7 +19,7 @@ import { OrganizationService } from '../organization/organization.service'; import { IOrganization } from '../organization/organization.interface'; import { IAgent } from '@domain/agent/agent/agent.interface'; import { CredentialDefinition } from '@domain/agent/credential/credential.definition'; -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { CommunityContributorsUpdateType } from '@common/enums/community.contributors.update.type'; import { CommunityContributorType } from '@common/enums/community.contributor.type'; import { ICommunityRolePolicy } from '../community-policy/community.policy.role.interface'; @@ -64,7 +64,7 @@ export class CommunityRoleService { public async removeAllCommunityRoles(community: ICommunity) { // Remove all issued role credentials for contributors - for (const role of Object.values(CommunityRole)) { + for (const role of Object.values(CommunityRoleType)) { const users = await this.getUsersWithRole(community, role); for (const user of users) { await this.removeUserFromRole(community, user.id, role, false); @@ -120,12 +120,12 @@ export class CommunityRoleService { async getCommunityRoles( agentInfo: AgentInfo, community: ICommunity - ): Promise { - const result: CommunityRole[] = []; + ): Promise { + const result: CommunityRoleType[] = []; const agent = await this.agentService.getAgentOrFail(agentInfo.agentID); - const roles: CommunityRole[] = Object.values( - CommunityRole - ) as CommunityRole[]; + const roles: CommunityRoleType[] = Object.values( + CommunityRoleType + ) as CommunityRoleType[]; for (const role of roles) { const hasAgentRole = await this.isInRole(agent, community, role); if (hasAgentRole) { @@ -195,7 +195,7 @@ export class CommunityRoleService { async getUsersWithRole( community: ICommunity, - role: CommunityRole, + role: CommunityRoleType, limit?: number ): Promise { const membershipCredential = this.getCredentialDefinitionForRole( @@ -213,7 +213,7 @@ export class CommunityRoleService { async getVirtualContributorsWithRole( community: ICommunity, - role: CommunityRole + role: CommunityRoleType ): Promise { const membershipCredential = this.getCredentialDefinitionForRole( community, @@ -229,7 +229,7 @@ export class CommunityRoleService { async getOrganizationsWithRole( community: ICommunity, - role: CommunityRole + role: CommunityRoleType ): Promise { const membershipCredential = this.getCredentialDefinitionForRole( community, @@ -243,7 +243,7 @@ export class CommunityRoleService { async countContributorsPerRole( community: ICommunity, - role: CommunityRole, + role: CommunityRoleType, contributorType: CommunityContributorType ): Promise { const membershipCredential = this.getCredentialDefinitionForRole( @@ -270,7 +270,7 @@ export class CommunityRoleService { getCredentialDefinitionForRole( community: ICommunity, - role: CommunityRole + role: CommunityRoleType ): CredentialDefinition { const policyRole = this.communityService.getCommunityPolicyForRole( community, @@ -282,7 +282,7 @@ export class CommunityRoleService { async assignContributorToRole( community: ICommunity, contributorID: string, - role: CommunityRole, + role: CommunityRoleType, contributorType: CommunityContributorType, agentInfo?: AgentInfo, triggerNewMemberEvents = false @@ -321,7 +321,7 @@ export class CommunityRoleService { async assignUserToRole( community: ICommunity, userID: string, - role: CommunityRole, + role: CommunityRoleType, agentInfo?: AgentInfo, triggerNewMemberEvents = false ): Promise { @@ -346,7 +346,7 @@ export class CommunityRoleService { role, CommunityContributorType.USER ); - if (role === CommunityRole.ADMIN && parentCommunity) { + if (role === CommunityRoleType.ADMIN && parentCommunity) { // also assign as subspace admin in parent community if there is a parent community const credential = this.getCredentialForImplicitRole( parentCommunity, @@ -377,11 +377,11 @@ export class CommunityRoleService { private async contributorAddedToRole( contributor: IContributor, community: ICommunity, - role: CommunityRole, + role: CommunityRoleType, agentInfo?: AgentInfo, triggerNewMemberEvents = false ) { - if (role === CommunityRole.MEMBER) { + if (role === CommunityRoleType.MEMBER) { this.communityService.addMemberToCommunication(contributor, community); if (agentInfo) { @@ -413,7 +413,7 @@ export class CommunityRoleService { async assignVirtualToRole( community: ICommunity, virtualContributorID: string, - role: CommunityRole, + role: CommunityRoleType, agentInfo?: AgentInfo, triggerNewMemberEvents = false ): Promise { @@ -495,7 +495,7 @@ export class CommunityRoleService { async assignOrganizationToRole( community: ICommunity, organizationID: string, - role: CommunityRole + role: CommunityRoleType ): Promise { const { organization, agent } = await this.organizationService.getOrganizationAndAgent(organizationID); @@ -513,7 +513,7 @@ export class CommunityRoleService { async removeUserFromRole( community: ICommunity, userID: string, - role: CommunityRole, + role: CommunityRoleType, validatePolicyLimits = true ): Promise { const { user, agent } = await this.userService.getUserAndAgent(userID); @@ -528,14 +528,14 @@ export class CommunityRoleService { const parentCommunity = await this.communityService.getParentCommunity(community); - if (role === CommunityRole.ADMIN && parentCommunity) { + if (role === CommunityRoleType.ADMIN && parentCommunity) { // Check if an admin anywhere else in the community const peerCommunities = await this.communityService.getPeerCommunites( parentCommunity, community ); const hasAnotherAdminRole = peerCommunities.some(pc => - this.isInRole(agent, pc, CommunityRole.ADMIN) + this.isInRole(agent, pc, CommunityRoleType.ADMIN) ); if (!hasAnotherAdminRole) { @@ -547,7 +547,7 @@ export class CommunityRoleService { } } - if (role === CommunityRole.MEMBER) { + if (role === CommunityRoleType.MEMBER) { await this.communityService.removeMemberFromCommunication( community, user @@ -560,7 +560,7 @@ export class CommunityRoleService { async removeOrganizationFromRole( community: ICommunity, organizationID: string, - role: CommunityRole, + role: CommunityRoleType, validatePolicyLimits = true ): Promise { const { organization, agent } = @@ -580,7 +580,7 @@ export class CommunityRoleService { async removeVirtualFromRole( community: ICommunity, virtualContributorID: string, - role: CommunityRole, + role: CommunityRoleType, validatePolicyLimits = true ): Promise { const { virtualContributor, agent } = @@ -612,7 +612,7 @@ export class CommunityRoleService { private async validateUserCommunityPolicy( community: ICommunity, communityPolicyRole: ICommunityRolePolicy, - role: CommunityRole, + role: CommunityRoleType, action: CommunityContributorsUpdateType ) { const userMembersCount = await this.countContributorsPerRole( @@ -643,7 +643,7 @@ export class CommunityRoleService { private async validateOrganizationCommunityPolicy( community: ICommunity, communityPolicyRole: ICommunityRolePolicy, - role: CommunityRole, + role: CommunityRoleType, action: CommunityContributorsUpdateType ) { const orgMemberCount = await this.countContributorsPerRole( @@ -674,7 +674,7 @@ export class CommunityRoleService { private async validateCommunityPolicyLimits( community: ICommunity, communityPolicyRole: ICommunityRolePolicy, - role: CommunityRole, + role: CommunityRoleType, action: CommunityContributorsUpdateType, contributorType: CommunityContributorType ) { @@ -698,7 +698,7 @@ export class CommunityRoleService { public async assignContributorAgentToRole( community: ICommunity, agent: IAgent, - role: CommunityRole, + role: CommunityRoleType, contributorType: CommunityContributorType ): Promise { const communityPolicyRole = this.communityService.getCommunityPolicyForRole( @@ -755,7 +755,7 @@ export class CommunityRoleService { // Use the admin credential to get the resourceID const adminCredential = this.getCredentialDefinitionForRole( community, - CommunityRole.ADMIN + CommunityRoleType.ADMIN ); const resourceID = adminCredential.resourceID; switch (role) { @@ -776,7 +776,7 @@ export class CommunityRoleService { private async removeContributorFromRole( community: ICommunity, agent: IAgent, - role: CommunityRole, + role: CommunityRoleType, contributorType: CommunityContributorType, validatePolicyLimits: boolean ): Promise { @@ -804,7 +804,7 @@ export class CommunityRoleService { async isMember(agent: IAgent, community: ICommunity): Promise { const membershipCredential = this.getCredentialDefinitionForRole( community, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); const validCredential = await this.agentService.hasValidCredential( @@ -820,7 +820,7 @@ export class CommunityRoleService { async isInRole( agent: IAgent, community: ICommunity, - role: CommunityRole + role: CommunityRoleType ): Promise { const membershipCredential = this.getCredentialDefinitionForRole( community, @@ -1065,7 +1065,7 @@ export class CommunityRoleService { async getMembersCount(community: ICommunity): Promise { const membershipCredential = this.getCredentialDefinitionForRole( community, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); const credentialMatches = diff --git a/src/domain/community/community-role/dto/community.role.dto.role.assign.organization.ts b/src/domain/community/community-role/dto/community.role.dto.role.assign.organization.ts index f661ed73c6..ea57e60288 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.assign.organization.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.assign.organization.ts @@ -1,4 +1,4 @@ -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { UUID, UUID_NAMEID } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @@ -10,6 +10,6 @@ export class AssignCommunityRoleToOrganizationInput { @Field(() => UUID_NAMEID, { nullable: false }) organizationID!: string; - @Field(() => CommunityRole, { nullable: false }) - role!: CommunityRole; + @Field(() => CommunityRoleType, { nullable: false }) + role!: CommunityRoleType; } diff --git a/src/domain/community/community-role/dto/community.role.dto.role.assign.user.ts b/src/domain/community/community-role/dto/community.role.dto.role.assign.user.ts index f020cf99d6..898cf5390f 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.assign.user.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.assign.user.ts @@ -1,4 +1,4 @@ -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { UUID, UUID_NAMEID_EMAIL } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @@ -10,6 +10,6 @@ export class AssignCommunityRoleToUserInput { @Field(() => UUID_NAMEID_EMAIL, { nullable: false }) userID!: string; - @Field(() => CommunityRole, { nullable: false }) - role!: CommunityRole; + @Field(() => CommunityRoleType, { nullable: false }) + role!: CommunityRoleType; } diff --git a/src/domain/community/community-role/dto/community.role.dto.role.assign.virtual.ts b/src/domain/community/community-role/dto/community.role.dto.role.assign.virtual.ts index 516667628c..df16bd4fae 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.assign.virtual.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.assign.virtual.ts @@ -1,4 +1,4 @@ -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { UUID, UUID_NAMEID } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @@ -10,6 +10,6 @@ export class AssignCommunityRoleToVirtualInput { @Field(() => UUID_NAMEID, { nullable: false }) virtualContributorID!: string; - @Field(() => CommunityRole, { nullable: false }) - role!: CommunityRole; + @Field(() => CommunityRoleType, { nullable: false }) + role!: CommunityRoleType; } diff --git a/src/domain/community/community-role/dto/community.role.dto.role.remove.organization.ts b/src/domain/community/community-role/dto/community.role.dto.role.remove.organization.ts index b2f19218a8..877df36769 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.remove.organization.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.remove.organization.ts @@ -1,4 +1,4 @@ -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { UUID, UUID_NAMEID } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @@ -10,6 +10,6 @@ export class RemoveCommunityRoleFromOrganizationInput { @Field(() => UUID_NAMEID, { nullable: false }) organizationID!: string; - @Field(() => CommunityRole, { nullable: false }) - role!: CommunityRole; + @Field(() => CommunityRoleType, { nullable: false }) + role!: CommunityRoleType; } diff --git a/src/domain/community/community-role/dto/community.role.dto.role.remove.user.ts b/src/domain/community/community-role/dto/community.role.dto.role.remove.user.ts index 37af76929c..68e76d11e1 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.remove.user.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.remove.user.ts @@ -1,4 +1,4 @@ -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { UUID, UUID_NAMEID_EMAIL } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @@ -10,6 +10,6 @@ export class RemoveCommunityRoleFromUserInput { @Field(() => UUID_NAMEID_EMAIL, { nullable: false }) userID!: string; - @Field(() => CommunityRole, { nullable: false }) - role!: CommunityRole; + @Field(() => CommunityRoleType, { nullable: false }) + role!: CommunityRoleType; } diff --git a/src/domain/community/community-role/dto/community.role.dto.role.remove.virtual.ts b/src/domain/community/community-role/dto/community.role.dto.role.remove.virtual.ts index d30223461d..ba0649e47b 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.remove.virtual.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.remove.virtual.ts @@ -1,4 +1,4 @@ -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { UUID, UUID_NAMEID } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @@ -10,6 +10,6 @@ export class RemoveCommunityRoleFromVirtualInput { @Field(() => UUID_NAMEID, { nullable: false }) virtualContributorID!: string; - @Field(() => CommunityRole, { nullable: false }) - role!: CommunityRole; + @Field(() => CommunityRoleType, { nullable: false }) + role!: CommunityRoleType; } diff --git a/src/domain/community/community/community.service.authorization.ts b/src/domain/community/community/community.service.authorization.ts index 7d01432d45..5b4963599f 100644 --- a/src/domain/community/community/community.service.authorization.ts +++ b/src/domain/community/community/community.service.authorization.ts @@ -35,7 +35,7 @@ import { CommunityGuidelinesAuthorizationService } from '../community-guidelines import { CommunityPolicyService } from '../community-policy/community.policy.service'; import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; import { ICommunityPolicy } from '../community-policy/community.policy.interface'; -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { LicenseEngineService } from '@core/license-engine/license.engine.service'; import { LicensePrivilege } from '@common/enums/license.privilege'; import { AuthorizationPolicyRulePrivilege } from '@core/authorization/authorization.policy.rule.privilege'; @@ -286,14 +286,14 @@ export class CommunityAuthorizationService { this.communityPolicyService.getCredentialsForRoleWithParents( policy, spaceSettings, - CommunityRole.ADMIN + CommunityRoleType.ADMIN ); if (spaceSettings.membership.allowSubspaceAdminsToInviteMembers) { // use the member credential to create subspace admin credential const subspaceAdminCredential: ICredentialDefinition = this.communityPolicyService.getCredentialForRole( policy, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); subspaceAdminCredential.type = AuthorizationCredential.SPACE_SUBSPACE_ADMIN; @@ -331,7 +331,7 @@ export class CommunityAuthorizationService { this.communityPolicyService.getCredentialsForRoleWithParents( policy, spaceSettings, - CommunityRole.ADMIN + CommunityRoleType.ADMIN ); criterias.push({ type: AuthorizationCredential.GLOBAL_ADMIN, @@ -373,7 +373,7 @@ export class CommunityAuthorizationService { const parentCommunityCredential = this.communityPolicyService.getDirectParentCredentialForRole( policy, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); // Allow member of the parent community to Apply @@ -407,7 +407,7 @@ export class CommunityAuthorizationService { this.communityPolicyService.getCredentialsForRoleWithParents( policy, spaceSettings, - CommunityRole.ADMIN + CommunityRoleType.ADMIN ); const addMembers = this.authorizationPolicyService.createCredentialRule( diff --git a/src/domain/community/community/community.service.ts b/src/domain/community/community/community.service.ts index c380cae288..4caa50b5c3 100644 --- a/src/domain/community/community/community.service.ts +++ b/src/domain/community/community/community.service.ts @@ -17,7 +17,7 @@ import { AuthorizationPolicyService } from '@domain/common/authorization-policy/ import { CommunicationService } from '@domain/communication/communication/communication.service'; import { ICommunication } from '@domain/communication/communication'; import { LogContext } from '@common/enums/logging.context'; -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { ICommunityRolePolicy } from '../community-policy/community.policy.role.interface'; import { ICommunityPolicy } from '../community-policy/community.policy.interface'; import { CommunityPolicyService } from '../community-policy/community.policy.service'; @@ -430,7 +430,7 @@ export class CommunityService { public getCommunityPolicyForRole( community: ICommunity, - role: CommunityRole + role: CommunityRoleType ): ICommunityRolePolicy { const policy = this.getCommunityPolicy(community); return this.communityPolicyService.getCommunityRolePolicy(policy, role); diff --git a/src/domain/community/invitation/invitation.entity.ts b/src/domain/community/invitation/invitation.entity.ts index 4bba367283..f6f30e0939 100644 --- a/src/domain/community/invitation/invitation.entity.ts +++ b/src/domain/community/invitation/invitation.entity.ts @@ -5,6 +5,7 @@ import { IInvitation } from './invitation.interface'; import { AuthorizableEntity } from '@domain/common/entity/authorizable-entity'; import { CommunityContributorType } from '@common/enums/community.contributor.type'; import { ENUM_LENGTH, MID_TEXT_LENGTH, UUID_LENGTH } from '@common/constants'; +import { RoleManager } from '@domain/access/role-manager/role.manager.entity'; @Entity() export class Invitation extends AuthorizableEntity implements IInvitation { // todo ID in migration is varchar - must be char(36) @@ -37,4 +38,11 @@ export class Invitation extends AuthorizableEntity implements IInvitation { @Column('varchar', { length: ENUM_LENGTH, nullable: false }) contributorType!: CommunityContributorType; + + @ManyToOne(() => RoleManager, manager => manager.invitations, { + eager: false, + cascade: false, + onDelete: 'CASCADE', + }) + roleManager?: RoleManager; } diff --git a/src/domain/space/space/space.service.authorization.ts b/src/domain/space/space/space.service.authorization.ts index 855181fad7..41989ce689 100644 --- a/src/domain/space/space/space.service.authorization.ts +++ b/src/domain/space/space/space.service.authorization.ts @@ -18,7 +18,7 @@ import { CollaborationAuthorizationService } from '@domain/collaboration/collabo import { ContextAuthorizationService } from '@domain/context/context/context.service.authorization'; import { ICommunityPolicy } from '@domain/community/community-policy/community.policy.interface'; import { SpacePrivacyMode } from '@common/enums/space.privacy.mode'; -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { POLICY_RULE_SPACE_CREATE_SUBSPACE, CREDENTIAL_RULE_SPACE_MEMBERS_READ, @@ -120,7 +120,7 @@ export class SpaceAuthorizationService { this.communityPolicyService.getCredentialsForRole( parentCommunityPolicyWithSettings, spaceSettings, - CommunityRole.ADMIN + CommunityRoleType.ADMIN ); } @@ -393,7 +393,7 @@ export class SpaceAuthorizationService { const memberCriteras = this.communityPolicyService.getCredentialsForRole( policy, spaceSettings, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); const spaceMember = this.authorizationPolicyService.createCredentialRule( [AuthorizationPrivilege.READ], @@ -406,7 +406,7 @@ export class SpaceAuthorizationService { this.communityPolicyService.getCredentialsForRole( policy, spaceSettings, - CommunityRole.ADMIN + CommunityRoleType.ADMIN ); const spaceAdmin = this.authorizationPolicyService.createCredentialRule( [ @@ -460,7 +460,7 @@ export class SpaceAuthorizationService { const memberCriteria = this.communityPolicyService.getCredentialsForRole( policy, spaceSettings, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); const collaborationSettings = spaceSettings.collaboration; if ( @@ -470,7 +470,7 @@ export class SpaceAuthorizationService { const parentCredential = this.communityPolicyService.getDirectParentCredentialForRole( policy, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); if (parentCredential) memberCriteria.push(parentCredential); } @@ -493,7 +493,7 @@ export class SpaceAuthorizationService { ...this.communityPolicyService.getCredentialsForRoleWithParents( policy, spaceSettings, - CommunityRole.ADMIN + CommunityRoleType.ADMIN ), ]; const subspaceSpaceAdmins = diff --git a/src/domain/space/space/space.service.ts b/src/domain/space/space/space.service.ts index deeb784008..13de732e6a 100644 --- a/src/domain/space/space/space.service.ts +++ b/src/domain/space/space/space.service.ts @@ -53,7 +53,7 @@ import { SpaceDefaultsService } from '../space.defaults/space.defaults.service'; import { SpaceSettingsService } from '../space.settings/space.settings.service'; import { UpdateSpaceSettingsEntityInput } from '../space.settings/dto/space.settings.dto.update'; import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service'; -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { SpaceLevel } from '@common/enums/space.level'; import { UpdateSpaceSettingsInput } from './dto/space.dto.update.settings'; import { IContributor } from '@domain/community/contributor/contributor.interface'; @@ -982,7 +982,7 @@ export class SpaceService { public async assignContributorToRole( space: ISpace, contributor: IContributor, - role: CommunityRole, + role: CommunityRoleType, type: CommunityContributorType ) { if (!space.community) { @@ -1032,21 +1032,21 @@ export class SpaceService { await this.communityRoleService.assignUserToRole( space.community, agentInfo.userID, - CommunityRole.MEMBER, + CommunityRoleType.MEMBER, agentInfo ); await this.communityRoleService.assignUserToRole( space.community, agentInfo.userID, - CommunityRole.LEAD, + CommunityRoleType.LEAD, agentInfo ); await this.communityRoleService.assignUserToRole( space.community, agentInfo.userID, - CommunityRole.ADMIN, + CommunityRoleType.ADMIN, agentInfo ); } diff --git a/src/platform/admin/communication/admin.communication.service.ts b/src/platform/admin/communication/admin.communication.service.ts index f3eeb17c46..fd8d9b8214 100644 --- a/src/platform/admin/communication/admin.communication.service.ts +++ b/src/platform/admin/communication/admin.communication.service.ts @@ -13,7 +13,7 @@ import { CommunicationAdminOrphanedUsageResult } from './dto/admin.communication import { CommunicationAdminRoomResult } from './dto/admin.communication.dto.orphaned.room.result'; import { CommunicationAdminRemoveOrphanedRoomInput } from './dto/admin.communication.dto.remove.orphaned.room'; import { ValidationException } from '@common/exceptions'; -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { IRoom } from '@domain/communication/room/room.interface'; import { CommunityRoleService } from '@domain/community/community-role/community.role.service'; @@ -39,7 +39,7 @@ export class AdminCommunicationService { ); const communityMembers = await this.communityRoleService.getUsersWithRole( community, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); const communication = await this.communityService.getCommunication( community.id, @@ -110,7 +110,7 @@ export class AdminCommunicationService { ); const communityMembers = await this.communityRoleService.getUsersWithRole( community, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); for (const communityMember of communityMembers) { await this.communicationService.addContributorToCommunications( diff --git a/src/platform/invitation/platform.invitation.entity.ts b/src/platform/invitation/platform.invitation.entity.ts index e9a1e934c6..6d791699c0 100644 --- a/src/platform/invitation/platform.invitation.entity.ts +++ b/src/platform/invitation/platform.invitation.entity.ts @@ -10,6 +10,7 @@ import { SMALL_TEXT_LENGTH, UUID_LENGTH, } from '@common/constants'; +import { RoleManager } from '@domain/access/role-manager/role.manager.entity'; @Entity() export class PlatformInvitation extends AuthorizableEntity @@ -23,6 +24,13 @@ export class PlatformInvitation }) community?: Community; + @ManyToOne(() => RoleManager, manager => manager.platformInvitations, { + eager: false, + cascade: false, + onDelete: 'CASCADE', + }) + roleManager?: RoleManager; + @Column('boolean', { default: false }) communityInvitedToParent!: boolean; diff --git a/src/services/api/conversion/conversion.service.ts b/src/services/api/conversion/conversion.service.ts index 0198e3b717..e0d64011e0 100644 --- a/src/services/api/conversion/conversion.service.ts +++ b/src/services/api/conversion/conversion.service.ts @@ -8,7 +8,7 @@ import { EntityNotInitializedException, ValidationException, } from '@common/exceptions'; -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { IOrganization } from '@domain/community/organization/organization.interface'; import { IUser } from '@domain/community/user/user.interface'; import { ICommunity } from '@domain/community/community/community.interface'; @@ -84,7 +84,7 @@ export class ConversionService { const challengeCommunityLeadOrgs = await this.communityRoleService.getOrganizationsWithRole( subspace.community, - CommunityRole.LEAD + CommunityRoleType.LEAD ); if (challengeCommunityLeadOrgs.length !== 1) { throw new ValidationException( @@ -130,15 +130,15 @@ export class ConversionService { const userMembers = await this.communityRoleService.getUsersWithRole( space.community, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); const userLeads = await this.communityRoleService.getUsersWithRole( space.community, - CommunityRole.LEAD + CommunityRoleType.LEAD ); const orgMembers = await this.communityRoleService.getOrganizationsWithRole( space.community, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); // Remove the contributors from old roles @@ -153,7 +153,7 @@ export class ConversionService { await this.communityRoleService.removeUserFromRole( space.community, agentInfo.userID, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); // Swap the communications @@ -307,19 +307,19 @@ export class ConversionService { const userMembers = await this.communityRoleService.getUsersWithRole( subsubspace.community, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); const userLeads = await this.communityRoleService.getUsersWithRole( subsubspace.community, - CommunityRole.LEAD + CommunityRoleType.LEAD ); const orgMembers = await this.communityRoleService.getOrganizationsWithRole( subsubspace.community, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); const orgLeads = await this.communityRoleService.getOrganizationsWithRole( subsubspace.community, - CommunityRole.LEAD + CommunityRoleType.LEAD ); // Remove the contributors from old roles @@ -335,12 +335,12 @@ export class ConversionService { await this.communityRoleService.removeUserFromRole( subspace.community, agentInfo.userID, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); await this.communityRoleService.removeUserFromRole( subspace.community, agentInfo.userID, - CommunityRole.LEAD + CommunityRoleType.LEAD ); // Swap the communication @@ -528,28 +528,28 @@ export class ConversionService { await this.communityRoleService.removeUserFromRole( community, userMember.id, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); } for (const userLead of userLeads) { await this.communityRoleService.removeUserFromRole( community, userLead.id, - CommunityRole.LEAD + CommunityRoleType.LEAD ); } for (const orgMember of orgMembers) { await this.communityRoleService.removeOrganizationFromRole( community, orgMember.id, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); } for (const orgLead of orgLeads) { await this.communityRoleService.removeOrganizationFromRole( community, orgLead.id, - CommunityRole.LEAD + CommunityRoleType.LEAD ); } } @@ -565,21 +565,21 @@ export class ConversionService { await this.communityRoleService.assignUserToRole( community, userMember.id, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); } for (const userLead of userLeads) { await this.communityRoleService.assignUserToRole( community, userLead.id, - CommunityRole.LEAD + CommunityRoleType.LEAD ); } for (const orgMember of orgMembers) { await this.communityRoleService.assignOrganizationToRole( community, orgMember.id, - CommunityRole.MEMBER + CommunityRoleType.MEMBER ); } if (orgLeads) { @@ -587,7 +587,7 @@ export class ConversionService { await this.communityRoleService.assignOrganizationToRole( community, orgLead.id, - CommunityRole.LEAD + CommunityRoleType.LEAD ); } } diff --git a/src/services/api/roles/util/group.credentials.by.entity.ts b/src/services/api/roles/util/group.credentials.by.entity.ts index 8b519d0e07..a6d5da8c89 100644 --- a/src/services/api/roles/util/group.credentials.by.entity.ts +++ b/src/services/api/roles/util/group.credentials.by.entity.ts @@ -1,12 +1,12 @@ import { ICredential } from '@src/domain'; import { AuthorizationCredential } from '@common/enums'; import { OrganizationRole } from '@common/enums/organization.role'; -import { CommunityRole } from '@common/enums/community.role'; +import { CommunityRoleType } from '@common/enums/community.role'; import { CommunityRoleImplicit } from '@common/enums/community.role.implicit'; export type CredentialRole = | OrganizationRole - | CommunityRole + | CommunityRoleType | CommunityRoleImplicit; export type EntityCredentialType = 'spaces' | 'organizations' | 'groups'; @@ -77,9 +77,9 @@ const credentialTypeToRole = ( type: AuthorizationCredential ): CredentialRole => { const roleMap: Partial> = { - [AuthorizationCredential.SPACE_ADMIN]: CommunityRole.ADMIN, - [AuthorizationCredential.SPACE_LEAD]: CommunityRole.LEAD, - [AuthorizationCredential.SPACE_MEMBER]: CommunityRole.MEMBER, + [AuthorizationCredential.SPACE_ADMIN]: CommunityRoleType.ADMIN, + [AuthorizationCredential.SPACE_LEAD]: CommunityRoleType.LEAD, + [AuthorizationCredential.SPACE_MEMBER]: CommunityRoleType.MEMBER, [AuthorizationCredential.SPACE_SUBSPACE_ADMIN]: CommunityRoleImplicit.SUBSPACE_ADMIN, @@ -88,7 +88,7 @@ const credentialTypeToRole = ( OrganizationRole.ASSOCIATE, [AuthorizationCredential.ORGANIZATION_OWNER]: OrganizationRole.OWNER, - [AuthorizationCredential.USER_GROUP_MEMBER]: CommunityRole.MEMBER, // hack for now; not used + [AuthorizationCredential.USER_GROUP_MEMBER]: CommunityRoleType.MEMBER, // hack for now; not used }; const role = roleMap[type]; From 01ac15088a56c531378a9d7ba1dd5990a402d93b Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Sun, 15 Sep 2024 17:11:25 +0100 Subject: [PATCH 02/78] refactor community functionality to use RoleManager module --- .../dto/role.manager.dto.create.ts | 2 +- .../role-manager/role.manager.module.ts | 2 + .../role.manager.resolver.mutations.ts | 6 +- .../role-manager/role.manager.service.ts | 21 + .../callout.contribution.module.ts | 4 +- ...lout.contribution.service.authorization.ts | 20 +- .../collaboration/callout/callout.module.ts | 4 +- .../callout/callout.resolver.mutations.ts | 6 +- .../callout/callout.service.authorization.ts | 6 +- .../collaboration/collaboration.module.ts | 4 +- .../collaboration.resolver.mutations.ts | 4 +- .../collaboration.service.authorization.ts | 61 ++- .../collaboration/collaboration.service.ts | 12 +- src/domain/collaboration/post/post.module.ts | 4 +- .../post/post.service.authorization.ts | 22 +- .../room/room.service.mentions.ts | 2 +- .../application/application.entity.ts | 8 - .../application/application.interface.ts | 4 +- .../application/application.service.ts | 2 +- .../application/dto/application.dto.create.ts | 2 +- .../community.policy.definition.ts | 7 - .../community.policy.entity.ts | 25 - .../community.policy.interface.ts | 9 - .../community.policy.module.ts | 12 - .../community.policy.resolver.fields.ts | 43 -- .../community.policy.role.interface.ts | 41 -- .../community-policy/community.policy.role.ts | 22 - .../community.policy.service.ts | 192 -------- ....lifecycle.application.options.provider.ts | 10 +- ...e.lifecycle.invitation.options.provider.ts | 18 +- .../community-role/community.role.module.ts | 4 + .../community.role.resolver.mutations.ts | 99 ++-- .../community.role.service.events.ts | 2 +- .../community-role/community.role.service.ts | 441 ++++++++++-------- .../dto/community.role.dto.apply.ts | 2 +- .../community.role.dto.invite.contributor.ts | 2 +- .../dto/community.role.dto.join.ts | 2 +- ....role.dto.platform.invitation.community.ts | 2 +- ...unity.role.dto.role.assign.organization.ts | 2 +- .../community.role.dto.role.assign.user.ts | 2 +- .../community.role.dto.role.assign.virtual.ts | 2 +- ...unity.role.dto.role.remove.organization.ts | 2 +- .../community.role.dto.role.remove.user.ts | 2 +- .../community.role.dto.role.remove.virtual.ts | 2 +- .../community/community/community.entity.ts | 40 +- .../community/community.interface.ts | 15 +- .../community/community/community.module.ts | 12 +- .../community/community.resolver.fields.ts | 21 +- .../community/community.resolver.mutations.ts | 69 --- .../community.service.authorization.ts | 113 ++--- .../community/community/community.service.ts | 160 ++----- .../community/dto/community.dto.create.ts | 7 +- .../invitation/dto/invitation.dto.create.ts | 2 +- .../community/invitation/invitation.entity.ts | 8 - .../invitation/invitation.interface.ts | 4 +- .../invitation/invitation.service.ts | 8 +- .../definitions/space.community.policy.ts | 42 -- ...=> space.role.manager.application.form.ts} | 0 .../definitions/space.role.manager.roles.ts | 81 ++++ .../definitions/subspace.community.policy.ts | 41 -- ...subspace.role.manager.application.form.ts} | 0 .../subspace.role.manager.roles.ts | 81 ++++ .../space.defaults/space.defaults.service.ts | 22 +- src/domain/space/space/index.ts | 4 +- src/domain/space/space/space.module.ts | 4 +- .../space/space.service.authorization.ts | 104 ++--- src/domain/space/space/space.service.ts | 46 +- src/domain/timeline/event/event.module.ts | 2 - .../dto/platform.invitation.dto.create.ts | 2 +- .../invitation/platform.invitation.entity.ts | 9 - .../platform.invitation.interface.ts | 4 +- .../invitation/platform.invitation.service.ts | 2 +- .../notification.payload.builder.ts | 2 +- .../api/conversion/conversion.service.ts | 91 ++-- src/services/api/me/me.service.ts | 12 +- .../api/registration/registration.service.ts | 8 +- src/services/api/roles/roles.service.ts | 54 ++- .../community.resolver.service.ts | 33 +- .../infrastructure/naming/naming.service.ts | 26 +- test/mocks/community.resolver.service.mock.ts | 4 +- 80 files changed, 887 insertions(+), 1387 deletions(-) delete mode 100644 src/domain/community/community-policy/community.policy.definition.ts delete mode 100644 src/domain/community/community-policy/community.policy.entity.ts delete mode 100644 src/domain/community/community-policy/community.policy.interface.ts delete mode 100644 src/domain/community/community-policy/community.policy.module.ts delete mode 100644 src/domain/community/community-policy/community.policy.resolver.fields.ts delete mode 100644 src/domain/community/community-policy/community.policy.role.interface.ts delete mode 100644 src/domain/community/community-policy/community.policy.role.ts delete mode 100644 src/domain/community/community-policy/community.policy.service.ts delete mode 100644 src/domain/space/space.defaults/definitions/space.community.policy.ts rename src/domain/space/space.defaults/definitions/{space.community.application.form.ts => space.role.manager.application.form.ts} (100%) create mode 100644 src/domain/space/space.defaults/definitions/space.role.manager.roles.ts delete mode 100644 src/domain/space/space.defaults/definitions/subspace.community.policy.ts rename src/domain/space/space.defaults/definitions/{subspace.community.application.form.ts => subspace.role.manager.application.form.ts} (100%) create mode 100644 src/domain/space/space.defaults/definitions/subspace.role.manager.roles.ts diff --git a/src/domain/access/role-manager/dto/role.manager.dto.create.ts b/src/domain/access/role-manager/dto/role.manager.dto.create.ts index 4d2bc7b142..d3d8ea9a6a 100644 --- a/src/domain/access/role-manager/dto/role.manager.dto.create.ts +++ b/src/domain/access/role-manager/dto/role.manager.dto.create.ts @@ -3,7 +3,7 @@ import { CreateFormInput } from '@domain/common/form/dto/form.dto.create'; import { IRoleManager } from '../role.manager.interface'; export class CreateRoleManagerInput { - parentRoleManager!: IRoleManager; + parentRoleManager?: IRoleManager; roles!: CreateRoleInput[]; applicationForm!: CreateFormInput; } diff --git a/src/domain/access/role-manager/role.manager.module.ts b/src/domain/access/role-manager/role.manager.module.ts index 4b3222d851..c1d3dad817 100644 --- a/src/domain/access/role-manager/role.manager.module.ts +++ b/src/domain/access/role-manager/role.manager.module.ts @@ -17,6 +17,7 @@ import { PlatformInvitationModule } from '@platform/invitation/platform.invitati import { InvitationModule } from '@domain/community/invitation/invitation.module'; import { ApplicationModule } from '@domain/community/application/application.module'; import { VirtualContributorModule } from '@domain/community/virtual-contributor/virtual.contributor.module'; +import { RoleModule } from '../role/role.module'; @Module({ imports: [ @@ -28,6 +29,7 @@ import { VirtualContributorModule } from '@domain/community/virtual-contributor/ AgentModule, StorageAggregatorResolverModule, FormModule, + RoleModule, InvitationModule, ApplicationModule, PlatformInvitationModule, diff --git a/src/domain/access/role-manager/role.manager.resolver.mutations.ts b/src/domain/access/role-manager/role.manager.resolver.mutations.ts index 4214704341..f4ed86314f 100644 --- a/src/domain/access/role-manager/role.manager.resolver.mutations.ts +++ b/src/domain/access/role-manager/role.manager.resolver.mutations.ts @@ -6,18 +6,14 @@ import { GraphqlGuard } from '@core/authorization'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { AuthorizationPrivilege } from '@common/enums'; import { AuthorizationService } from '@core/authorization/authorization.service'; -import { AgentService } from '@domain/agent/agent/agent.service'; import { UpdateRoleManagerApplicationFormInput } from './dto/role.manager.dto.update.application.form'; -import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service'; import { IRoleManager } from './role.manager.interface'; @Resolver() export class RoleManagerResolverMutations { constructor( private authorizationService: AuthorizationService, - private authorizationPolicyService: AuthorizationPolicyService, - private roleManagerService: RoleManagerService, - private agentService: AgentService + private roleManagerService: RoleManagerService ) {} @UseGuards(GraphqlGuard) diff --git a/src/domain/access/role-manager/role.manager.service.ts b/src/domain/access/role-manager/role.manager.service.ts index 7afd953df9..61b4539872 100644 --- a/src/domain/access/role-manager/role.manager.service.ts +++ b/src/domain/access/role-manager/role.manager.service.ts @@ -200,6 +200,27 @@ export class RoleManagerService { return roleManager; } + public async getPeerRoleManagers( + parentRoleManager: IRoleManager, + childRoleManager: IRoleManager + ): Promise { + const peerRoleManagers: IRoleManager[] = + await this.roleManagerRepository.find({ + where: { + parentRoleManager: { + id: parentRoleManager.id, + }, + }, + }); + const result: IRoleManager[] = []; + for (const roleManager of peerRoleManagers) { + if (roleManager && !(roleManager.id === childRoleManager.id)) { + result.push(roleManager); + } + } + return result; + } + public inheritParentCredentials(roleManager: IRoleManager): IRoleManager { const roleManagerParent = roleManager.parentRoleManager; if (!roleManagerParent) { diff --git a/src/domain/collaboration/callout-contribution/callout.contribution.module.ts b/src/domain/collaboration/callout-contribution/callout.contribution.module.ts index b3ed76c0d9..7ce4caf59e 100644 --- a/src/domain/collaboration/callout-contribution/callout.contribution.module.ts +++ b/src/domain/collaboration/callout-contribution/callout.contribution.module.ts @@ -10,8 +10,8 @@ import { WhiteboardModule } from '@domain/common/whiteboard/whiteboard.module'; import { NamingModule } from '@services/infrastructure/naming/naming.module'; import { PostModule } from '../post/post.module'; import { ContributorLookupModule } from '@services/infrastructure/contributor-lookup/contributor.lookup.module'; -import { CommunityPolicyModule } from '@domain/community/community-policy/community.policy.module'; import { LinkModule } from '../link/link.module'; +import { RoleManagerModule } from '@domain/access/role-manager/role.manager.module'; @Module({ imports: [ @@ -22,7 +22,7 @@ import { LinkModule } from '../link/link.module'; NamingModule, LinkModule, ContributorLookupModule, - CommunityPolicyModule, + RoleManagerModule, TypeOrmModule.forFeature([CalloutContribution]), ], providers: [ diff --git a/src/domain/collaboration/callout-contribution/callout.contribution.service.authorization.ts b/src/domain/collaboration/callout-contribution/callout.contribution.service.authorization.ts index 459201f47e..e42484000b 100644 --- a/src/domain/collaboration/callout-contribution/callout.contribution.service.authorization.ts +++ b/src/domain/collaboration/callout-contribution/callout.contribution.service.authorization.ts @@ -5,10 +5,8 @@ import { CalloutContributionService } from './callout.contribution.service'; import { ICalloutContribution } from './callout.contribution.interface'; import { WhiteboardAuthorizationService } from '@domain/common/whiteboard'; import { PostAuthorizationService } from '../post/post.service.authorization'; -import { ICommunityPolicy } from '@domain/community/community-policy/community.policy.interface'; import { EntityNotInitializedException } from '@common/exceptions'; import { LogContext } from '@common/enums/logging.context'; -import { CommunityPolicyService } from '@domain/community/community-policy/community.policy.service'; import { IAuthorizationPolicyRuleCredential } from '@core/authorization/authorization.policy.rule.credential.interface'; import { CommunityRoleType } from '@common/enums/community.role'; import { AuthorizationCredential, AuthorizationPrivilege } from '@common/enums'; @@ -20,6 +18,8 @@ import { import { LinkAuthorizationService } from '../link/link.service.authorization'; import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; +import { IRoleManager } from '@domain/access/role-manager/role.manager.interface'; +import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; @Injectable() export class CalloutContributionAuthorizationService { @@ -29,13 +29,13 @@ export class CalloutContributionAuthorizationService { private postAuthorizationService: PostAuthorizationService, private whiteboardAuthorizationService: WhiteboardAuthorizationService, private linkAuthorizationService: LinkAuthorizationService, - private communityPolicyService: CommunityPolicyService + private roleManagerService: RoleManagerService ) {} public async applyAuthorizationPolicy( contributionID: string, parentAuthorization: IAuthorizationPolicy | undefined, - communityPolicy?: ICommunityPolicy, + roleManager?: IRoleManager, spaceSettings?: ISpaceSettings ): Promise { const contribution = @@ -111,7 +111,7 @@ export class CalloutContributionAuthorizationService { // Extend to give the user creating the contribution more rights contribution.authorization = this.appendCredentialRules( contribution, - communityPolicy, + roleManager, spaceSettings ); updatedAuthorizations.push(contribution.authorization); @@ -121,7 +121,7 @@ export class CalloutContributionAuthorizationService { await this.postAuthorizationService.applyAuthorizationPolicy( contribution.post, contribution.authorization, - communityPolicy, + roleManager, spaceSettings ); updatedAuthorizations.push(...postAuthorizations); @@ -150,7 +150,7 @@ export class CalloutContributionAuthorizationService { private appendCredentialRules( contribution: ICalloutContribution, - communityPolicy?: ICommunityPolicy, + communityPolicy?: IRoleManager, spaceSettings?: ISpaceSettings ): IAuthorizationPolicy { const authorization = contribution.authorization; @@ -204,10 +204,10 @@ export class CalloutContributionAuthorizationService { ]; if (communityPolicy && spaceSettings) { const roleCredentials = - this.communityPolicyService.getCredentialsForRoleWithParents( + this.roleManagerService.getCredentialsForRoleWithParents( communityPolicy, - spaceSettings, - CommunityRoleType.ADMIN + CommunityRoleType.ADMIN, + spaceSettings ); credentials.push(...roleCredentials); } diff --git a/src/domain/collaboration/callout/callout.module.ts b/src/domain/collaboration/callout/callout.module.ts index 0d43f78d69..73757a17f6 100644 --- a/src/domain/collaboration/callout/callout.module.ts +++ b/src/domain/collaboration/callout/callout.module.ts @@ -12,7 +12,6 @@ import { NamingModule } from '@services/infrastructure/naming/naming.module'; import { ActivityAdapterModule } from '@services/adapters/activity-adapter/activity.adapter.module'; import { NotificationAdapterModule } from '@services/adapters/notification-adapter/notification.adapter.module'; import { EntityResolverModule } from '@services/infrastructure/entity-resolver/entity.resolver.module'; -import { CommunityPolicyModule } from '@domain/community/community-policy/community.policy.module'; import { MessagingModule } from '@domain/communication/messaging/messaging.module'; import { ContributionReporterModule } from '@services/external/elasticsearch/contribution-reporter'; import { RoomModule } from '@domain/communication/room/room.module'; @@ -24,6 +23,7 @@ import { CalloutContributionDefaultsModule } from '../callout-contribution-defau import { CalloutContributionPolicyModule } from '../callout-contribution-policy/callout.contribution.policy.module'; import { CalloutContributionModule } from '../callout-contribution/callout.contribution.module'; import { PostModule } from '../post/post.module'; +import { RoleManagerModule } from '@domain/access/role-manager/role.manager.module'; @Module({ imports: [ @@ -34,7 +34,7 @@ import { PostModule } from '../post/post.module'; AuthorizationPolicyModule, AuthorizationModule, RoomModule, - CommunityPolicyModule, + RoleManagerModule, EntityResolverModule, ContributorLookupModule, NamingModule, diff --git a/src/domain/collaboration/callout/callout.resolver.mutations.ts b/src/domain/collaboration/callout/callout.resolver.mutations.ts index afffda858f..cb83e91b18 100644 --- a/src/domain/collaboration/callout/callout.resolver.mutations.ts +++ b/src/domain/collaboration/callout/callout.resolver.mutations.ts @@ -228,10 +228,8 @@ export class CalloutResolverMutations { agentInfo.userID ); - const { communityPolicy, spaceSettings } = - await this.namingService.getCommunityPolicyAndSettingsForCallout( - callout.id - ); + const { roleManager: communityPolicy, spaceSettings } = + await this.namingService.getRoleManagerAndSettingsForCallout(callout.id); contribution = await this.calloutContributionService.save(contribution); // Ensure settings are available const updatedAuthorizations = diff --git a/src/domain/collaboration/callout/callout.service.authorization.ts b/src/domain/collaboration/callout/callout.service.authorization.ts index 325aeda27e..e7313f4be4 100644 --- a/src/domain/collaboration/callout/callout.service.authorization.ts +++ b/src/domain/collaboration/callout/callout.service.authorization.ts @@ -10,7 +10,6 @@ import { } from '@common/enums'; import { EntityNotInitializedException } from '@common/exceptions'; import { AuthorizationPolicyRulePrivilege } from '@core/authorization/authorization.policy.rule.privilege'; -import { ICommunityPolicy } from '@domain/community/community-policy/community.policy.interface'; import { CalloutType } from '@common/enums/callout.type'; import { IAuthorizationPolicyRuleCredential } from '@core/authorization/authorization.policy.rule.credential.interface'; import { @@ -23,6 +22,7 @@ import { RoomAuthorizationService } from '@domain/communication/room/room.servic import { CalloutFramingAuthorizationService } from '../callout-framing/callout.framing.service.authorization'; import { CalloutContributionAuthorizationService } from '../callout-contribution/callout.contribution.service.authorization'; import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; +import { IRoleManager } from '@domain/access/role-manager/role.manager.interface'; @Injectable() export class CalloutAuthorizationService { @@ -37,7 +37,7 @@ export class CalloutAuthorizationService { public async applyAuthorizationPolicy( calloutInput: ICallout, parentAuthorization: IAuthorizationPolicy | undefined, - communityPolicy?: ICommunityPolicy, + roleManager?: IRoleManager, spaceSettings?: ISpaceSettings ): Promise { const callout = await this.calloutService.getCalloutOrFail( @@ -90,7 +90,7 @@ export class CalloutAuthorizationService { await this.contributionAuthorizationService.applyAuthorizationPolicy( contribution.id, callout.authorization, - communityPolicy, + roleManager, spaceSettings ); updatedAuthorizations.push(...updatedContributionAuthorizations); diff --git a/src/domain/collaboration/collaboration/collaboration.module.ts b/src/domain/collaboration/collaboration/collaboration.module.ts index bd1a6fc986..c5e972c918 100644 --- a/src/domain/collaboration/collaboration/collaboration.module.ts +++ b/src/domain/collaboration/collaboration/collaboration.module.ts @@ -13,7 +13,6 @@ import { WhiteboardModule } from '@domain/common/whiteboard/whiteboard.module'; import { PostModule } from '../post/post.module'; import { ActivityAdapterModule } from '@services/adapters/activity-adapter/activity.adapter.module'; import { NotificationAdapterModule } from '@services/adapters/notification-adapter/notification.adapter.module'; -import { CommunityPolicyModule } from '@domain/community/community-policy/community.policy.module'; import { ContributionReporterModule } from '@services/external/elasticsearch/contribution-reporter'; import { EntityResolverModule } from '@services/infrastructure/entity-resolver/entity.resolver.module'; import { TagsetTemplateSetModule } from '@domain/common/tagset-template-set/tagset.template.set.module'; @@ -23,6 +22,7 @@ import { InnovationFlowModule } from '../innovation-flow/innovation.flow.module' import { SpaceDefaultsModule } from '@domain/space/space.defaults/space.defaults.module'; import { CalloutGroupsModule } from '../callout-groups/callout.group.module'; import { LicenseEngineModule } from '@core/license-engine/license.engine.module'; +import { RoleManagerModule } from '@domain/access/role-manager/role.manager.module'; @Module({ imports: [ @@ -32,7 +32,7 @@ import { LicenseEngineModule } from '@core/license-engine/license.engine.module' AuthorizationPolicyModule, AuthorizationModule, CalloutModule, - CommunityPolicyModule, + RoleManagerModule, NamingModule, EntityResolverModule, StorageAggregatorResolverModule, diff --git a/src/domain/collaboration/collaboration/collaboration.resolver.mutations.ts b/src/domain/collaboration/collaboration/collaboration.resolver.mutations.ts index e18b3ddfc6..ce37e93ef2 100644 --- a/src/domain/collaboration/collaboration/collaboration.resolver.mutations.ts +++ b/src/domain/collaboration/collaboration/collaboration.resolver.mutations.ts @@ -85,8 +85,8 @@ export class CollaborationResolverMutations { agentInfo.userID ); - const { communityPolicy, spaceSettings } = - await this.namingService.getCommunityPolicyAndSettingsForCollaboration( + const { roleManager: communityPolicy, spaceSettings } = + await this.namingService.getRoleManagerAndSettingsForCollaboration( collaboration.id ); // callout needs to be saved to apply the authorization policy diff --git a/src/domain/collaboration/collaboration/collaboration.service.authorization.ts b/src/domain/collaboration/collaboration/collaboration.service.authorization.ts index 1aa7e9a346..c9a297ce56 100644 --- a/src/domain/collaboration/collaboration/collaboration.service.authorization.ts +++ b/src/domain/collaboration/collaboration/collaboration.service.authorization.ts @@ -9,10 +9,8 @@ import { CollaborationService } from '@domain/collaboration/collaboration/collab import { ICollaboration } from '@domain/collaboration/collaboration/collaboration.interface'; import { CalloutAuthorizationService } from '@domain/collaboration/callout/callout.service.authorization'; import { AuthorizationCredential } from '@common/enums'; -import { ICommunityPolicy } from '@domain/community/community-policy/community.policy.interface'; import { IAuthorizationPolicyRuleCredential } from '@core/authorization/authorization.policy.rule.credential.interface'; import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; -import { CommunityPolicyService } from '@domain/community/community-policy/community.policy.service'; import { CREDENTIAL_RULE_COLLABORATION_CONTRIBUTORS, POLICY_RULE_COLLABORATION_CREATE, @@ -29,13 +27,15 @@ import { LicenseEngineService } from '@core/license-engine/license.engine.servic import { LicensePrivilege } from '@common/enums/license.privilege'; import { IAgent } from '@domain/agent/agent/agent.interface'; import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; +import { IRoleManager } from '@domain/access/role-manager'; +import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; @Injectable() export class CollaborationAuthorizationService { constructor( private licenseEngineService: LicenseEngineService, private collaborationService: CollaborationService, - private communityPolicyService: CommunityPolicyService, + private roleManagerService: RoleManagerService, private authorizationPolicyService: AuthorizationPolicyService, private timelineAuthorizationService: TimelineAuthorizationService, private calloutAuthorizationService: CalloutAuthorizationService, @@ -45,7 +45,7 @@ export class CollaborationAuthorizationService { public async applyAuthorizationPolicy( collaborationInput: ICollaboration, parentAuthorization: IAuthorizationPolicy, - communityPolicy?: ICommunityPolicy, + roleManager?: IRoleManager, spaceSettings?: ISpaceSettings, spaceAgent?: IAgent ): Promise { @@ -82,14 +82,14 @@ export class CollaborationAuthorizationService { collaboration.authorization = await this.appendCredentialRules( collaboration.authorization, - communityPolicy, + roleManager, spaceSettings, spaceAgent ); - if (communityPolicy && spaceSettings && spaceAgent) { + if (roleManager && spaceSettings && spaceAgent) { collaboration.authorization = this.appendCredentialRulesForContributors( collaboration.authorization, - communityPolicy, + roleManager, spaceSettings ); @@ -104,7 +104,7 @@ export class CollaborationAuthorizationService { const childUpdatedAuthorizations = await this.propagateAuthorizationToChildEntities( collaboration, - communityPolicy, + roleManager, spaceSettings ); updatedAuthorizations.push(...childUpdatedAuthorizations); @@ -114,7 +114,7 @@ export class CollaborationAuthorizationService { private async propagateAuthorizationToChildEntities( collaboration: ICollaboration, - communityPolicy?: ICommunityPolicy, + roleManager?: IRoleManager, spaceSettings?: ISpaceSettings ): Promise { if ( @@ -135,7 +135,7 @@ export class CollaborationAuthorizationService { await this.calloutAuthorizationService.applyAuthorizationPolicy( callout, collaboration.authorization, - communityPolicy, + roleManager, spaceSettings ); updatedAuthorizations.push(...updatedCalloutAuthorizations); @@ -147,11 +147,11 @@ export class CollaborationAuthorizationService { collaboration.authorization ); - if (communityPolicy && spaceSettings) { + if (roleManager && spaceSettings) { const extendedAuthorizationContributors = this.appendCredentialRulesForContributors( clonedAuthorization, - communityPolicy, + roleManager, spaceSettings ); const timelineAuthorizations = @@ -173,23 +173,22 @@ export class CollaborationAuthorizationService { } private getContributorCredentials( - policy: ICommunityPolicy, + roleManager: IRoleManager, spaceSettings: ISpaceSettings ): ICredentialDefinition[] { // add challenge members - let contributorCriterias = - this.communityPolicyService.getCredentialsForRole( - policy, - spaceSettings, - CommunityRoleType.MEMBER - ); + let contributorCriterias = this.roleManagerService.getCredentialsForRole( + roleManager, + CommunityRoleType.MEMBER, + spaceSettings + ); // optionally add space members if (spaceSettings.collaboration.inheritMembershipRights) { contributorCriterias = - this.communityPolicyService.getCredentialsForRoleWithParents( - policy, - spaceSettings, - CommunityRoleType.MEMBER + this.roleManagerService.getCredentialsForRoleWithParents( + roleManager, + CommunityRoleType.MEMBER, + spaceSettings ); } @@ -202,14 +201,14 @@ export class CollaborationAuthorizationService { private async appendCredentialRules( authorization: IAuthorizationPolicy | undefined, - policy?: ICommunityPolicy, + roleManager?: IRoleManager, spaceSettings?: ISpaceSettings, spaceAgent?: IAgent ): Promise { if (!authorization) throw new EntityNotInitializedException( `Authorization definition not found for Context: ${JSON.stringify( - policy + roleManager )}`, LogContext.COLLABORATION ); @@ -217,7 +216,7 @@ export class CollaborationAuthorizationService { const newRules: IAuthorizationPolicyRuleCredential[] = []; // For templates these will not be available - if (!policy || !spaceSettings || !spaceAgent) { + if (!roleManager || !spaceSettings || !spaceAgent) { return authorization; } const saveAsTemplateEnabled = @@ -226,10 +225,10 @@ export class CollaborationAuthorizationService { spaceAgent ); if (saveAsTemplateEnabled) { - const adminCriterias = this.communityPolicyService.getCredentialsForRole( - policy, - spaceSettings, - CommunityRoleType.ADMIN + const adminCriterias = this.roleManagerService.getCredentialsForRole( + roleManager, + CommunityRoleType.ADMIN, + spaceSettings ); adminCriterias.push({ type: AuthorizationCredential.GLOBAL_ADMIN, @@ -254,7 +253,7 @@ export class CollaborationAuthorizationService { public appendCredentialRulesForContributors( authorization: IAuthorizationPolicy | undefined, - policy: ICommunityPolicy, + policy: IRoleManager, spaceSettings: ISpaceSettings ): IAuthorizationPolicy { if (!authorization) diff --git a/src/domain/collaboration/collaboration/collaboration.service.ts b/src/domain/collaboration/collaboration/collaboration.service.ts index 8771ce5b4f..cdb0dcc0c7 100644 --- a/src/domain/collaboration/collaboration/collaboration.service.ts +++ b/src/domain/collaboration/collaboration/collaboration.service.ts @@ -26,7 +26,6 @@ import { CreateCalloutOnCollaborationInput } from '@domain/collaboration/collabo import { CalloutVisibility } from '@common/enums/callout.visibility'; import { limitAndShuffle } from '@common/utils/limitAndShuffle'; import { UUID_LENGTH } from '@common/constants/entity.field.length.constants'; -import { ICommunityPolicy } from '@domain/community/community-policy/community.policy.interface'; import { CollaborationArgsCallouts } from './dto/collaboration.args.callouts'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { AuthorizationService } from '@core/authorization/authorization.service'; @@ -54,6 +53,7 @@ import { SpaceLevel } from '@common/enums/space.level'; import { Callout } from '@domain/collaboration/callout'; import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type'; import { CreateInnovationFlowInput } from '../innovation-flow/dto/innovation.flow.dto.create'; +import { IRoleManager } from '@domain/access/role-manager'; @Injectable() export class CollaborationService { @@ -690,14 +690,12 @@ export class CollaborationService { return result.whiteboardsCount; } - public async getCommunityPolicy( - collaborationID: string - ): Promise { - const { communityPolicy } = - await this.namingService.getCommunityPolicyAndSettingsForCollaboration( + public async getRoleManager(collaborationID: string): Promise { + const { roleManager } = + await this.namingService.getRoleManagerAndSettingsForCollaboration( collaborationID ); - return communityPolicy; + return roleManager; } public async updateCalloutsSortOrder( diff --git a/src/domain/collaboration/post/post.module.ts b/src/domain/collaboration/post/post.module.ts index 1c8c23b833..6838734c87 100644 --- a/src/domain/collaboration/post/post.module.ts +++ b/src/domain/collaboration/post/post.module.ts @@ -8,17 +8,17 @@ import { PostResolverMutations } from './post.resolver.mutations'; import { PostService } from './post.service'; import { PostResolverFields } from './post.resolver.fields'; import { PostAuthorizationService } from './post.service.authorization'; -import { CommunityPolicyModule } from '@domain/community/community-policy/community.policy.module'; import { ProfileModule } from '@domain/common/profile/profile.module'; import { RoomModule } from '@domain/communication/room/room.module'; import { ContributorLookupModule } from '@services/infrastructure/contributor-lookup/contributor.lookup.module'; +import { RoleManagerModule } from '@domain/access/role-manager/role.manager.module'; @Module({ imports: [ AuthorizationPolicyModule, AuthorizationModule, RoomModule, - CommunityPolicyModule, + RoleManagerModule, VisualModule, ContributorLookupModule, ProfileModule, diff --git a/src/domain/collaboration/post/post.service.authorization.ts b/src/domain/collaboration/post/post.service.authorization.ts index 82500d66a9..e4e1b22c0e 100644 --- a/src/domain/collaboration/post/post.service.authorization.ts +++ b/src/domain/collaboration/post/post.service.authorization.ts @@ -8,8 +8,6 @@ import { AuthorizationPrivilege, LogContext, } from '@common/enums'; -import { ICommunityPolicy } from '@domain/community/community-policy/community.policy.interface'; -import { CommunityPolicyService } from '@domain/community/community-policy/community.policy.service'; import { IAuthorizationPolicyRuleCredential } from '@core/authorization/authorization.policy.rule.credential.interface'; import { CREDENTIAL_RULE_POST_CREATED_BY, @@ -21,20 +19,22 @@ import { CommunityRoleType } from '@common/enums/community.role'; import { RelationshipNotFoundException } from '@common/exceptions/relationship.not.found.exception'; import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; +import { IRoleManager } from '@domain/access/role-manager/role.manager.interface'; +import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; @Injectable() export class PostAuthorizationService { constructor( private authorizationPolicyService: AuthorizationPolicyService, private roomAuthorizationService: RoomAuthorizationService, - private communityPolicyService: CommunityPolicyService, + private roleManagerService: RoleManagerService, private profileAuthorizationService: ProfileAuthorizationService ) {} async applyAuthorizationPolicy( post: IPost, parentAuthorization: IAuthorizationPolicy | undefined, - communityPolicy?: ICommunityPolicy, + roleManager?: IRoleManager, spaceSettings?: ISpaceSettings ): Promise { if (!post.profile) { @@ -73,7 +73,7 @@ export class PostAuthorizationService { // Extend to give the user creating the post more rights post.authorization = this.appendCredentialRules( post, - communityPolicy, + roleManager, spaceSettings ); updatedAuthorizations.push(post.authorization); @@ -91,7 +91,7 @@ export class PostAuthorizationService { private appendCredentialRules( post: IPost, - communityPolicy?: ICommunityPolicy, + roleManager?: IRoleManager, spaceSettings?: ISpaceSettings ): IAuthorizationPolicy { const authorization = post.authorization; @@ -124,12 +124,12 @@ export class PostAuthorizationService { }, ]; - if (communityPolicy && spaceSettings) { + if (roleManager && spaceSettings) { const roleCredentials = - this.communityPolicyService.getCredentialsForRoleWithParents( - communityPolicy, - spaceSettings, - CommunityRoleType.ADMIN + this.roleManagerService.getCredentialsForRoleWithParents( + roleManager, + CommunityRoleType.ADMIN, + spaceSettings ); credentials.push(...roleCredentials); } diff --git a/src/domain/communication/room/room.service.mentions.ts b/src/domain/communication/room/room.service.mentions.ts index 0a75f8e21b..077bbd01c9 100644 --- a/src/domain/communication/room/room.service.mentions.ts +++ b/src/domain/communication/room/room.service.mentions.ts @@ -51,7 +51,7 @@ export class RoomServiceMentions { // The ID of the actual community where the question is being asked const space = - await this.communityResolverService.getSpaceForCommunityOrFail( + await this.communityResolverService.getSpaceForRoleManagerOrFail( community.id ); return space.id; diff --git a/src/domain/community/application/application.entity.ts b/src/domain/community/application/application.entity.ts index 8a8f9ec696..7628c8e1da 100644 --- a/src/domain/community/application/application.entity.ts +++ b/src/domain/community/application/application.entity.ts @@ -6,7 +6,6 @@ import { ManyToOne, OneToOne, } from 'typeorm'; -import { Community } from '@domain/community/community/community.entity'; import { Lifecycle } from '@domain/common/lifecycle/lifecycle.entity'; import { IApplication } from './application.interface'; import { NVP } from '@domain/common/nvp/nvp.entity'; @@ -35,13 +34,6 @@ export class Application extends AuthorizableEntity implements IApplication { }) user?: User; - @ManyToOne(() => Community, community => community.applications, { - eager: false, - cascade: false, - onDelete: 'CASCADE', - }) - community?: Community; - @ManyToOne(() => RoleManager, manager => manager.applications, { eager: false, cascade: false, diff --git a/src/domain/community/application/application.interface.ts b/src/domain/community/application/application.interface.ts index 35395126e7..d508a63581 100644 --- a/src/domain/community/application/application.interface.ts +++ b/src/domain/community/application/application.interface.ts @@ -1,15 +1,15 @@ import { ILifecycle } from '@domain/common/lifecycle/lifecycle.interface'; -import { ICommunity } from '@domain/community/community/community.interface'; import { IUser } from '@domain/community/user/user.interface'; import { Field, ObjectType } from '@nestjs/graphql'; import { IAuthorizable } from '@domain/common/entity/authorizable-entity'; import { IQuestion } from '@domain/common/question/question.interface'; +import { IRoleManager } from '@domain/access/role-manager'; @ObjectType('Application') export abstract class IApplication extends IAuthorizable { user?: IUser; - community?: ICommunity; + roleManager?: IRoleManager; @Field(() => ILifecycle, { nullable: false }) lifecycle?: ILifecycle; diff --git a/src/domain/community/application/application.service.ts b/src/domain/community/application/application.service.ts index e763ebd565..b624798df4 100644 --- a/src/domain/community/application/application.service.ts +++ b/src/domain/community/application/application.service.ts @@ -153,7 +153,7 @@ export class ApplicationService { states: string[] = [] ): Promise { const findOpts: FindManyOptions = { - relations: { community: true }, + relations: { roleManager: true }, where: { user: { id: userID } }, }; diff --git a/src/domain/community/application/dto/application.dto.create.ts b/src/domain/community/application/dto/application.dto.create.ts index 100c0db056..37a7406b8c 100644 --- a/src/domain/community/application/dto/application.dto.create.ts +++ b/src/domain/community/application/dto/application.dto.create.ts @@ -2,7 +2,7 @@ import { CreateNVPInput } from '@domain/common/nvp'; export class CreateApplicationInput { userID!: string; - parentID!: string; + roleManagerID!: string; questions!: CreateNVPInput[]; } diff --git a/src/domain/community/community-policy/community.policy.definition.ts b/src/domain/community/community-policy/community.policy.definition.ts deleted file mode 100644 index c4089047b7..0000000000 --- a/src/domain/community/community-policy/community.policy.definition.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ICommunityRolePolicy } from './community.policy.role.interface'; - -export abstract class ICommunityPolicyDefinition { - member!: ICommunityRolePolicy; - lead!: ICommunityRolePolicy; - admin!: ICommunityRolePolicy; -} diff --git a/src/domain/community/community-policy/community.policy.entity.ts b/src/domain/community/community-policy/community.policy.entity.ts deleted file mode 100644 index ad7676e7f3..0000000000 --- a/src/domain/community/community-policy/community.policy.entity.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { BaseAlkemioEntity } from '@domain/common/entity/base-entity'; -import { Column, Entity } from 'typeorm'; -import { ICommunityPolicy } from './community.policy.interface'; - -@Entity() -export class CommunityPolicy - extends BaseAlkemioEntity - implements ICommunityPolicy -{ - @Column('text', { nullable: false }) - member!: string; - - @Column('text', { nullable: false }) - lead!: string; - - @Column('text', { nullable: false }) - admin!: string; - - constructor(member: string, lead: string, admin: string) { - super(); - this.member = member; - this.lead = lead; - this.admin = admin; - } -} diff --git a/src/domain/community/community-policy/community.policy.interface.ts b/src/domain/community/community-policy/community.policy.interface.ts deleted file mode 100644 index 3468600760..0000000000 --- a/src/domain/community/community-policy/community.policy.interface.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { IBaseAlkemio } from '@domain/common/entity/base-entity'; -import { ObjectType } from '@nestjs/graphql'; - -@ObjectType('CommunityPolicy') -export abstract class ICommunityPolicy extends IBaseAlkemio { - member!: string; - lead!: string; - admin!: string; -} diff --git a/src/domain/community/community-policy/community.policy.module.ts b/src/domain/community/community-policy/community.policy.module.ts deleted file mode 100644 index 22aa64fc1d..0000000000 --- a/src/domain/community/community-policy/community.policy.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { CommunityPolicy } from './community.policy.entity'; -import { CommunityPolicyResolverFields } from './community.policy.resolver.fields'; -import { CommunityPolicyService } from './community.policy.service'; - -@Module({ - imports: [TypeOrmModule.forFeature([CommunityPolicy])], - providers: [CommunityPolicyService, CommunityPolicyResolverFields], - exports: [CommunityPolicyService], -}) -export class CommunityPolicyModule {} diff --git a/src/domain/community/community-policy/community.policy.resolver.fields.ts b/src/domain/community/community-policy/community.policy.resolver.fields.ts deleted file mode 100644 index f409184340..0000000000 --- a/src/domain/community/community-policy/community.policy.resolver.fields.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Parent, ResolveField, Resolver } from '@nestjs/graphql'; -import { CommunityRoleType } from '@common/enums/community.role'; -import { ICommunityPolicy } from './community.policy.interface'; -import { CommunityPolicyService } from './community.policy.service'; -import { ICommunityRolePolicy } from './community.policy.role.interface'; - -@Resolver(() => ICommunityPolicy) -export class CommunityPolicyResolverFields { - constructor(private communityPolicyService: CommunityPolicyService) {} - - @ResolveField('member', () => ICommunityRolePolicy, { - nullable: false, - description: 'The role policy that defines the members for this Community.', - }) - member(@Parent() communityPolicy: ICommunityPolicy): ICommunityRolePolicy { - return this.communityPolicyService.getCommunityRolePolicy( - communityPolicy, - CommunityRoleType.MEMBER - ); - } - - @ResolveField('lead', () => ICommunityRolePolicy, { - nullable: false, - description: 'The role policy that defines the leads for this Community.', - }) - lead(@Parent() communityPolicy: ICommunityPolicy): ICommunityRolePolicy { - return this.communityPolicyService.getCommunityRolePolicy( - communityPolicy, - CommunityRoleType.LEAD - ); - } - - @ResolveField('admin', () => ICommunityRolePolicy, { - nullable: false, - description: 'The role policy that defines the Admins for this Community.', - }) - admin(@Parent() communityPolicy: ICommunityPolicy): ICommunityRolePolicy { - return this.communityPolicyService.getCommunityRolePolicy( - communityPolicy, - CommunityRoleType.ADMIN - ); - } -} diff --git a/src/domain/community/community-policy/community.policy.role.interface.ts b/src/domain/community/community-policy/community.policy.role.interface.ts deleted file mode 100644 index 474648c255..0000000000 --- a/src/domain/community/community-policy/community.policy.role.interface.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; -import { Field, ObjectType } from '@nestjs/graphql'; - -@ObjectType('CommunityRolePolicy') -export abstract class ICommunityRolePolicy { - @Field(() => Boolean, { - description: 'Is this role enabled for this Community', - }) - enabled!: boolean; - - @Field(() => ICredentialDefinition, { - description: 'The CredentialDefinition that is associated with this role', - }) - credential!: ICredentialDefinition; - - @Field(() => [ICredentialDefinition], { - description: - 'The CredentialDefinitions associated with this role in parent communities', - }) - parentCredentials!: ICredentialDefinition[]; - - @Field(() => Number, { - description: 'Minimum number of Users in this role', - }) - minUser!: number; - - @Field(() => Number, { - description: 'Maximum number of Users in this role', - }) - maxUser!: number; - - @Field(() => Number, { - description: 'Minimun number of Organizations in this role', - }) - minOrg!: number; - - @Field(() => Number, { - description: 'Maximum number of Organizations in this role', - }) - maxOrg!: number; -} diff --git a/src/domain/community/community-policy/community.policy.role.ts b/src/domain/community/community-policy/community.policy.role.ts deleted file mode 100644 index 8d8aaae29b..0000000000 --- a/src/domain/community/community-policy/community.policy.role.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { CredentialDefinition } from '@domain/agent/credential/credential.definition'; -import { ICommunityRolePolicy } from './community.policy.role.interface'; - -export class CommunityRolePolicy implements ICommunityRolePolicy { - credential: CredentialDefinition; - parentCredentials: CredentialDefinition[]; - minUser: number; - maxUser: number; - minOrg: number; - maxOrg: number; - enabled: boolean; - - constructor(credential: CredentialDefinition) { - this.credential = credential; - this.parentCredentials = []; - this.minOrg = -1; - this.maxOrg = -1; - this.minUser = -1; - this.maxUser = -1; - this.enabled = false; - } -} diff --git a/src/domain/community/community-policy/community.policy.service.ts b/src/domain/community/community-policy/community.policy.service.ts deleted file mode 100644 index c3a4333e01..0000000000 --- a/src/domain/community/community-policy/community.policy.service.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { CommunityRoleType } from '@common/enums/community.role'; -import { LogContext } from '@common/enums/logging.context'; -import { EntityNotInitializedException } from '@common/exceptions/entity.not.initialized.exception'; -import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; -import { Inject, Injectable, LoggerService } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; -import { Repository } from 'typeorm'; -import { CommunityPolicy } from './community.policy.entity'; -import { ICommunityPolicy } from './community.policy.interface'; -import { ICommunityRolePolicy } from './community.policy.role.interface'; -import { AuthorizationCredential } from '@common/enums/authorization.credential'; -import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; - -@Injectable() -export class CommunityPolicyService { - constructor( - @InjectRepository(CommunityPolicy) - private communityPolicyRepository: Repository, - @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService - ) {} - - public createCommunityPolicy( - member: ICommunityRolePolicy, - lead: ICommunityRolePolicy, - admin: ICommunityRolePolicy - ): ICommunityPolicy { - return new CommunityPolicy( - this.serializeRolePolicy(member), - this.serializeRolePolicy(lead), - this.serializeRolePolicy(admin) - ); - } - - public async removeCommunityPolicy( - communityPolicy: ICommunityPolicy - ): Promise { - await this.communityPolicyRepository.remove( - communityPolicy as CommunityPolicy - ); - return true; - } - - public getCommunityRolePolicy( - policy: ICommunityPolicy, - role: CommunityRoleType - ): ICommunityRolePolicy { - switch (role) { - case CommunityRoleType.MEMBER: - return this.deserializeRolePolicy(policy.member); - case CommunityRoleType.LEAD: - return this.deserializeRolePolicy(policy.lead); - case CommunityRoleType.ADMIN: - return this.deserializeRolePolicy(policy.admin); - default: - throw new EntityNotInitializedException( - `Unable to locate role '${role}' for community policy: ${policy.id}`, - LogContext.COMMUNITY - ); - } - } - - getDirectParentCredentialForRole( - policy: ICommunityPolicy, - role: CommunityRoleType - ): ICredentialDefinition | undefined { - const rolePolicy = this.getCommunityRolePolicy(policy, role); - - // First entry is the immediate parent - if (rolePolicy.parentCredentials.length === 0) { - return undefined; - } - const parentCommunityCredential = rolePolicy.parentCredentials[0]; - return parentCommunityCredential; - } - - public getParentCredentialsForRole( - policy: ICommunityPolicy, - role: CommunityRoleType - ): ICredentialDefinition[] { - const rolePolicy = this.getCommunityRolePolicy(policy, role); - - return rolePolicy.parentCredentials; - } - - public getCredentialsForRoleWithParents( - policy: ICommunityPolicy, - spaceSettings: ISpaceSettings, - role: CommunityRoleType - ): ICredentialDefinition[] { - const result = this.getCredentialsForRole(policy, spaceSettings, role); - return result.concat(this.getParentCredentialsForRole(policy, role)); - } - - public getCredentialsForRole( - policy: ICommunityPolicy, - spaceSettings: ISpaceSettings, - role: CommunityRoleType - ): ICredentialDefinition[] { - const result = [this.getCredentialForRole(policy, role)]; - if ( - role === CommunityRoleType.ADMIN && - spaceSettings.privacy.allowPlatformSupportAsAdmin - ) { - result.push({ - type: AuthorizationCredential.GLOBAL_SUPPORT, - resourceID: '', - }); - } - return result; - } - - public getCredentialForRole( - policy: ICommunityPolicy, - role: CommunityRoleType - ): ICredentialDefinition { - const rolePolicy = this.getCommunityRolePolicy(policy, role); - return rolePolicy.credential; - } - - // Update the Community policy to have the right resource ID - public updateCommunityPolicyResourceID( - communityPolicy: ICommunityPolicy, - resourceID: string - ): ICommunityPolicy { - const memberPolicy = this.deserializeRolePolicy(communityPolicy.member); - memberPolicy.credential.resourceID = resourceID; - communityPolicy.member = this.serializeRolePolicy(memberPolicy); - - const leadPolicy = this.deserializeRolePolicy(communityPolicy.lead); - leadPolicy.credential.resourceID = resourceID; - communityPolicy.lead = this.serializeRolePolicy(leadPolicy); - - const adminPolicy = this.deserializeRolePolicy(communityPolicy.admin); - adminPolicy.credential.resourceID = resourceID; - communityPolicy.admin = this.serializeRolePolicy(adminPolicy); - - return communityPolicy; - } - - public inheritParentCredentials( - communityPolicyParent: ICommunityPolicy, - communityPolicy: ICommunityPolicy - ): ICommunityPolicy { - const memberPolicy = this.inheritParentRoleCredentials( - communityPolicyParent.member, - communityPolicy.member - ); - const leadPolicy = this.inheritParentRoleCredentials( - communityPolicyParent.lead, - communityPolicy.lead - ); - const adminPolicy = this.inheritParentRoleCredentials( - communityPolicyParent.admin, - communityPolicy.admin - ); - - communityPolicy.member = this.serializeRolePolicy(memberPolicy); - communityPolicy.lead = this.serializeRolePolicy(leadPolicy); - communityPolicy.admin = this.serializeRolePolicy(adminPolicy); - - return communityPolicy; - } - - private save(policy: ICommunityPolicy): Promise { - return this.communityPolicyRepository.save(policy); - } - - private inheritParentRoleCredentials( - rolePolicyParentStr: string, - rolePolicyStr: string - ): ICommunityRolePolicy { - const rolePolicyParent: ICommunityRolePolicy = - this.deserializeRolePolicy(rolePolicyParentStr); - const rolePolicy: ICommunityRolePolicy = - this.deserializeRolePolicy(rolePolicyStr); - rolePolicy.parentCredentials?.push(rolePolicyParent.credential); - rolePolicyParent.parentCredentials?.forEach(c => - rolePolicy.parentCredentials?.push(c) - ); - - return rolePolicy; - } - - private deserializeRolePolicy(rolePolicyStr: string): ICommunityRolePolicy { - return JSON.parse(rolePolicyStr); - } - - private serializeRolePolicy(rolePolicy: ICommunityRolePolicy): string { - return JSON.stringify(rolePolicy); - } -} diff --git a/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts b/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts index 8e5c453133..e388355727 100644 --- a/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts +++ b/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts @@ -65,21 +65,21 @@ export class CommunityRoleApplicationLifecycleOptionsProvider { const application = await this.applicationService.getApplicationOrFail( event.parentID, { - relations: { community: true, user: true }, + relations: { roleManager: true, user: true }, } ); const userID = application.user?.id; - const community = application.community; - if (!userID || !community) + const roleManager = application.roleManager; + if (!userID || !roleManager) throw new EntityNotInitializedException( `Lifecycle not initialized on Application: ${application.id}`, LogContext.COMMUNITY ); await this.communityRoleService.assignUserToRole( - community, - userID, + roleManager, CommunityRoleType.MEMBER, + userID, event.agentInfo, true ); diff --git a/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts b/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts index 56f4b6f6c8..c255adb757 100644 --- a/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts +++ b/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts @@ -88,15 +88,15 @@ export class CommunityRoleInvitationLifecycleOptionsProvider { event.parentID, { relations: { - community: { - parentCommunity: true, + roleManager: { + parentRoleManager: true, }, }, } ); const contributorID = invitation.invitedContributor; - const community = invitation.community; - if (!contributorID || !community) { + const roleManager = invitation.roleManager; + if (!contributorID || !roleManager) { throw new EntityNotInitializedException( `Lifecycle not initialized on Invitation: ${invitation.id}`, LogContext.COMMUNITY @@ -104,25 +104,25 @@ export class CommunityRoleInvitationLifecycleOptionsProvider { } if (invitation.invitedToParent) { - if (!community.parentCommunity) { + if (!roleManager.parentRoleManager) { throw new EntityNotInitializedException( `Unable to load parent community when flag to add is set: ${invitation.id}`, LogContext.COMMUNITY ); } await this.communityRoleService.assignContributorToRole( - community.parentCommunity, - contributorID, + roleManager.parentRoleManager, CommunityRoleType.MEMBER, + contributorID, invitation.contributorType, event.agentInfo, true ); } await this.communityRoleService.assignContributorToRole( - community, - contributorID, + roleManager, CommunityRoleType.MEMBER, + contributorID, invitation.contributorType, event.agentInfo, true diff --git a/src/domain/community/community-role/community.role.module.ts b/src/domain/community/community-role/community.role.module.ts index 48393771fd..199b686963 100644 --- a/src/domain/community/community-role/community.role.module.ts +++ b/src/domain/community/community-role/community.role.module.ts @@ -22,6 +22,8 @@ import { ContributionReporterModule } from '@services/external/elasticsearch/con import { NotificationAdapterModule } from '@services/adapters/notification-adapter/notification.adapter.module'; import { ActivityAdapterModule } from '@services/adapters/activity-adapter/activity.adapter.module'; import { LifecycleModule } from '@domain/common/lifecycle/lifecycle.module'; +import { RoleManagerModule } from '@domain/access/role-manager/role.manager.module'; +import { RoleModule } from '@domain/access/role/role.module'; @Module({ imports: [ @@ -30,6 +32,8 @@ import { LifecycleModule } from '@domain/common/lifecycle/lifecycle.module'; EntityResolverModule, AgentModule, CommunityModule, + RoleManagerModule, + RoleModule, UserModule, ContributorModule, OrganizationModule, diff --git a/src/domain/community/community-role/community.role.resolver.mutations.ts b/src/domain/community/community-role/community.role.resolver.mutations.ts index 18a5d23d6e..f50dc2bfed 100644 --- a/src/domain/community/community-role/community.role.resolver.mutations.ts +++ b/src/domain/community/community-role/community.role.resolver.mutations.ts @@ -56,6 +56,8 @@ import { CommunityRoleInvitationLifecycleOptionsProvider } from './community.rol import { CommunityRoleApplicationLifecycleOptionsProvider } from './community.role.lifecycle.application.options.provider'; import { IVirtualContributor } from '../virtual-contributor/virtual.contributor.interface'; import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service'; +import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; +import { CommunityResolverService } from '@services/infrastructure/entity-resolver/community.resolver.service'; @Resolver() export class CommunityRoleResolverMutations { @@ -68,6 +70,8 @@ export class CommunityRoleResolverMutations { private virtualContributorService: VirtualContributorService, private communityRoleService: CommunityRoleService, private communityService: CommunityService, + private roleManagerService: RoleManagerService, + private communityResolverService: CommunityResolverService, private communityAuthorizationService: CommunityAuthorizationService, private communityLifecycleApplicationOptionsProvider: CommunityRoleApplicationLifecycleOptionsProvider, private communityLifecycleInvitationOptionsProvider: CommunityRoleInvitationLifecycleOptionsProvider, @@ -90,8 +94,8 @@ export class CommunityRoleResolverMutations { @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: AssignCommunityRoleToUserInput ): Promise { - const community = await this.communityService.getCommunityOrFail( - roleData.communityID + const roleManager = await this.roleManagerService.getRoleManagerOrFail( + roleData.roleManagerID ); let requiredPrivilege = AuthorizationPrivilege.GRANT; @@ -101,15 +105,15 @@ export class CommunityRoleResolverMutations { this.authorizationService.grantAccessOrFail( agentInfo, - community.authorization, + roleManager.authorization, requiredPrivilege, - `assign user community role: ${community.id}` + `assign user community role: ${roleManager.id}` ); await this.communityRoleService.assignUserToRole( - community, - roleData.userID, + roleManager, roleData.role, + roleData.userID, agentInfo, true ); @@ -132,20 +136,20 @@ export class CommunityRoleResolverMutations { @Args('roleData') roleData: AssignCommunityRoleToOrganizationInput ): Promise { - const community = await this.communityService.getCommunityOrFail( - roleData.communityID + const roleManager = await this.roleManagerService.getRoleManagerOrFail( + roleData.roleManagerID ); this.authorizationService.grantAccessOrFail( agentInfo, - community.authorization, + roleManager.authorization, AuthorizationPrivilege.GRANT, - `assign organization community role: ${community.id}` + `assign organization community role: ${roleManager.id}` ); return await this.communityRoleService.assignOrganizationToRole( - community, - roleData.organizationID, - roleData.role + roleManager, + roleData.role, + roleData.organizationID ); } @@ -159,15 +163,15 @@ export class CommunityRoleResolverMutations { @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: AssignCommunityRoleToVirtualInput ): Promise { - const community = await this.communityService.getCommunityOrFail( - roleData.communityID + const roleManager = await this.roleManagerService.getRoleManagerOrFail( + roleData.roleManagerID ); let requiredPrivilege = AuthorizationPrivilege.GRANT; if (roleData.role === CommunityRoleType.MEMBER) { const sameAccount = await this.communityRoleService.isCommunityAccountMatchingVcAccount( - community.id, + roleManager.id, roleData.virtualContributorID ); if (sameAccount) { @@ -180,23 +184,23 @@ export class CommunityRoleResolverMutations { this.authorizationService.grantAccessOrFail( agentInfo, - community.authorization, + roleManager.authorization, requiredPrivilege, - `assign virtual community role: ${community.id}` + `assign virtual community role: ${roleManager.id}` ); // Also require ACCESS_VIRTUAL_CONTRIBUTORS to assign a virtual contributor this.authorizationService.grantAccessOrFail( agentInfo, - community.authorization, + roleManager.authorization, AuthorizationPrivilege.ACCESS_VIRTUAL_CONTRIBUTOR, - `assign virtual community role VC privilege: ${community.id}` + `assign virtual community role VC privilege: ${roleManager.id}` ); await this.communityRoleService.assignVirtualToRole( - community, - roleData.virtualContributorID, + roleManager, roleData.role, + roleData.virtualContributorID, agentInfo, true ); @@ -215,13 +219,18 @@ export class CommunityRoleResolverMutations { @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: RemoveCommunityRoleFromUserInput ): Promise { - const community = await this.communityService.getCommunityOrFail( - roleData.communityID + const roleManager = await this.roleManagerService.getRoleManagerOrFail( + roleData.roleManagerID ); // Extend the authorization policy with a credential rule to assign the GRANT privilege // to the user specified in the incoming mutation. Then if it is the same user as is logged // in then the user will have the GRANT privilege + so can carry out the mutation + const community = + await this.communityResolverService.getCommunityForRoleManager( + roleManager.id + ); + const extendedAuthorization = this.communityAuthorizationService.extendAuthorizationPolicyForSelfRemoval( community, @@ -232,13 +241,13 @@ export class CommunityRoleResolverMutations { agentInfo, extendedAuthorization, AuthorizationPrivilege.GRANT, - `remove user from community role: ${community.id}` + `remove user from community role: ${roleManager.id}` ); await this.communityRoleService.removeUserFromRole( - community, - roleData.userID, - roleData.role + roleManager, + roleData.role, + roleData.userID ); // reset the user authorization policy so that their profile is not visible // to other community members @@ -259,20 +268,20 @@ export class CommunityRoleResolverMutations { @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: RemoveCommunityRoleFromOrganizationInput ): Promise { - const community = await this.communityService.getCommunityOrFail( - roleData.communityID + const roleManager = await this.roleManagerService.getRoleManagerOrFail( + roleData.roleManagerID ); await this.authorizationService.grantAccessOrFail( agentInfo, - community.authorization, + roleManager.authorization, AuthorizationPrivilege.GRANT, - `remove community role organization: ${community.id}` + `remove community role organization: ${roleManager.id}` ); return await this.communityRoleService.removeOrganizationFromRole( - community, - roleData.organizationID, - roleData.role + roleManager, + roleData.role, + roleData.organizationID ); } @@ -286,7 +295,7 @@ export class CommunityRoleResolverMutations { @Args('roleData') roleData: RemoveCommunityRoleFromVirtualInput ): Promise { const community = await this.communityService.getCommunityOrFail( - roleData.communityID + roleData.roleManagerID ); // Extend the authorization policy with a credential rule to assign the GRANT privilege @@ -307,8 +316,8 @@ export class CommunityRoleResolverMutations { await this.communityRoleService.removeVirtualFromRole( community, - roleData.virtualContributorID, - roleData.role + roleData.role, + roleData.virtualContributorID ); return await this.virtualContributorService.getVirtualContributorOrFail( @@ -326,7 +335,7 @@ export class CommunityRoleResolverMutations { @Args('applicationData') applicationData: CommunityRoleApplyInput ): Promise { const community = await this.communityService.getCommunityOrFail( - applicationData.communityID, + applicationData.roleManagerID, { relations: { parentCommunity: true, @@ -359,7 +368,7 @@ export class CommunityRoleResolverMutations { } let application = await this.communityRoleService.createApplication({ - parentID: community.id, + roleManagerID: community.id, questions: applicationData.questions, userID: agentInfo.userID, }); @@ -395,7 +404,7 @@ export class CommunityRoleResolverMutations { invitationData: CreateInvitationForContributorsOnCommunityInput ): Promise { const community = await this.communityService.getCommunityOrFail( - invitationData.communityID, + invitationData.roleManagerID, { relations: { parentCommunity: { @@ -489,7 +498,7 @@ export class CommunityRoleResolverMutations { welcomeMessage?: string ): Promise { const input: CreateInvitationInput = { - communityID: community.id, + roleManagerID: community.id, invitedContributor: invitedContributor.id, createdBy: agentInfo.userID, invitedToParent, @@ -552,7 +561,7 @@ export class CommunityRoleResolverMutations { invitationData: CreatePlatformInvitationOnCommunityInput ): Promise { const community = await this.communityService.getCommunityOrFail( - invitationData.communityID, + invitationData.roleManagerID, { relations: { parentCommunity: { @@ -643,7 +652,7 @@ export class CommunityRoleResolverMutations { @Args('joinCommunityData') joiningData: CommunityJoinInput ): Promise { const community = await this.communityService.getCommunityOrFail( - joiningData.communityID + joiningData.roleManagerID ); const membershipStatus = await this.communityRoleService.getMembershipStatus(agentInfo, community); @@ -663,8 +672,8 @@ export class CommunityRoleResolverMutations { await this.communityRoleService.assignUserToRole( community, - agentInfo.userID, CommunityRoleType.MEMBER, + agentInfo.userID, agentInfo, true ); diff --git a/src/domain/community/community-role/community.role.service.events.ts b/src/domain/community/community-role/community.role.service.events.ts index 2514ac2029..7418419491 100644 --- a/src/domain/community/community-role/community.role.service.events.ts +++ b/src/domain/community/community-role/community.role.service.events.ts @@ -50,7 +50,7 @@ export class CommunityRoleEventsService { // Record the contribution events const space = - await this.communityResolverService.getSpaceForCommunityOrFail( + await this.communityResolverService.getSpaceForRoleManagerOrFail( community.id ); switch (space.type) { diff --git a/src/domain/community/community-role/community.role.service.ts b/src/domain/community/community-role/community.role.service.ts index c1577ecd5f..162b1928be 100644 --- a/src/domain/community/community-role/community.role.service.ts +++ b/src/domain/community/community-role/community.role.service.ts @@ -22,7 +22,6 @@ import { CredentialDefinition } from '@domain/agent/credential/credential.defini import { CommunityRoleType } from '@common/enums/community.role'; import { CommunityContributorsUpdateType } from '@common/enums/community.contributors.update.type'; import { CommunityContributorType } from '@common/enums/community.contributor.type'; -import { ICommunityRolePolicy } from '../community-policy/community.policy.role.interface'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; import { InvitationService } from '../invitation/invitation.service'; @@ -43,6 +42,9 @@ import { IPlatformInvitation } from '@platform/invitation'; import { CreatePlatformInvitationInput } from '@platform/invitation/dto/platform.invitation.dto.create'; import { AiServerAdapter } from '@services/adapters/ai-server-adapter/ai.server.adapter'; import { CommunityService } from '../community/community.service'; +import { IRoleManager } from '@domain/access/role-manager'; +import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; +import { RoleService } from '@domain/access/role/role.service'; @Injectable() export class CommunityRoleService { @@ -58,27 +60,29 @@ export class CommunityRoleService { private communityResolverService: CommunityResolverService, private communityRoleEventsService: CommunityRoleEventsService, private communityService: CommunityService, + private roleManagerService: RoleManagerService, + private roleService: RoleService, private aiServerAdapter: AiServerAdapter, //TODO: remove this asap @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService ) {} - public async removeAllCommunityRoles(community: ICommunity) { + public async removeAllCommunityRoles(roleManager: IRoleManager) { // Remove all issued role credentials for contributors - for (const role of Object.values(CommunityRoleType)) { - const users = await this.getUsersWithRole(community, role); + for (const roleType of Object.values(CommunityRoleType)) { + const users = await this.getUsersWithRole(roleManager, roleType); for (const user of users) { - await this.removeUserFromRole(community, user.id, role, false); + await this.removeUserFromRole(roleManager, roleType, user.id, false); } const organizations = await this.getOrganizationsWithRole( - community, - role + roleManager, + roleType ); for (const organization of organizations) { await this.removeOrganizationFromRole( - community, + roleManager, + roleType, organization.id, - role, false ); } @@ -157,11 +161,11 @@ export class CommunityRoleService { private async findOpenApplication( userID: string, - communityID: string + roleManagerID: string ): Promise { const applications = await this.applicationService.findExistingApplications( userID, - communityID + roleManagerID ); for (const application of applications) { // skip any finalized applications; only want to return pending applications @@ -176,11 +180,11 @@ export class CommunityRoleService { private async findOpenInvitation( contributorID: string, - communityID: string + roleManagerID: string ): Promise { const invitations = await this.invitationService.findExistingInvitations( contributorID, - communityID + roleManagerID ); for (const invitation of invitations) { // skip any finalized applications; only want to return pending applications @@ -194,13 +198,13 @@ export class CommunityRoleService { } async getUsersWithRole( - community: ICommunity, - role: CommunityRoleType, + roleManager: IRoleManager, + roleType: CommunityRoleType, limit?: number ): Promise { const membershipCredential = this.getCredentialDefinitionForRole( - community, - role + roleManager, + roleType ); return await this.userService.usersWithCredentials( { @@ -212,12 +216,12 @@ export class CommunityRoleService { } async getVirtualContributorsWithRole( - community: ICommunity, - role: CommunityRoleType + roleManager: IRoleManager, + roleType: CommunityRoleType ): Promise { const membershipCredential = this.getCredentialDefinitionForRole( - community, - role + roleManager, + roleType ); return await this.virtualContributorService.virtualContributorsWithCredentials( { @@ -228,12 +232,12 @@ export class CommunityRoleService { } async getOrganizationsWithRole( - community: ICommunity, - role: CommunityRoleType + roleManager: IRoleManager, + roleType: CommunityRoleType ): Promise { const membershipCredential = this.getCredentialDefinitionForRole( - community, - role + roleManager, + roleType ); return await this.organizationService.organizationsWithCredentials({ type: membershipCredential.type, @@ -242,13 +246,13 @@ export class CommunityRoleService { } async countContributorsPerRole( - community: ICommunity, - role: CommunityRoleType, + roleManager: IRoleManager, + roleType: CommunityRoleType, contributorType: CommunityContributorType ): Promise { const membershipCredential = this.getCredentialDefinitionForRole( - community, - role + roleManager, + roleType ); if (contributorType === CommunityContributorType.ORGANIZATION) { @@ -269,20 +273,20 @@ export class CommunityRoleService { } getCredentialDefinitionForRole( - community: ICommunity, + roleManager: IRoleManager, role: CommunityRoleType ): CredentialDefinition { - const policyRole = this.communityService.getCommunityPolicyForRole( - community, + const credential = this.roleManagerService.getCredentialForRole( + roleManager, role ); - return policyRole.credential; + return credential; } async assignContributorToRole( - community: ICommunity, + roleManager: IRoleManager, + roleType: CommunityRoleType, contributorID: string, - role: CommunityRoleType, contributorType: CommunityContributorType, agentInfo?: AgentInfo, triggerNewMemberEvents = false @@ -290,23 +294,23 @@ export class CommunityRoleService { switch (contributorType) { case CommunityContributorType.USER: return await this.assignUserToRole( - community, + roleManager, + roleType, contributorID, - role, agentInfo, triggerNewMemberEvents ); case CommunityContributorType.ORGANIZATION: return await this.assignOrganizationToRole( - community, - contributorID, - role + roleManager, + roleType, + contributorID ); case CommunityContributorType.VIRTUAL: return await this.assignVirtualToRole( - community, + roleManager, + roleType, contributorID, - role, agentInfo, triggerNewMemberEvents ); @@ -319,34 +323,38 @@ export class CommunityRoleService { } async assignUserToRole( - community: ICommunity, + roleManager: IRoleManager, + roleType: CommunityRoleType, userID: string, - role: CommunityRoleType, agentInfo?: AgentInfo, triggerNewMemberEvents = false ): Promise { const { user, agent } = await this.userService.getUserAndAgent(userID); const { isMember: hasMemberRoleInParent, parentCommunity } = - await this.isMemberInParentCommunity(agent, community.id); + await this.isMemberInParentCommunity(agent, roleManager.id); if (!hasMemberRoleInParent) { throw new ValidationException( - `Unable to assign Agent (${agent.id}) to community (${community.id}): agent is not a member of parent community ${parentCommunity?.id}`, + `Unable to assign Agent (${agent.id}) to community (${roleManager.id}): agent is not a member of parent community ${parentCommunity?.id}`, LogContext.SPACES ); } - const userAlreadyHasRole = await this.isInRole(agent, community, role); + const userAlreadyHasRole = await this.isInRole( + agent, + roleManager, + roleType + ); if (userAlreadyHasRole) { return user; } user.agent = await this.assignContributorAgentToRole( - community, + roleManager, + roleType, agent, - role, CommunityContributorType.USER ); - if (role === CommunityRoleType.ADMIN && parentCommunity) { + if (roleType === CommunityRoleType.ADMIN && parentCommunity) { // also assign as subspace admin in parent community if there is a parent community const credential = this.getCredentialForImplicitRole( parentCommunity, @@ -365,8 +373,8 @@ export class CommunityRoleService { await this.contributorAddedToRole( user, - community, - role, + roleManager, + roleType, agentInfo, triggerNewMemberEvents ); @@ -376,12 +384,16 @@ export class CommunityRoleService { private async contributorAddedToRole( contributor: IContributor, - community: ICommunity, + roleManager: IRoleManager, role: CommunityRoleType, agentInfo?: AgentInfo, triggerNewMemberEvents = false ) { if (role === CommunityRoleType.MEMBER) { + const community = + await this.communityResolverService.getCommunityForRoleManager( + roleManager.id + ); this.communityService.addMemberToCommunication(contributor, community); if (agentInfo) { @@ -394,7 +406,7 @@ export class CommunityRoleService { if (triggerNewMemberEvents) { const levelZeroSpaceID = await this.communityService.getLevelZeroSpaceIdForCommunity( - community + roleManager ); const displayName = await this.communityService.getDisplayName(community); @@ -411,9 +423,9 @@ export class CommunityRoleService { } async assignVirtualToRole( - community: ICommunity, + roleManager: IRoleManager, + roleType: CommunityRoleType, virtualContributorID: string, - role: CommunityRoleType, agentInfo?: AgentInfo, triggerNewMemberEvents = false ): Promise { @@ -422,43 +434,47 @@ export class CommunityRoleService { virtualContributorID ); const { isMember: hasMemberRoleInParent, parentCommunity } = - await this.isMemberInParentCommunity(agent, community.id); + await this.isMemberInParentCommunity(agent, roleManager.id); if (!hasMemberRoleInParent) { if (!parentCommunity) { throw new ValidationException( - `Unable to find parent community for community ${community.id}`, + `Unable to find parent community for community ${roleManager.id}`, LogContext.SPACES ); } throw new ValidationException( - `Unable to assign Agent (${agent.id}) to community (${community.id}): agent is not a member of parent community ${parentCommunity.id}`, + `Unable to assign Agent (${agent.id}) to community (${roleManager.id}): agent is not a member of parent community ${parentCommunity.id}`, LogContext.SPACES ); } - const virtualAlreadyHasRole = await this.isInRole(agent, community, role); + const virtualAlreadyHasRole = await this.isInRole( + agent, + roleManager, + roleType + ); if (virtualAlreadyHasRole) { return virtualContributor; } virtualContributor.agent = await this.assignContributorAgentToRole( - community, + roleManager, + roleType, agent, - role, CommunityContributorType.VIRTUAL ); await this.contributorAddedToRole( virtualContributor, - community, - role, + roleManager, + roleType, agentInfo, triggerNewMemberEvents ); // TO: THIS BREAKS THE DECOUPLING const space = - await this.communityResolverService.getSpaceForCommunityOrFail( - community.id + await this.communityResolverService.getSpaceForRoleManagerOrFail( + roleManager.id ); this.aiServerAdapter.ensureContextIsLoaded(space.id); return virtualContributor; @@ -493,17 +509,17 @@ export class CommunityRoleService { } async assignOrganizationToRole( - community: ICommunity, - organizationID: string, - role: CommunityRoleType + roleManager: IRoleManager, + roleType: CommunityRoleType, + organizationID: string ): Promise { const { organization, agent } = await this.organizationService.getOrganizationAndAgent(organizationID); organization.agent = await this.assignContributorAgentToRole( - community, + roleManager, + roleType, agent, - role, CommunityContributorType.ORGANIZATION ); @@ -511,43 +527,49 @@ export class CommunityRoleService { } async removeUserFromRole( - community: ICommunity, + roleManager: IRoleManager, + roleType: CommunityRoleType, userID: string, - role: CommunityRoleType, validatePolicyLimits = true ): Promise { const { user, agent } = await this.userService.getUserAndAgent(userID); user.agent = await this.removeContributorFromRole( - community, + roleManager, + roleType, agent, - role, CommunityContributorType.USER, validatePolicyLimits ); - const parentCommunity = - await this.communityService.getParentCommunity(community); - if (role === CommunityRoleType.ADMIN && parentCommunity) { + const parentRoleManager = + await this.roleManagerService.getParentRoleManager(roleManager); + if (roleType === CommunityRoleType.ADMIN && parentRoleManager) { // Check if an admin anywhere else in the community - const peerCommunities = await this.communityService.getPeerCommunites( - parentCommunity, - community - ); - const hasAnotherAdminRole = peerCommunities.some(pc => + const peerRoleManagers = + await this.roleManagerService.getPeerRoleManagers( + parentRoleManager, + roleManager + ); + const hasAnotherAdminRole = peerRoleManagers.some(pc => this.isInRole(agent, pc, CommunityRoleType.ADMIN) ); if (!hasAnotherAdminRole) { await this.removeContributorToImplicitRole( - parentCommunity, + parentRoleManager, agent, CommunityRoleImplicit.SUBSPACE_ADMIN ); } } - if (role === CommunityRoleType.MEMBER) { + if (roleType === CommunityRoleType.MEMBER) { + const community = + await this.communityResolverService.getCommunityForRoleManager( + roleManager.id + ); + await this.communityService.removeMemberFromCommunication( community, user @@ -558,18 +580,18 @@ export class CommunityRoleService { } async removeOrganizationFromRole( - community: ICommunity, + roleManager: IRoleManager, + roleType: CommunityRoleType, organizationID: string, - role: CommunityRoleType, validatePolicyLimits = true ): Promise { const { organization, agent } = await this.organizationService.getOrganizationAndAgent(organizationID); organization.agent = await this.removeContributorFromRole( - community, + roleManager, + roleType, agent, - role, CommunityContributorType.ORGANIZATION, validatePolicyLimits ); @@ -578,9 +600,9 @@ export class CommunityRoleService { } async removeVirtualFromRole( - community: ICommunity, + roleManager: IRoleManager, + roleType: CommunityRoleType, virtualContributorID: string, - role: CommunityRoleType, validatePolicyLimits = true ): Promise { const { virtualContributor, agent } = @@ -589,9 +611,9 @@ export class CommunityRoleService { ); virtualContributor.agent = await this.removeContributorFromRole( - community, + roleManager, + roleType, agent, - role, CommunityContributorType.VIRTUAL, validatePolicyLimits ); @@ -610,30 +632,36 @@ export class CommunityRoleService { } private async validateUserCommunityPolicy( - community: ICommunity, - communityPolicyRole: ICommunityRolePolicy, - role: CommunityRoleType, + roleManager: IRoleManager, + roleType: CommunityRoleType, action: CommunityContributorsUpdateType ) { const userMembersCount = await this.countContributorsPerRole( - community, - role, + roleManager, + roleType, CommunityContributorType.USER ); + const roleDefinition = this.roleManagerService.getRoleDefinition( + roleManager, + roleType + ); + + const userPolicy = this.roleService.getUserPolicy(roleDefinition); + if (action === CommunityContributorsUpdateType.ASSIGN) { - if (userMembersCount === communityPolicyRole.maxUser) { + if (userMembersCount === userPolicy.maximum) { throw new CommunityPolicyRoleLimitsException( - `Max limit of users reached for role '${role}': ${communityPolicyRole.maxUser}, cannot assign new user.`, + `Max limit of users reached for role '${roleType}': ${userPolicy.maximum}, cannot assign new user.`, LogContext.COMMUNITY ); } } if (action === CommunityContributorsUpdateType.REMOVE) { - if (userMembersCount === communityPolicyRole.minUser) { + if (userMembersCount === userPolicy.minimum) { throw new CommunityPolicyRoleLimitsException( - `Min limit of users reached for role '${role}': ${communityPolicyRole.minUser}, cannot remove user.`, + `Min limit of users reached for role '${roleType}': ${userPolicy.minimum}, cannot remove user.`, LogContext.COMMUNITY ); } @@ -641,30 +669,37 @@ export class CommunityRoleService { } private async validateOrganizationCommunityPolicy( - community: ICommunity, - communityPolicyRole: ICommunityRolePolicy, - role: CommunityRoleType, + roleManager: IRoleManager, + roleType: CommunityRoleType, action: CommunityContributorsUpdateType ) { const orgMemberCount = await this.countContributorsPerRole( - community, - role, + roleManager, + roleType, CommunityContributorType.ORGANIZATION ); + const roleDefinition = this.roleManagerService.getRoleDefinition( + roleManager, + roleType + ); + + const organizationPolicy = + this.roleService.getOrganizationPolicy(roleDefinition); + if (action === CommunityContributorsUpdateType.ASSIGN) { - if (orgMemberCount === communityPolicyRole.maxOrg) { + if (orgMemberCount === organizationPolicy.maximum) { throw new CommunityPolicyRoleLimitsException( - `Max limit of organizations reached for role '${role}': ${communityPolicyRole.maxOrg}, cannot assign new organization.`, + `Max limit of organizations reached for role '${roleType}': ${organizationPolicy.maximum}, cannot assign new organization.`, LogContext.COMMUNITY ); } } if (action === CommunityContributorsUpdateType.REMOVE) { - if (orgMemberCount === communityPolicyRole.minOrg) { + if (orgMemberCount === organizationPolicy.minimum) { throw new CommunityPolicyRoleLimitsException( - `Min limit of organizations reached for role '${role}': ${communityPolicyRole.minOrg}, cannot remove organization.`, + `Min limit of organizations reached for role '${roleType}': ${organizationPolicy.minimum}, cannot remove organization.`, LogContext.COMMUNITY ); } @@ -672,60 +707,53 @@ export class CommunityRoleService { } private async validateCommunityPolicyLimits( - community: ICommunity, - communityPolicyRole: ICommunityRolePolicy, - role: CommunityRoleType, + roleManager: IRoleManager, + roleType: CommunityRoleType, action: CommunityContributorsUpdateType, contributorType: CommunityContributorType ) { if (contributorType === CommunityContributorType.USER) - await this.validateUserCommunityPolicy( - community, - communityPolicyRole, - role, - action - ); + await this.validateUserCommunityPolicy(roleManager, roleType, action); if (contributorType === CommunityContributorType.ORGANIZATION) await this.validateOrganizationCommunityPolicy( - community, - communityPolicyRole, - role, + roleManager, + roleType, action ); } public async assignContributorAgentToRole( - community: ICommunity, + roleManager: IRoleManager, + roleType: CommunityRoleType, agent: IAgent, - role: CommunityRoleType, contributorType: CommunityContributorType ): Promise { - const communityPolicyRole = this.communityService.getCommunityPolicyForRole( - community, - role - ); await this.validateCommunityPolicyLimits( - community, - communityPolicyRole, - role, + roleManager, + roleType, CommunityContributorsUpdateType.ASSIGN, contributorType ); + const roleCredential = this.roleManagerService.getCredentialForRole( + roleManager, + roleType + ); + return await this.agentService.grantCredential({ agentID: agent.id, - type: communityPolicyRole.credential.type, - resourceID: communityPolicyRole.credential.resourceID, + type: roleCredential.type, + resourceID: roleCredential.resourceID, }); } private async assignContributorToImplicitRole( - community: ICommunity, + roleManager: IRoleManager, agent: IAgent, role: CommunityRoleImplicit ): Promise { - const credential = this.getCredentialForImplicitRole(community, role); + const credential = this.getCredentialForImplicitRole(roleManager, role); return await this.agentService.grantCredential({ agentID: agent.id, @@ -735,11 +763,11 @@ export class CommunityRoleService { } private async removeContributorToImplicitRole( - community: ICommunity, + roleManager: IRoleManager, agent: IAgent, role: CommunityRoleImplicit ): Promise { - const credential = this.getCredentialForImplicitRole(community, role); + const credential = this.getCredentialForImplicitRole(roleManager, role); return await this.agentService.revokeCredential({ agentID: agent.id, @@ -749,12 +777,12 @@ export class CommunityRoleService { } private getCredentialForImplicitRole( - community: ICommunity, + roleManager: IRoleManager, role: CommunityRoleImplicit ): CredentialDefinition { // Use the admin credential to get the resourceID const adminCredential = this.getCredentialDefinitionForRole( - community, + roleManager, CommunityRoleType.ADMIN ); const resourceID = adminCredential.resourceID; @@ -774,36 +802,36 @@ export class CommunityRoleService { } private async removeContributorFromRole( - community: ICommunity, + roleManager: IRoleManager, + roleType: CommunityRoleType, agent: IAgent, - role: CommunityRoleType, contributorType: CommunityContributorType, validatePolicyLimits: boolean ): Promise { - const communityPolicyRole = this.communityService.getCommunityPolicyForRole( - community, - role - ); if (validatePolicyLimits) { await this.validateCommunityPolicyLimits( - community, - communityPolicyRole, - role, + roleManager, + roleType, CommunityContributorsUpdateType.REMOVE, contributorType ); } + const roleCredential = this.roleManagerService.getCredentialForRole( + roleManager, + roleType + ); + return await this.agentService.revokeCredential({ agentID: agent.id, - type: communityPolicyRole.credential.type, - resourceID: communityPolicyRole.credential.resourceID, + type: roleCredential.type, + resourceID: roleCredential.resourceID, }); } - async isMember(agent: IAgent, community: ICommunity): Promise { + async isMember(agent: IAgent, roleManager: IRoleManager): Promise { const membershipCredential = this.getCredentialDefinitionForRole( - community, + roleManager, CommunityRoleType.MEMBER ); @@ -819,11 +847,11 @@ export class CommunityRoleService { async isInRole( agent: IAgent, - community: ICommunity, + roleManager: IRoleManager, role: CommunityRoleType ): Promise { const membershipCredential = this.getCredentialDefinitionForRole( - community, + roleManager, role ); @@ -839,11 +867,11 @@ export class CommunityRoleService { async isInRoleImplicit( agent: IAgent, - community: ICommunity, + roleManager: IRoleManager, role: CommunityRoleImplicit ): Promise { const membershipCredential = this.getCredentialForImplicitRole( - community, + roleManager, role ); @@ -863,18 +891,20 @@ export class CommunityRoleService { const { user, agent } = await this.userService.getUserAndAgent( applicationData.userID ); - const community = await this.communityService.getCommunityOrFail( - applicationData.parentID, + const roleManager = await this.roleManagerService.getRoleManagerOrFail( + applicationData.roleManagerID, { - relations: { parentCommunity: true }, + relations: { + parentRoleManager: true, + }, } ); - await this.validateApplicationFromUser(user, agent, community); + await this.validateApplicationFromUser(user, agent, roleManager); const application = await this.applicationService.createApplication(applicationData); - application.community = community; + application.roleManager = roleManager; return await this.applicationService.save(application); } @@ -885,21 +915,21 @@ export class CommunityRoleService { await this.contributorService.getContributorAndAgent( invitationData.invitedContributor ); - const community = await this.communityService.getCommunityOrFail( - invitationData.communityID + const roleManager = await this.roleManagerService.getRoleManagerOrFail( + invitationData.roleManagerID ); await this.validateInvitationToExistingContributor( contributor, agent, - community + roleManager ); const invitation = await this.invitationService.createInvitation( invitationData, contributor ); - invitation.community = community; + invitation.roleManager = roleManager; return await this.invitationService.save(invitation); } @@ -910,10 +940,10 @@ export class CommunityRoleService { ): Promise { await this.validateInvitationToExternalUser( invitationData.email, - invitationData.communityID + invitationData.roleManagerID ); - const community = await this.communityService.getCommunityOrFail( - invitationData.communityID, + const roleManager = await this.roleManagerService.getRoleManagerOrFail( + invitationData.roleManagerID, { relations: {}, } @@ -927,39 +957,42 @@ export class CommunityRoleService { await this.platformInvitationService.createPlatformInvitation( externalInvitationInput ); - externalInvitation.community = community; + externalInvitation.roleManager = roleManager; return await this.platformInvitationService.save(externalInvitation); } private async validateApplicationFromUser( user: IUser, agent: IAgent, - community: ICommunity + roleManager: IRoleManager ) { const openApplication = await this.findOpenApplication( user.id, - community.id + roleManager.id ); if (openApplication) { throw new CommunityMembershipException( - `Application not possible: An open application (ID: ${openApplication.id}) already exists for contributor ${openApplication.user?.id} on Community: ${community.id}.`, + `Application not possible: An open application (ID: ${openApplication.id}) already exists for contributor ${openApplication.user?.id} on Community: ${roleManager.id}.`, LogContext.COMMUNITY ); } - const openInvitation = await this.findOpenInvitation(user.id, community.id); + const openInvitation = await this.findOpenInvitation( + user.id, + roleManager.id + ); if (openInvitation) { throw new CommunityMembershipException( - `Application not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on Community: ${community.id}.`, + `Application not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on Community: ${roleManager.id}.`, LogContext.COMMUNITY ); } // Check if the user is already a member; if so do not allow an application - const isExistingMember = await this.isMember(agent, community); + const isExistingMember = await this.isMember(agent, roleManager); if (isExistingMember) throw new CommunityMembershipException( - `Application not possible: Contributor ${user.id} is already a member of the Community: ${community.id}.`, + `Application not possible: Contributor ${user.id} is already a member of the Community: ${roleManager.id}.`, LogContext.COMMUNITY ); } @@ -967,42 +1000,42 @@ export class CommunityRoleService { private async validateInvitationToExistingContributor( contributor: IContributor, agent: IAgent, - community: ICommunity + roleManager: IRoleManager ) { const openInvitation = await this.findOpenInvitation( contributor.id, - community.id + roleManager.id ); if (openInvitation) { throw new CommunityMembershipException( - `Invitation not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on Community: ${community.id}.`, + `Invitation not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on Community: ${roleManager.id}.`, LogContext.COMMUNITY ); } const openApplication = await this.findOpenApplication( contributor.id, - community.id + roleManager.id ); if (openApplication) { throw new CommunityMembershipException( - `Invitation not possible: An open application (ID: ${openApplication.id}) already exists for contributor ${openApplication.user?.id} on Community: ${community.id}.`, + `Invitation not possible: An open application (ID: ${openApplication.id}) already exists for contributor ${openApplication.user?.id} on Community: ${roleManager.id}.`, LogContext.COMMUNITY ); } // Check if the user is already a member; if so do not allow an application - const isExistingMember = await this.isMember(agent, community); + const isExistingMember = await this.isMember(agent, roleManager); if (isExistingMember) throw new CommunityMembershipException( - `Invitation not possible: Contributor ${contributor.id} is already a member of the Community: ${community.id}.`, + `Invitation not possible: Contributor ${contributor.id} is already a member of the Community: ${roleManager.id}.`, LogContext.COMMUNITY ); } private async validateInvitationToExternalUser( email: string, - communityID: string + roleManagerID: string ) { // Check if a user with the provided email address already exists or not const isExistingUser = await this.userService.isRegisteredUser(email); @@ -1019,47 +1052,43 @@ export class CommunityRoleService { ); for (const platformInvitation of platformInvitations) { if ( - platformInvitation.community && - platformInvitation.community.id === communityID + platformInvitation.roleManager && + platformInvitation.roleManager.id === roleManagerID ) { throw new CommunityMembershipException( - `An invitation with the provided email address (${email}) already exists for the specified community: ${communityID}`, + `An invitation with the provided email address (${email}) already exists for the specified community: ${roleManagerID}`, LogContext.COMMUNITY ); } } } - async getApplications(community: ICommunity): Promise { - const communityApps = await this.communityService.getCommunityOrFail( - community.id, - { + async getApplications(roleManager: IRoleManager): Promise { + const communityApplications = + await this.roleManagerService.getRoleManagerOrFail(roleManager.id, { relations: { applications: true }, - } - ); - return communityApps?.applications || []; + }); + return communityApplications?.applications || []; } - async getInvitations(community: ICommunity): Promise { - const communityApps = await this.communityService.getCommunityOrFail( - community.id, - { + async getInvitations(roleManager: IRoleManager): Promise { + const roleManagerInvitations = + await this.roleManagerService.getRoleManagerOrFail(roleManager.id, { relations: { invitations: true }, - } - ); - return communityApps?.invitations || []; + }); + return roleManagerInvitations?.invitations || []; } async getPlatformInvitations( - community: ICommunity + roleManager: IRoleManager ): Promise { - const communityApps = await this.communityService.getCommunityOrFail( - community.id, + const communityInvs = await this.roleManagerService.getRoleManagerOrFail( + roleManager.id, { relations: { platformInvitations: true }, } ); - return communityApps?.platformInvitations || []; + return communityInvs?.platformInvitations || []; } async getMembersCount(community: ICommunity): Promise { diff --git a/src/domain/community/community-role/dto/community.role.dto.apply.ts b/src/domain/community/community-role/dto/community.role.dto.apply.ts index 12830e7090..f247f51b97 100644 --- a/src/domain/community/community-role/dto/community.role.dto.apply.ts +++ b/src/domain/community/community-role/dto/community.role.dto.apply.ts @@ -9,7 +9,7 @@ import { UUID_LENGTH } from '@common/constants'; export class CommunityRoleApplyInput { @Field(() => UUID, { nullable: false }) @MaxLength(UUID_LENGTH) - communityID!: string; + roleManagerID!: string; @Field(() => [CreateNVPInput], { nullable: false }) @ValidateNested({ each: true }) diff --git a/src/domain/community/community-role/dto/community.role.dto.invite.contributor.ts b/src/domain/community/community-role/dto/community.role.dto.invite.contributor.ts index edd9cd5b68..37f58c97a7 100644 --- a/src/domain/community/community-role/dto/community.role.dto.invite.contributor.ts +++ b/src/domain/community/community-role/dto/community.role.dto.invite.contributor.ts @@ -7,7 +7,7 @@ import { MID_TEXT_LENGTH, UUID_LENGTH } from '@common/constants'; export class CreateInvitationForContributorsOnCommunityInput { @Field(() => UUID, { nullable: false }) @MaxLength(UUID_LENGTH) - communityID!: string; + roleManagerID!: string; @Field(() => [UUID], { nullable: false, diff --git a/src/domain/community/community-role/dto/community.role.dto.join.ts b/src/domain/community/community-role/dto/community.role.dto.join.ts index d51303031b..46406cfbd2 100644 --- a/src/domain/community/community-role/dto/community.role.dto.join.ts +++ b/src/domain/community/community-role/dto/community.role.dto.join.ts @@ -7,5 +7,5 @@ import { UUID_LENGTH } from '@common/constants'; export class CommunityJoinInput { @Field(() => UUID, { nullable: false }) @MaxLength(UUID_LENGTH) - communityID!: string; + roleManagerID!: string; } diff --git a/src/domain/community/community-role/dto/community.role.dto.platform.invitation.community.ts b/src/domain/community/community-role/dto/community.role.dto.platform.invitation.community.ts index e7b8df8e8c..056cc48abf 100644 --- a/src/domain/community/community-role/dto/community.role.dto.platform.invitation.community.ts +++ b/src/domain/community/community-role/dto/community.role.dto.platform.invitation.community.ts @@ -8,5 +8,5 @@ import { CreatePlatformInvitationInput } from '@platform/invitation/dto/platform export class CreatePlatformInvitationOnCommunityInput extends CreatePlatformInvitationInput { @Field(() => UUID, { nullable: false }) @MaxLength(UUID_LENGTH) - communityID!: string; + roleManagerID!: string; } diff --git a/src/domain/community/community-role/dto/community.role.dto.role.assign.organization.ts b/src/domain/community/community-role/dto/community.role.dto.role.assign.organization.ts index ea57e60288..25d3921368 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.assign.organization.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.assign.organization.ts @@ -5,7 +5,7 @@ import { Field, InputType } from '@nestjs/graphql'; @InputType() export class AssignCommunityRoleToOrganizationInput { @Field(() => UUID, { nullable: false }) - communityID!: string; + roleManagerID!: string; @Field(() => UUID_NAMEID, { nullable: false }) organizationID!: string; diff --git a/src/domain/community/community-role/dto/community.role.dto.role.assign.user.ts b/src/domain/community/community-role/dto/community.role.dto.role.assign.user.ts index 898cf5390f..0c6e7e105a 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.assign.user.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.assign.user.ts @@ -5,7 +5,7 @@ import { Field, InputType } from '@nestjs/graphql'; @InputType() export class AssignCommunityRoleToUserInput { @Field(() => UUID, { nullable: false }) - communityID!: string; + roleManagerID!: string; @Field(() => UUID_NAMEID_EMAIL, { nullable: false }) userID!: string; diff --git a/src/domain/community/community-role/dto/community.role.dto.role.assign.virtual.ts b/src/domain/community/community-role/dto/community.role.dto.role.assign.virtual.ts index df16bd4fae..b4e15d4c6c 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.assign.virtual.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.assign.virtual.ts @@ -5,7 +5,7 @@ import { Field, InputType } from '@nestjs/graphql'; @InputType() export class AssignCommunityRoleToVirtualInput { @Field(() => UUID, { nullable: false }) - communityID!: string; + roleManagerID!: string; @Field(() => UUID_NAMEID, { nullable: false }) virtualContributorID!: string; diff --git a/src/domain/community/community-role/dto/community.role.dto.role.remove.organization.ts b/src/domain/community/community-role/dto/community.role.dto.role.remove.organization.ts index 877df36769..1417c6e019 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.remove.organization.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.remove.organization.ts @@ -5,7 +5,7 @@ import { Field, InputType } from '@nestjs/graphql'; @InputType() export class RemoveCommunityRoleFromOrganizationInput { @Field(() => UUID, { nullable: false }) - communityID!: string; + roleManagerID!: string; @Field(() => UUID_NAMEID, { nullable: false }) organizationID!: string; diff --git a/src/domain/community/community-role/dto/community.role.dto.role.remove.user.ts b/src/domain/community/community-role/dto/community.role.dto.role.remove.user.ts index 68e76d11e1..1411ee9ffe 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.remove.user.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.remove.user.ts @@ -5,7 +5,7 @@ import { Field, InputType } from '@nestjs/graphql'; @InputType() export class RemoveCommunityRoleFromUserInput { @Field(() => UUID, { nullable: false }) - communityID!: string; + roleManagerID!: string; @Field(() => UUID_NAMEID_EMAIL, { nullable: false }) userID!: string; diff --git a/src/domain/community/community-role/dto/community.role.dto.role.remove.virtual.ts b/src/domain/community/community-role/dto/community.role.dto.role.remove.virtual.ts index ba0649e47b..c2a60d0428 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.remove.virtual.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.remove.virtual.ts @@ -5,7 +5,7 @@ import { Field, InputType } from '@nestjs/graphql'; @InputType() export class RemoveCommunityRoleFromVirtualInput { @Field(() => UUID, { nullable: false }) - communityID!: string; + roleManagerID!: string; @Field(() => UUID_NAMEID, { nullable: false }) virtualContributorID!: string; diff --git a/src/domain/community/community/community.entity.ts b/src/domain/community/community/community.entity.ts index a9e66465a1..b272ed0ce2 100644 --- a/src/domain/community/community/community.entity.ts +++ b/src/domain/community/community/community.entity.ts @@ -10,14 +10,10 @@ import { IGroupable } from '@src/common/interfaces/groupable.interface'; import { UserGroup } from '@domain/community/user-group/user-group.entity'; import { ICommunity } from '@domain/community/community/community.interface'; import { AuthorizableEntity } from '@domain/common/entity/authorizable-entity'; -import { Application } from '@domain/community/application/application.entity'; import { Communication } from '@domain/communication/communication/communication.entity'; import { UUID_LENGTH } from '@src/common/constants/entity.field.length.constants'; -import { CommunityPolicy } from '../community-policy/community.policy.entity'; -import { Form } from '@domain/common/form/form.entity'; -import { Invitation } from '../invitation/invitation.entity'; import { CommunityGuidelines } from '../community-guidelines/community.guidelines.entity'; -import { PlatformInvitation } from '@platform/invitation/platform.invitation.entity'; +import { RoleManager } from '@domain/access/role-manager'; @Entity() export class Community @@ -40,49 +36,19 @@ export class Community @JoinColumn() guidelines?: CommunityGuidelines; - @OneToOne(() => Form, { - eager: false, - cascade: true, - onDelete: 'SET NULL', - }) - @JoinColumn() - applicationForm?: Form; - @OneToMany(() => UserGroup, userGroup => userGroup.community, { eager: false, cascade: true, }) groups?: UserGroup[]; - @OneToMany(() => Application, application => application.community, { - eager: false, - cascade: true, - }) - applications?: Application[]; - - @OneToMany(() => Invitation, invitation => invitation.community, { + @OneToOne(() => RoleManager, { eager: false, cascade: true, - }) - invitations?: Invitation[]; - - @OneToMany( - () => PlatformInvitation, - platformInvitation => platformInvitation.community, - { - eager: false, - cascade: true, - } - ) - platformInvitations?: PlatformInvitation[]; - - @OneToOne(() => CommunityPolicy, { - eager: true, - cascade: true, onDelete: 'SET NULL', }) @JoinColumn() - policy!: CommunityPolicy; + roleManager!: RoleManager; // The parent community can have many child communities; the relationship is controlled by the child. @ManyToOne(() => Community, { diff --git a/src/domain/community/community/community.interface.ts b/src/domain/community/community/community.interface.ts index ec05be2c72..c7dc692b1d 100644 --- a/src/domain/community/community/community.interface.ts +++ b/src/domain/community/community/community.interface.ts @@ -1,14 +1,10 @@ -import { IApplication } from '@domain/community/application/application.interface'; import { IUserGroup } from '@domain/community/user-group/user-group.interface'; import { ObjectType } from '@nestjs/graphql'; import { IGroupable } from '@domain/common/interfaces/groupable.interface'; import { IAuthorizable } from '@domain/common/entity/authorizable-entity'; import { ICommunication } from '@domain/communication/communication'; -import { ICommunityPolicy } from '../community-policy/community.policy.interface'; -import { IForm } from '@domain/common/form/form.interface'; -import { IInvitation } from '../invitation/invitation.interface'; import { ICommunityGuidelines } from '../community-guidelines/community.guidelines.interface'; -import { IPlatformInvitation } from '@platform/invitation'; +import { IRoleManager } from '@domain/access/role-manager'; @ObjectType('Community', { implements: () => [IGroupable], @@ -16,15 +12,10 @@ import { IPlatformInvitation } from '@platform/invitation'; export abstract class ICommunity extends IAuthorizable { groups?: IUserGroup[]; - applications?: IApplication[]; - invitations?: IInvitation[]; - platformInvitations?: IPlatformInvitation[]; - - applicationForm?: IForm; - parentCommunity?: ICommunity; - policy!: ICommunityPolicy; + roleManager!: IRoleManager; + guidelines?: ICommunityGuidelines; communication?: ICommunication; diff --git a/src/domain/community/community/community.module.ts b/src/domain/community/community/community.module.ts index 5373d1319e..48be6be2ec 100644 --- a/src/domain/community/community/community.module.ts +++ b/src/domain/community/community/community.module.ts @@ -6,21 +6,17 @@ import { UserGroupModule } from '@domain/community/user-group/user-group.module' import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { TrustRegistryAdapterModule } from '@services/external/trust-registry/trust.registry.adapter/trust.registry.adapter.module'; -import { CommunityPolicyModule } from '../community-policy/community.policy.module'; import { Community } from './community.entity'; import { CommunityResolverFields } from './community.resolver.fields'; import { CommunityResolverMutations } from './community.resolver.mutations'; import { CommunityService } from './community.service'; import { CommunityAuthorizationService } from './community.service.authorization'; -import { FormModule } from '@domain/common/form/form.module'; import { StorageAggregatorResolverModule } from '@services/infrastructure/storage-aggregator-resolver/storage.aggregator.resolver.module'; import { CommunityGuidelinesModule } from '../community-guidelines/community.guidelines.module'; import { LicenseEngineModule } from '@core/license-engine/license.engine.module'; -import { InvitationModule } from '../invitation/invitation.module'; -import { ApplicationModule } from '../application/application.module'; -import { PlatformInvitationModule } from '@platform/invitation/platform.invitation.module'; import { EntityResolverModule } from '@services/infrastructure/entity-resolver/entity.resolver.module'; import { VirtualContributorModule } from '../virtual-contributor/virtual.contributor.module'; +import { RoleManagerModule } from '@domain/access/role-manager/role.manager.module'; @Module({ imports: [ @@ -29,16 +25,12 @@ import { VirtualContributorModule } from '../virtual-contributor/virtual.contrib AgentModule, EntityResolverModule, UserGroupModule, + RoleManagerModule, CommunicationModule, - CommunityPolicyModule, CommunityGuidelinesModule, LicenseEngineModule, AgentModule, StorageAggregatorResolverModule, - FormModule, - InvitationModule, - ApplicationModule, - PlatformInvitationModule, VirtualContributorModule, TypeOrmModule.forFeature([Community]), TrustRegistryAdapterModule, diff --git a/src/domain/community/community/community.resolver.fields.ts b/src/domain/community/community/community.resolver.fields.ts index cb04e150e1..54e13e7c3a 100644 --- a/src/domain/community/community/community.resolver.fields.ts +++ b/src/domain/community/community/community.resolver.fields.ts @@ -7,10 +7,9 @@ import { CommunityService } from './community.service'; import { IUserGroup } from '@domain/community/user-group'; import { AuthorizationPrivilege } from '@common/enums'; import { ICommunication } from '@domain/communication/communication/communication.interface'; -import { ICommunityPolicy } from '../community-policy/community.policy.interface'; -import { IForm } from '@domain/common/form/form.interface'; import { UUID } from '@domain/common/scalars/scalar.uuid'; import { ICommunityGuidelines } from '../community-guidelines/community.guidelines.interface'; +import { IRoleManager } from '@domain/access/role-manager'; @Resolver(() => ICommunity) export class CommunityResolverFields { @@ -40,16 +39,6 @@ export class CommunityResolverFields { return await this.communityService.getUserGroup(community, groupID); } - @UseGuards(GraphqlGuard) - @ResolveField('applicationForm', () => IForm, { - nullable: false, - description: 'The Form used for Applications to this community.', - }) - @Profiling.api - async applicationForm(@Parent() community: Community): Promise { - return await this.communityService.getApplicationForm(community); - } - @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) @UseGuards(GraphqlGuard) @ResolveField('communication', () => ICommunication, { @@ -65,12 +54,12 @@ export class CommunityResolverFields { @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) @UseGuards(GraphqlGuard) - @ResolveField('policy', () => ICommunityPolicy, { + @ResolveField('roleManager', () => IRoleManager, { nullable: false, - description: 'The policy that defines the roles for this Community.', + description: 'The RoleManager for this Community.', }) - async policy(@Parent() community: Community): Promise { - return this.communityService.getCommunityPolicy(community); + async policy(@Parent() community: Community): Promise { + return this.communityService.getRoleManager(community); } @UseGuards(GraphqlGuard) diff --git a/src/domain/community/community/community.resolver.mutations.ts b/src/domain/community/community/community.resolver.mutations.ts index 742b1f69f1..94179c953c 100644 --- a/src/domain/community/community/community.resolver.mutations.ts +++ b/src/domain/community/community/community.resolver.mutations.ts @@ -3,18 +3,13 @@ import { Args, Mutation, Resolver } from '@nestjs/graphql'; import { IUserGroup } from '@domain/community/user-group'; import { CommunityService } from './community.service'; import { CurrentUser, Profiling } from '@src/common/decorators'; -import { ICommunity } from '@domain/community/community/community.interface'; import { GraphqlGuard } from '@core/authorization'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { AuthorizationPrivilege } from '@common/enums'; import { AuthorizationService } from '@core/authorization/authorization.service'; import { UserGroupAuthorizationService } from '../user-group/user-group.service.authorization'; import { AgentService } from '@domain/agent/agent/agent.service'; -import { CommunityMemberClaim } from '@services/external/trust-registry/trust.registry.claim/claim.community.member'; -import { AgentBeginVerifiedCredentialOfferOutput } from '@domain/agent/agent/dto/agent.dto.verified.credential.offer.begin.output'; -import { AlkemioUserClaim } from '@services/external/trust-registry/trust.registry.claim/claim.alkemio.user'; import { CreateUserGroupInput } from '../user-group/dto'; -import { UpdateCommunityApplicationFormInput } from './dto/community.dto.update.application.form'; import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service'; @Resolver() @@ -54,68 +49,4 @@ export class CommunityResolverMutations { await this.authorizationPolicyService.saveAll(authorizations); return group; } - - @UseGuards(GraphqlGuard) - @Mutation(() => ICommunity, { - description: 'Update the Application Form used by this Community.', - }) - @Profiling.api - async updateCommunityApplicationForm( - @CurrentUser() agentInfo: AgentInfo, - @Args('applicationFormData') - applicationFormData: UpdateCommunityApplicationFormInput - ): Promise { - const community = await this.communityService.getCommunityOrFail( - applicationFormData.communityID - ); - - await this.authorizationService.grantAccessOrFail( - agentInfo, - community.authorization, - AuthorizationPrivilege.UPDATE, - `update community application form: ${community.id}` - ); - - return await this.communityService.updateApplicationForm( - community, - applicationFormData.formData - ); - } - - @UseGuards(GraphqlGuard) - @Mutation(() => AgentBeginVerifiedCredentialOfferOutput, { - description: 'Generate community member credential offer', - }) - async beginCommunityMemberVerifiedCredentialOfferInteraction( - @Args({ name: 'communityID', type: () => String }) communityID: string, - @CurrentUser() agentInfo: AgentInfo - ): Promise { - const community = - await this.communityService.getCommunityOrFail(communityID); - await this.authorizationService.grantAccessOrFail( - agentInfo, - community.authorization, - AuthorizationPrivilege.READ, - `beginCommunityMemberCredentialOfferInteraction: ${community.id}` - ); - - return await this.agentService.beginCredentialOfferInteraction( - agentInfo.agentID, - [ - { - type: 'CommunityMemberCredential', - claims: [ - new AlkemioUserClaim({ - userID: agentInfo.userID, - email: agentInfo.email, - }), - new CommunityMemberClaim({ - communityID: community.id, - communityDisplayName: community.id, - }), - ], - }, - ] - ); - } } diff --git a/src/domain/community/community/community.service.authorization.ts b/src/domain/community/community/community.service.authorization.ts index 5b4963599f..1141f5e69c 100644 --- a/src/domain/community/community/community.service.authorization.ts +++ b/src/domain/community/community/community.service.authorization.ts @@ -10,7 +10,6 @@ import { AuthorizationPolicyService } from '@domain/common/authorization-policy/ import { IAuthorizationPolicy } from '@domain/common/authorization-policy/authorization.policy.interface'; import { UserGroupAuthorizationService } from '../user-group/user-group.service.authorization'; import { CommunicationAuthorizationService } from '@domain/communication/communication/communication.service.authorization'; -import { ApplicationAuthorizationService } from '../application/application.service.authorization'; import { AuthorizationPolicyRuleVerifiedCredential } from '@core/authorization/authorization.policy.rule.verified.credential'; import { IAuthorizationPolicyRuleCredential } from '@core/authorization/authorization.policy.rule.credential.interface'; import { @@ -29,22 +28,21 @@ import { CREDENTIAL_RULE_SUBSPACE_PARENT_MEMBER_JOIN, CREDENTIAL_RULE_COMMUNITY_ADD_MEMBER, } from '@common/constants'; -import { InvitationAuthorizationService } from '../invitation/invitation.service.authorization'; import { RelationshipNotFoundException } from '@common/exceptions/relationship.not.found.exception'; import { CommunityGuidelinesAuthorizationService } from '../community-guidelines/community.guidelines.service.authorization'; -import { CommunityPolicyService } from '../community-policy/community.policy.service'; import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; -import { ICommunityPolicy } from '../community-policy/community.policy.interface'; import { CommunityRoleType } from '@common/enums/community.role'; import { LicenseEngineService } from '@core/license-engine/license.engine.service'; import { LicensePrivilege } from '@common/enums/license.privilege'; import { AuthorizationPolicyRulePrivilege } from '@core/authorization/authorization.policy.rule.privilege'; import { IAgent } from '@domain/agent'; -import { PlatformInvitationAuthorizationService } from '@platform/invitation/platform.invitation.service.authorization'; import { VirtualContributorService } from '../virtual-contributor/virtual.contributor.service'; import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; import { CommunityMembershipPolicy } from '@common/enums/community.membership.policy'; import { EntityNotInitializedException } from '@common/exceptions/entity.not.initialized.exception'; +import { RoleManagerAuthorizationService } from '@domain/access/role-manager/role.manager.service.authorization'; +import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; +import { IRoleManager } from '@domain/access/role-manager'; @Injectable() export class CommunityAuthorizationService { @@ -54,35 +52,29 @@ export class CommunityAuthorizationService { private authorizationPolicyService: AuthorizationPolicyService, private userGroupAuthorizationService: UserGroupAuthorizationService, private communicationAuthorizationService: CommunicationAuthorizationService, - private applicationAuthorizationService: ApplicationAuthorizationService, - private invitationAuthorizationService: InvitationAuthorizationService, - private communityPolicyService: CommunityPolicyService, private virtualContributorService: VirtualContributorService, - private platformInvitationAuthorizationService: PlatformInvitationAuthorizationService, + private roleManagerService: RoleManagerService, + private roleManagerAuthorizationService: RoleManagerAuthorizationService, private communityGuidelinesAuthorizationService: CommunityGuidelinesAuthorizationService ) {} async applyAuthorizationPolicy( - communityInput: ICommunity, + communityID: string, parentAuthorization: IAuthorizationPolicy, levelZeroSpaceAgent: IAgent, - communityPolicy: ICommunityPolicy, spaceSettings: ISpaceSettings, spaceMembershipAllowed: boolean, isSubspace: boolean ): Promise { const community = await this.communityService.getCommunityOrFail( - communityInput.id, + communityID, { relations: { communication: { updates: true, }, - policy: true, + roleManager: true, groups: true, - applications: true, - invitations: true, - platformInvitations: true, guidelines: { profile: true, }, @@ -92,11 +84,8 @@ export class CommunityAuthorizationService { if ( !community.communication || !community.communication.updates || - !community.policy || - !community.groups || - !community.applications || - !community.invitations || - !community.platformInvitations + !community.roleManager || + !community.groups ) { throw new RelationshipNotFoundException( `Unable to load child entities for community authorization: ${community.id} `, @@ -119,7 +108,7 @@ export class CommunityAuthorizationService { community.authorization, parentAuthorization?.anonymousReadAccess, levelZeroSpaceAgent, - communityPolicy, + community.roleManager, spaceSettings ); community.authorization = this.appendVerifiedCredentialRules( @@ -128,14 +117,14 @@ export class CommunityAuthorizationService { if (spaceMembershipAllowed) { community.authorization = this.extendCommunityAuthorizationPolicySpace( community.authorization, - communityPolicy, + community.roleManager, spaceSettings ); } if (isSubspace) { community.authorization = this.extendAuthorizationPolicySubspace( community.authorization, - communityPolicy, + community.roleManager, spaceSettings ); } @@ -162,32 +151,16 @@ export class CommunityAuthorizationService { updatedAuthorizations.push(...groupAuthorizations); } - for (const application of community.applications) { - const applicationAuthReset = - await this.applicationAuthorizationService.applyAuthorizationPolicy( - application, - community.authorization - ); - application.authorization = applicationAuthReset; - } - - for (const invitation of community.invitations) { - const invitationReset = - await this.invitationAuthorizationService.applyAuthorizationPolicy( - invitation, - community.authorization - ); - invitation.authorization = invitationReset; - } - - for (const externalInvitation of community.platformInvitations) { - const platformInvitationAuthorization = - await this.platformInvitationAuthorizationService.applyAuthorizationPolicy( - externalInvitation, - community.authorization - ); - updatedAuthorizations.push(platformInvitationAuthorization); - } + const roleManagerAuthorizations = + await this.roleManagerAuthorizationService.applyAuthorizationPolicy( + community.roleManager.id, + community.authorization, + levelZeroSpaceAgent, + spaceSettings, + spaceMembershipAllowed, + isSubspace + ); + updatedAuthorizations.push(...roleManagerAuthorizations); if (community.guidelines) { const guidelineAuthorizations = @@ -203,12 +176,12 @@ export class CommunityAuthorizationService { private extendCommunityAuthorizationPolicySpace( communityAuthorization: IAuthorizationPolicy | undefined, - policy: ICommunityPolicy, + roleManager: IRoleManager, spaceSettings: ISpaceSettings ): IAuthorizationPolicy { if (!communityAuthorization) throw new EntityNotInitializedException( - `Authorization definition not found for: ${JSON.stringify(policy)}`, + `Authorization definition not found for: ${JSON.stringify(roleManager)}`, LogContext.SPACES ); @@ -266,7 +239,7 @@ export class CommunityAuthorizationService { authorization: IAuthorizationPolicy | undefined, allowGlobalRegisteredReadAccess: boolean | undefined, levelZeroSpaceAgent: IAgent, - policy: ICommunityPolicy, + roleManager: IRoleManager, spaceSettings: ISpaceSettings ): Promise { const newRules: IAuthorizationPolicyRuleCredential[] = []; @@ -283,16 +256,16 @@ export class CommunityAuthorizationService { newRules.push(globalAdminAddMembers); const inviteMembersCriterias: ICredentialDefinition[] = - this.communityPolicyService.getCredentialsForRoleWithParents( - policy, - spaceSettings, - CommunityRoleType.ADMIN + this.roleManagerService.getCredentialsForRoleWithParents( + roleManager, + CommunityRoleType.ADMIN, + spaceSettings ); if (spaceSettings.membership.allowSubspaceAdminsToInviteMembers) { // use the member credential to create subspace admin credential const subspaceAdminCredential: ICredentialDefinition = - this.communityPolicyService.getCredentialForRole( - policy, + this.roleManagerService.getCredentialForRole( + roleManager, CommunityRoleType.MEMBER ); subspaceAdminCredential.type = @@ -328,10 +301,10 @@ export class CommunityAuthorizationService { ); if (accessVirtualContributors) { const criterias: ICredentialDefinition[] = - this.communityPolicyService.getCredentialsForRoleWithParents( - policy, - spaceSettings, - CommunityRoleType.ADMIN + this.roleManagerService.getCredentialsForRoleWithParents( + roleManager, + CommunityRoleType.ADMIN, + spaceSettings ); criterias.push({ type: AuthorizationCredential.GLOBAL_ADMIN, @@ -359,7 +332,7 @@ export class CommunityAuthorizationService { private extendAuthorizationPolicySubspace( authorization: IAuthorizationPolicy | undefined, - policy: ICommunityPolicy, + roleManager: IRoleManager, spaceSettings: ISpaceSettings ): IAuthorizationPolicy { if (!authorization) @@ -371,8 +344,8 @@ export class CommunityAuthorizationService { const newRules: IAuthorizationPolicyRuleCredential[] = []; const parentCommunityCredential = - this.communityPolicyService.getDirectParentCredentialForRole( - policy, + this.roleManagerService.getDirectParentCredentialForRole( + roleManager, CommunityRoleType.MEMBER ); @@ -404,10 +377,10 @@ export class CommunityAuthorizationService { } const adminCredentials = - this.communityPolicyService.getCredentialsForRoleWithParents( - policy, - spaceSettings, - CommunityRoleType.ADMIN + this.roleManagerService.getCredentialsForRoleWithParents( + roleManager, + CommunityRoleType.ADMIN, + spaceSettings ); const addMembers = this.authorizationPolicyService.createCredentialRule( diff --git a/src/domain/community/community/community.service.ts b/src/domain/community/community/community.service.ts index 4caa50b5c3..fe0c0f0f0c 100644 --- a/src/domain/community/community/community.service.ts +++ b/src/domain/community/community/community.service.ts @@ -11,21 +11,11 @@ import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; import { FindOneOptions, FindOptionsRelations, Repository } from 'typeorm'; import { CreateUserGroupInput } from '@domain/community/user-group/dto'; import { Community, ICommunity } from '@domain/community/community'; -import { ApplicationService } from '@domain/community/application/application.service'; import { AuthorizationPolicy } from '@domain/common/authorization-policy'; import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service'; import { CommunicationService } from '@domain/communication/communication/communication.service'; import { ICommunication } from '@domain/communication/communication'; import { LogContext } from '@common/enums/logging.context'; -import { CommunityRoleType } from '@common/enums/community.role'; -import { ICommunityRolePolicy } from '../community-policy/community.policy.role.interface'; -import { ICommunityPolicy } from '../community-policy/community.policy.interface'; -import { CommunityPolicyService } from '../community-policy/community.policy.service'; -import { ICommunityPolicyDefinition } from '../community-policy/community.policy.definition'; -import { IForm } from '@domain/common/form/form.interface'; -import { FormService } from '@domain/common/form/form.service'; -import { UpdateFormInput } from '@domain/common/form/dto/form.dto.update'; -import { InvitationService } from '../invitation/invitation.service'; import { CommunityResolverService } from '@services/infrastructure/entity-resolver/community.resolver.service'; import { StorageAggregatorResolverService } from '@services/infrastructure/storage-aggregator-resolver/storage.aggregator.resolver.service'; import { CommunityGuidelinesService } from '../community-guidelines/community.guidelines.service'; @@ -33,23 +23,20 @@ import { IStorageAggregator } from '@domain/storage/storage-aggregator/storage.a import { CreateCommunityInput } from './dto/community.dto.create'; import { ICommunityGuidelines } from '../community-guidelines/community.guidelines.interface'; import { IContributor } from '../contributor/contributor.interface'; -import { PlatformInvitationService } from '@platform/invitation/platform.invitation.service'; import { IUser } from '../user/user.interface'; import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type'; +import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; +import { IRoleManager } from '@domain/access/role-manager'; @Injectable() export class CommunityService { constructor( private authorizationPolicyService: AuthorizationPolicyService, private userGroupService: UserGroupService, - private applicationService: ApplicationService, - private invitationService: InvitationService, - private platformInvitationService: PlatformInvitationService, private communicationService: CommunicationService, private communityResolverService: CommunityResolverService, private communityGuidelinesService: CommunityGuidelinesService, - private formService: FormService, - private communityPolicyService: CommunityPolicyService, + private roleManagerService: RoleManagerService, private storageAggregatorResolverService: StorageAggregatorResolverService, @InjectRepository(Community) private communityRepository: Repository, @@ -64,11 +51,8 @@ export class CommunityService { community.authorization = new AuthorizationPolicy( AuthorizationPolicyType.COMMUNITY ); - const policy = communityData.policy as ICommunityPolicyDefinition; - community.policy = this.communityPolicyService.createCommunityPolicy( - policy.member, - policy.lead, - policy.admin + community.roleManager = await this.roleManagerService.createRoleManager( + communityData.roleManagerData ); community.guidelines = @@ -76,13 +60,6 @@ export class CommunityService { communityData.guidelines, storageAggregator ); - community.applicationForm = this.formService.createForm( - communityData.applicationForm - ); - - community.applications = []; - community.invitations = []; - community.platformInvitations = []; community.groups = []; community.communication = @@ -137,7 +114,7 @@ export class CommunityService { // Loads the group into the Community entity if not already present async getUserGroup( - community: ICommunity, + community: IRoleManager, groupID: string ): Promise { const communityWithGroups = await this.getCommunityOrFail(community.id, { @@ -182,25 +159,18 @@ export class CommunityService { // Note need to load it in with all contained entities so can remove fully const community = await this.getCommunityOrFail(communityID, { relations: { - applications: true, - invitations: true, - platformInvitations: true, + roleManager: true, groups: true, communication: true, - applicationForm: true, guidelines: true, }, }); if ( !community.communication || !community.communication.updates || - !community.policy || + !community.roleManager || !community.groups || - !community.applications || - !community.invitations || - !community.platformInvitations || - !community.guidelines || - !community.applicationForm + !community.guidelines ) { throw new RelationshipNotFoundException( `Unable to load child entities for community for deletion: ${community.id} `, @@ -218,33 +188,11 @@ export class CommunityService { if (community.authorization) await this.authorizationPolicyService.delete(community.authorization); - // Remove all applications - for (const application of community.applications) { - await this.applicationService.deleteApplication({ - ID: application.id, - }); - } - - // Remove all invitations - for (const invitation of community.invitations) { - await this.invitationService.deleteInvitation({ - ID: invitation.id, - }); - } - - for (const externalInvitation of community.platformInvitations) { - await this.platformInvitationService.deletePlatformInvitation({ - ID: externalInvitation.id, - }); - } - await this.communicationService.removeCommunication( community.communication.id ); - await this.formService.removeForm(community.applicationForm); - - await this.communityPolicyService.removeCommunityPolicy(community.policy); + await this.roleManagerService.removeRoleManager(community.roleManager.id); await this.communityGuidelinesService.deleteCommunityGuidelines( community.guidelines.id @@ -254,7 +202,7 @@ export class CommunityService { return true; } - async save(community: ICommunity): Promise { + async save(community: IRoleManager): Promise { return await this.communityRepository.save(community); } @@ -272,18 +220,6 @@ export class CommunityService { return undefined; } - async updateApplicationForm( - community: ICommunity, - formData: UpdateFormInput - ): Promise { - const applicationForm = await this.getApplicationForm(community); - community.applicationForm = await this.formService.updateForm( - applicationForm, - formData - ); - return await this.save(community); - } - public setParentCommunity( community?: ICommunity, parentCommunity?: ICommunity @@ -296,16 +232,15 @@ export class CommunityService { } community.parentCommunity = parentCommunity; // Also update the communityPolicy - community.policy = this.communityPolicyService.inheritParentCredentials( - this.getCommunityPolicy(parentCommunity), - this.getCommunityPolicy(community) + community.roleManager = this.roleManagerService.inheritParentCredentials( + community.roleManager ); return community; } public async getDisplayName(community: ICommunity): Promise { - return await this.communityResolverService.getDisplayNameForCommunityOrFail( + return await this.communityResolverService.getDisplayNameForRoleManagerOrFail( community.id ); } @@ -346,15 +281,21 @@ export class CommunityService { ); } - public getCommunityPolicy(community: ICommunity): ICommunityPolicy { - const policy = community.policy; - if (!policy) { + public async getRoleManager(community: ICommunity): Promise { + const communityWithRoleManager = await this.getCommunityOrFail( + community.id, + { + relations: { roleManager: true }, + } + ); + + if (!communityWithRoleManager.roleManager) { throw new EntityNotInitializedException( - `Unable to locate policy for community: ${community.id}`, + `Unable to locate Role Manager for community: ${community.id}`, LogContext.COMMUNITY ); } - return policy; + return communityWithRoleManager.roleManager; } public async getCommunityGuidelines( @@ -394,22 +335,6 @@ export class CommunityService { return communication; } - public async getPeerCommunites( - parentCommunity: ICommunity, - childCommunity: ICommunity - ): Promise { - const peerCommunities = await this.communityRepository.find({ - where: { - parentCommunity: { - id: parentCommunity.id, - }, - }, - }); - return peerCommunities.filter( - community => community.id !== childCommunity.id - ); - } - public async isCommunityAccountMatchingVcAccount( communityID: string, virtualContributorID: string @@ -421,46 +346,13 @@ export class CommunityService { } public async getLevelZeroSpaceIdForCommunity( - community: ICommunity + community: IRoleManager ): Promise { return await this.communityResolverService.getLevelZeroSpaceIdForCommunity( community.id ); } - public getCommunityPolicyForRole( - community: ICommunity, - role: CommunityRoleType - ): ICommunityRolePolicy { - const policy = this.getCommunityPolicy(community); - return this.communityPolicyService.getCommunityRolePolicy(policy, role); - } - - public updateCommunityPolicyResourceID( - community: ICommunity, - resourceID: string - ): ICommunityPolicy { - const policy = this.getCommunityPolicy(community); - return this.communityPolicyService.updateCommunityPolicyResourceID( - policy, - resourceID - ); - } - - async getApplicationForm(community: ICommunity): Promise { - const communityForm = await this.getCommunityOrFail(community.id, { - relations: { applicationForm: true }, - }); - const applicationForm = communityForm.applicationForm; - if (!applicationForm) { - throw new EntityNotFoundException( - `Unable to find Application Form for Community with ID: ${community.id}`, - LogContext.COMMUNITY - ); - } - return applicationForm; - } - async isSpaceCommunity(community: ICommunity): Promise { const parentCommunity = await this.getParentCommunity(community); diff --git a/src/domain/community/community/dto/community.dto.create.ts b/src/domain/community/community/dto/community.dto.create.ts index 21bf668d6e..154a17eb6a 100644 --- a/src/domain/community/community/dto/community.dto.create.ts +++ b/src/domain/community/community/dto/community.dto.create.ts @@ -1,11 +1,10 @@ -import { CreateFormInput } from '@domain/common/form/dto/form.dto.create'; +import { CreateRoleManagerInput } from '@domain/access/role-manager/dto/role.manager.dto.create'; import { CreateCommunityGuidelinesInput } from '@domain/community/community-guidelines/dto/community.guidelines.dto.create'; -import { ICommunityPolicyDefinition } from '@domain/community/community-policy/community.policy.definition'; export class CreateCommunityInput { guidelines!: CreateCommunityGuidelinesInput; name!: string; - policy!: ICommunityPolicyDefinition; - applicationForm!: CreateFormInput; + + roleManagerData!: CreateRoleManagerInput; } diff --git a/src/domain/community/invitation/dto/invitation.dto.create.ts b/src/domain/community/invitation/dto/invitation.dto.create.ts index 799cc3641c..ae33b23993 100644 --- a/src/domain/community/invitation/dto/invitation.dto.create.ts +++ b/src/domain/community/invitation/dto/invitation.dto.create.ts @@ -20,6 +20,6 @@ export class CreateInvitationInput { createdBy!: string; - communityID!: string; + roleManagerID!: string; invitedToParent!: boolean; } diff --git a/src/domain/community/invitation/invitation.entity.ts b/src/domain/community/invitation/invitation.entity.ts index f6f30e0939..266aad9e5e 100644 --- a/src/domain/community/invitation/invitation.entity.ts +++ b/src/domain/community/invitation/invitation.entity.ts @@ -1,5 +1,4 @@ import { Column, Entity, JoinColumn, ManyToOne, OneToOne } from 'typeorm'; -import { Community } from '@domain/community/community/community.entity'; import { Lifecycle } from '@domain/common/lifecycle/lifecycle.entity'; import { IInvitation } from './invitation.interface'; import { AuthorizableEntity } from '@domain/common/entity/authorizable-entity'; @@ -17,13 +16,6 @@ export class Invitation extends AuthorizableEntity implements IInvitation { @JoinColumn() lifecycle!: Lifecycle; - @ManyToOne(() => Community, community => community.invitations, { - eager: false, - cascade: false, - onDelete: 'CASCADE', - }) - community?: Community; - @Column('char', { length: UUID_LENGTH, nullable: false }) invitedContributor!: string; diff --git a/src/domain/community/invitation/invitation.interface.ts b/src/domain/community/invitation/invitation.interface.ts index 06c72eb25d..1fd82c3799 100644 --- a/src/domain/community/invitation/invitation.interface.ts +++ b/src/domain/community/invitation/invitation.interface.ts @@ -1,15 +1,15 @@ import { ILifecycle } from '@domain/common/lifecycle/lifecycle.interface'; -import { ICommunity } from '@domain/community/community/community.interface'; import { Field, ObjectType } from '@nestjs/graphql'; import { IAuthorizable } from '@domain/common/entity/authorizable-entity'; import { CommunityContributorType } from '@common/enums/community.contributor.type'; +import { IRoleManager } from '@domain/access/role-manager'; @ObjectType('Invitation') export class IInvitation extends IAuthorizable { invitedContributor!: string; createdBy!: string; - community?: ICommunity; + roleManager?: IRoleManager; @Field(() => ILifecycle, { nullable: false }) lifecycle!: ILifecycle; diff --git a/src/domain/community/invitation/invitation.service.ts b/src/domain/community/invitation/invitation.service.ts index c00a928be9..479e1bd001 100644 --- a/src/domain/community/invitation/invitation.service.ts +++ b/src/domain/community/invitation/invitation.service.ts @@ -133,14 +133,14 @@ export class InvitationService { async findExistingInvitations( contributorID: string, - communityID: string + roleManagerID: string ): Promise { const existingInvitations = await this.invitationRepository.find({ where: { invitedContributor: contributorID, - community: { id: communityID }, + roleManager: { id: roleManagerID }, }, - relations: { community: true }, + relations: { roleManager: true }, }); if (existingInvitations.length > 0) return existingInvitations; @@ -152,7 +152,7 @@ export class InvitationService { states: string[] = [] ): Promise { const findOpts: FindManyOptions = { - relations: { community: true }, + relations: { roleManager: true }, where: { invitedContributor: contributorID }, }; diff --git a/src/domain/space/space.defaults/definitions/space.community.policy.ts b/src/domain/space/space.defaults/definitions/space.community.policy.ts deleted file mode 100644 index e897fe49d4..0000000000 --- a/src/domain/space/space.defaults/definitions/space.community.policy.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { AuthorizationCredential } from '@common/enums'; -import { ICommunityPolicyDefinition } from '@domain/community/community-policy/community.policy.definition'; - -export const spaceCommunityPolicy: ICommunityPolicyDefinition = { - member: { - enabled: true, - credential: { - type: AuthorizationCredential.SPACE_MEMBER, - resourceID: '', - }, - parentCredentials: [], - minOrg: 0, - maxOrg: -1, - minUser: 0, - maxUser: -1, - }, - lead: { - enabled: true, - credential: { - type: AuthorizationCredential.SPACE_LEAD, - resourceID: '', - }, - parentCredentials: [], - minOrg: 0, - maxOrg: 2, - minUser: 0, - maxUser: 2, - }, - - admin: { - enabled: true, - credential: { - type: AuthorizationCredential.SPACE_ADMIN, - resourceID: '', - }, - parentCredentials: [], - minOrg: 0, - maxOrg: 0, - minUser: 0, - maxUser: -1, - }, -}; diff --git a/src/domain/space/space.defaults/definitions/space.community.application.form.ts b/src/domain/space/space.defaults/definitions/space.role.manager.application.form.ts similarity index 100% rename from src/domain/space/space.defaults/definitions/space.community.application.form.ts rename to src/domain/space/space.defaults/definitions/space.role.manager.application.form.ts diff --git a/src/domain/space/space.defaults/definitions/space.role.manager.roles.ts b/src/domain/space/space.defaults/definitions/space.role.manager.roles.ts new file mode 100644 index 0000000000..a160b9b660 --- /dev/null +++ b/src/domain/space/space.defaults/definitions/space.role.manager.roles.ts @@ -0,0 +1,81 @@ +import { AuthorizationCredential } from '@common/enums'; +import { CommunityRoleType } from '@common/enums/community.role'; +import { CreateRoleInput } from '@domain/access/role/dto/role.dto.create'; + +export const spaceCommunityRoles: CreateRoleInput[] = [ + { + type: CommunityRoleType.MEMBER, + requireBaseRole: false, + requireParentRole: true, + credentialData: { + type: AuthorizationCredential.SPACE_MEMBER, + resourceID: '', + }, + parentCredentialsData: [], + userPolicyData: { + enabled: true, + minimum: 0, + maximum: -1, + }, + organizationPolicyData: { + enabled: true, + minimum: 0, + maximum: -1, + }, + virtualContributorPolicyData: { + enabled: true, + minimum: 0, + maximum: -1, + }, + }, + { + type: CommunityRoleType.LEAD, + requireBaseRole: true, + requireParentRole: false, + credentialData: { + type: AuthorizationCredential.SPACE_LEAD, + resourceID: '', + }, + parentCredentialsData: [], + userPolicyData: { + enabled: true, + minimum: 0, + maximum: 2, + }, + organizationPolicyData: { + enabled: true, + minimum: 0, + maximum: 2, + }, + virtualContributorPolicyData: { + enabled: true, + minimum: 0, + maximum: 1, + }, + }, + { + type: CommunityRoleType.ADMIN, + requireBaseRole: true, + requireParentRole: false, + credentialData: { + type: AuthorizationCredential.SPACE_ADMIN, + resourceID: '', + }, + parentCredentialsData: [], + userPolicyData: { + enabled: true, + minimum: 0, + maximum: -1, + }, + organizationPolicyData: { + enabled: true, + minimum: 0, + maximum: 0, + }, + virtualContributorPolicyData: { + enabled: true, + minimum: 0, + maximum: 0, + }, + }, +]; diff --git a/src/domain/space/space.defaults/definitions/subspace.community.policy.ts b/src/domain/space/space.defaults/definitions/subspace.community.policy.ts deleted file mode 100644 index a564caa2b3..0000000000 --- a/src/domain/space/space.defaults/definitions/subspace.community.policy.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { AuthorizationCredential } from '@common/enums'; -import { ICommunityPolicyDefinition } from '@domain/community/community-policy/community.policy.definition'; - -export const subspaceCommunityPolicy: ICommunityPolicyDefinition = { - member: { - enabled: true, - credential: { - type: AuthorizationCredential.SPACE_MEMBER, - resourceID: '', - }, - parentCredentials: [], - minOrg: 0, - maxOrg: -1, - minUser: 0, - maxUser: -1, - }, - lead: { - enabled: true, - credential: { - type: AuthorizationCredential.SPACE_LEAD, - resourceID: '', - }, - parentCredentials: [], - minOrg: 0, - maxOrg: 9, - minUser: 0, - maxUser: 2, - }, - admin: { - enabled: true, - credential: { - type: AuthorizationCredential.SPACE_ADMIN, - resourceID: '', - }, - parentCredentials: [], - minOrg: 0, - maxOrg: 0, - minUser: 0, - maxUser: -1, - }, -}; diff --git a/src/domain/space/space.defaults/definitions/subspace.community.application.form.ts b/src/domain/space/space.defaults/definitions/subspace.role.manager.application.form.ts similarity index 100% rename from src/domain/space/space.defaults/definitions/subspace.community.application.form.ts rename to src/domain/space/space.defaults/definitions/subspace.role.manager.application.form.ts diff --git a/src/domain/space/space.defaults/definitions/subspace.role.manager.roles.ts b/src/domain/space/space.defaults/definitions/subspace.role.manager.roles.ts new file mode 100644 index 0000000000..218e3827c6 --- /dev/null +++ b/src/domain/space/space.defaults/definitions/subspace.role.manager.roles.ts @@ -0,0 +1,81 @@ +import { AuthorizationCredential } from '@common/enums'; +import { CommunityRoleType } from '@common/enums/community.role'; +import { CreateRoleInput } from '@domain/access/role/dto/role.dto.create'; + +export const subspaceCommunityRoles: CreateRoleInput[] = [ + { + type: CommunityRoleType.MEMBER, + requireBaseRole: false, + requireParentRole: true, + credentialData: { + type: AuthorizationCredential.SPACE_MEMBER, + resourceID: '', + }, + parentCredentialsData: [], + userPolicyData: { + enabled: true, + minimum: 0, + maximum: -1, + }, + organizationPolicyData: { + enabled: true, + minimum: 0, + maximum: -1, + }, + virtualContributorPolicyData: { + enabled: true, + minimum: 0, + maximum: -1, + }, + }, + { + type: CommunityRoleType.LEAD, + requireBaseRole: true, + requireParentRole: false, + credentialData: { + type: AuthorizationCredential.SPACE_LEAD, + resourceID: '', + }, + parentCredentialsData: [], + userPolicyData: { + enabled: true, + minimum: 0, + maximum: 2, + }, + organizationPolicyData: { + enabled: true, + minimum: 0, + maximum: 9, + }, + virtualContributorPolicyData: { + enabled: true, + minimum: 0, + maximum: 1, + }, + }, + { + type: CommunityRoleType.ADMIN, + requireBaseRole: true, + requireParentRole: false, + credentialData: { + type: AuthorizationCredential.SPACE_ADMIN, + resourceID: '', + }, + parentCredentialsData: [], + userPolicyData: { + enabled: true, + minimum: 0, + maximum: -1, + }, + organizationPolicyData: { + enabled: true, + minimum: 0, + maximum: 0, + }, + virtualContributorPolicyData: { + enabled: true, + minimum: 0, + maximum: 0, + }, + }, +]; diff --git a/src/domain/space/space.defaults/space.defaults.service.ts b/src/domain/space/space.defaults/space.defaults.service.ts index 3a834365d0..6dc1b6d518 100644 --- a/src/domain/space/space.defaults/space.defaults.service.ts +++ b/src/domain/space/space.defaults/space.defaults.service.ts @@ -11,12 +11,11 @@ import { SpaceDefaults } from './space.defaults.entity'; import { CreateCalloutInput } from '@domain/collaboration/callout/dto/callout.dto.create'; import { ISpaceSettings } from '../space.settings/space.settings.interface'; import { ICalloutGroup } from '@domain/collaboration/callout-groups/callout.group.interface'; -import { subspaceCommunityPolicy } from './definitions/subspace.community.policy'; -import { spaceCommunityPolicy } from './definitions/space.community.policy'; -import { ICommunityPolicyDefinition } from '@domain/community/community-policy/community.policy.definition'; +import { subspaceCommunityRoles } from './definitions/subspace.role.manager.roles'; +import { spaceCommunityRoles } from './definitions/space.role.manager.roles'; import { CreateFormInput } from '@domain/common/form/dto/form.dto.create'; -import { subspceCommunityApplicationForm } from './definitions/subspace.community.application.form'; -import { spaceCommunityApplicationForm } from './definitions/space.community.application.form'; +import { subspceCommunityApplicationForm } from './definitions/subspace.role.manager.application.form'; +import { spaceCommunityApplicationForm } from './definitions/space.role.manager.application.form'; import { ProfileType } from '@common/enums'; import { CalloutGroupName } from '@common/enums/callout.group.name'; import { SpaceLevel } from '@common/enums/space.level'; @@ -45,6 +44,7 @@ import { spaceDefaultsSettingsBlankSlate } from './definitions/blank-slate/space import { spaceDefaultsInnovationFlowStatesBlankSlate } from './definitions/blank-slate/space.defaults.innovation.flow.blank.slate'; import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type'; import { ITemplate } from '@domain/template/template/template.interface'; +import { CreateRoleInput } from '@domain/access/role/dto/role.dto.create'; @Injectable() export class SpaceDefaultsService { @@ -153,15 +153,15 @@ export class SpaceDefaultsService { } } - public getCommunityPolicy( + public getRoleManagerCommunityRoles( spaceLevel: SpaceLevel - ): ICommunityPolicyDefinition { + ): CreateRoleInput[] { switch (spaceLevel) { case SpaceLevel.CHALLENGE: case SpaceLevel.OPPORTUNITY: - return subspaceCommunityPolicy; + return subspaceCommunityRoles; case SpaceLevel.SPACE: - return spaceCommunityPolicy; + return spaceCommunityRoles; default: throw new EntityNotInitializedException( `Invalid space level: ${spaceLevel}`, @@ -181,7 +181,9 @@ export class SpaceDefaultsService { } } - public getCommunityApplicationForm(spaceLevel: SpaceLevel): CreateFormInput { + public getRoleManagerCommunityApplicationForm( + spaceLevel: SpaceLevel + ): CreateFormInput { switch (spaceLevel) { case SpaceLevel.CHALLENGE: case SpaceLevel.OPPORTUNITY: diff --git a/src/domain/space/space/index.ts b/src/domain/space/space/index.ts index a842c3b11f..0bfb5799ff 100644 --- a/src/domain/space/space/index.ts +++ b/src/domain/space/space/index.ts @@ -1,5 +1,5 @@ export * from './dto/space.dto.update'; export * from './dto/space.dto.delete'; export * from './dto/space.dto.create'; -export * from '../space.defaults/definitions/space.community.policy'; -export * from '../space.defaults/definitions/space.community.application.form'; +export * from '../space.defaults/definitions/space.role.manager.roles'; +export * from '../space.defaults/definitions/space.role.manager.application.form'; diff --git a/src/domain/space/space/space.module.ts b/src/domain/space/space/space.module.ts index f369a4f7ff..b79b4a33c0 100644 --- a/src/domain/space/space/space.module.ts +++ b/src/domain/space/space/space.module.ts @@ -18,7 +18,6 @@ import { ContextModule } from '@domain/context/context/context.module'; import { AgentModule } from '@domain/agent/agent/agent.module'; import { CollaborationModule } from '@domain/collaboration/collaboration/collaboration.module'; import { ProfileModule } from '@domain/common/profile/profile.module'; -import { CommunityPolicyModule } from '@domain/community/community-policy/community.policy.module'; import { CommunityModule } from '@domain/community/community/community.module'; import { StorageAggregatorModule } from '@domain/storage/storage-aggregator/storage.aggregator.module'; import { PlatformAuthorizationPolicyModule } from '@platform/authorization/platform.authorization.policy.module'; @@ -33,6 +32,7 @@ import { LicenseEngineModule } from '@core/license-engine/license.engine.module' import { LicenseIssuerModule } from '@platform/license-issuer/license.issuer.module'; import { TemplateModule } from '@domain/template/template/template.module'; import { InputCreatorModule } from '@services/api/input-creator/input.creator.module'; +import { RoleManagerModule } from '@domain/access/role-manager/role.manager.module'; @Module({ imports: [ @@ -43,7 +43,6 @@ import { InputCreatorModule } from '@services/api/input-creator/input.creator.mo ContextModule, CommunityModule, CommunityRoleModule, - CommunityPolicyModule, ProfileModule, LicensingModule, LicenseIssuerModule, @@ -61,6 +60,7 @@ import { InputCreatorModule } from '@services/api/input-creator/input.creator.mo ActivityAdapterModule, LoaderCreatorModule, TemplateModule, + RoleManagerModule, InputCreatorModule, NameReporterModule, TypeOrmModule.forFeature([Space]), diff --git a/src/domain/space/space/space.service.authorization.ts b/src/domain/space/space/space.service.authorization.ts index 41989ce689..6f30c6cb34 100644 --- a/src/domain/space/space/space.service.authorization.ts +++ b/src/domain/space/space/space.service.authorization.ts @@ -10,13 +10,11 @@ import { AuthorizationPolicyService } from '@domain/common/authorization-policy/ import { ISpace } from './space.interface'; import { SpaceVisibility } from '@common/enums/space.visibility'; import { RelationshipNotFoundException } from '@common/exceptions/relationship.not.found.exception'; -import { CommunityPolicyService } from '@domain/community/community-policy/community.policy.service'; import { CommunityAuthorizationService } from '@domain/community/community/community.service.authorization'; import { StorageAggregatorAuthorizationService } from '@domain/storage/storage-aggregator/storage.aggregator.service.authorization'; import { ProfileAuthorizationService } from '@domain/common/profile/profile.service.authorization'; import { CollaborationAuthorizationService } from '@domain/collaboration/collaboration/collaboration.service.authorization'; import { ContextAuthorizationService } from '@domain/context/context/context.service.authorization'; -import { ICommunityPolicy } from '@domain/community/community-policy/community.policy.interface'; import { SpacePrivacyMode } from '@common/enums/space.privacy.mode'; import { CommunityRoleType } from '@common/enums/community.role'; import { @@ -38,13 +36,15 @@ import { AgentAuthorizationService } from '@domain/agent/agent/agent.service.aut import { IAgent } from '@domain/agent/agent/agent.interface'; import { ISpaceSettings } from '../space.settings/space.settings.interface'; import { TemplatesSetAuthorizationService } from '@domain/template/templates-set/templates.set.service.authorization'; +import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; +import { IRoleManager } from '@domain/access/role-manager'; @Injectable() export class SpaceAuthorizationService { constructor( private authorizationPolicyService: AuthorizationPolicyService, private agentAuthorizationService: AgentAuthorizationService, - private communityPolicyService: CommunityPolicyService, + private roleManagerService: RoleManagerService, private storageAggregatorAuthorizationService: StorageAggregatorAuthorizationService, private profileAuthorizationService: ProfileAuthorizationService, private contextAuthorizationService: ContextAuthorizationService, @@ -63,13 +63,13 @@ export class SpaceAuthorizationService { parentSpace: { authorization: true, community: { - policy: true, + roleManager: true, }, }, agent: true, authorization: true, community: { - policy: true, + roleManager: true, }, collaboration: true, context: true, @@ -83,7 +83,7 @@ export class SpaceAuthorizationService { if ( !space.authorization || !space.community || - !space.community.policy || + !space.community.roleManager || !space.subspaces ) { throw new RelationshipNotFoundException( @@ -103,24 +103,22 @@ export class SpaceAuthorizationService { // Allow the parent admins to also delete subspaces let parentSpaceAdminCredentialCriterias: ICredentialDefinition[] = []; if (space.parentSpace) { - if (!space.parentSpace.community || !space.parentSpace.community.policy) { + const parentSpaceCommunity = space.parentSpace.community; + if (!parentSpaceCommunity || !parentSpaceCommunity.roleManager) { throw new RelationshipNotFoundException( - `Unable to load Space with parent community policy in auth reset: ${space.id} `, + `Unable to load Space with parent RoleManager in auth reset: ${space.id} `, LogContext.SPACES ); } - const parentCommunityPolicyWithSettings = this.getCommunityPolicy( - space.parentSpace - ); const spaceSettings = this.spaceSettingsService.getSettings( spaceInput.settingsStr ); parentSpaceAdminCredentialCriterias = - this.communityPolicyService.getCredentialsForRole( - parentCommunityPolicyWithSettings, - spaceSettings, - CommunityRoleType.ADMIN + this.roleManagerService.getCredentialsForRole( + parentSpaceCommunity.roleManager, + CommunityRoleType.ADMIN, + spaceSettings ); } @@ -128,7 +126,6 @@ export class SpaceAuthorizationService { space.authorization ); - const communityPolicy = this.getCommunityPolicy(space); const spaceSettings = this.spaceSettingsService.getSettings( space.settingsStr ); @@ -167,7 +164,7 @@ export class SpaceAuthorizationService { case SpaceVisibility.DEMO: space.authorization = this.extendAuthorizationPolicyLocal( space.authorization, - communityPolicy, + space.community.roleManager, spaceSettings, parentSpaceAdminCredentialCriterias ); @@ -178,7 +175,7 @@ export class SpaceAuthorizationService { if (space.level !== SpaceLevel.SPACE) { space.authorization = this.extendPrivateSubspaceAdmins( space.authorization, - communityPolicy, + space.community.roleManager, spaceSettings ); } @@ -208,7 +205,7 @@ export class SpaceAuthorizationService { const childAuthorzations = await this.propagateAuthorizationToChildEntities( space, levelZeroSpaceAgent, - communityPolicy, + space.community.roleManager, spaceSettings, spaceMembershipAllowed ); @@ -242,7 +239,7 @@ export class SpaceAuthorizationService { public async propagateAuthorizationToChildEntities( space: ISpace, levelZeroSpaceAgent: IAgent, - communityPolicy: ICommunityPolicy, + roleManager: IRoleManager, spaceSettings: ISpaceSettings, spaceMembershipAllowed: boolean ): Promise { @@ -251,7 +248,7 @@ export class SpaceAuthorizationService { !space.agent || !space.collaboration || !space.community || - !space.community.policy || + !space.community.roleManager || !space.context || !space.profile || !space.storageAggregator @@ -268,10 +265,9 @@ export class SpaceAuthorizationService { const communityAuthorizations = await this.communityAuthorizationService.applyAuthorizationPolicy( - space.community, + space.community.id, space.authorization, levelZeroSpaceAgent, - communityPolicy, spaceSettings, spaceMembershipAllowed, isSubspaceCommunity @@ -282,7 +278,7 @@ export class SpaceAuthorizationService { await this.collaborationAuthorizationService.applyAuthorizationPolicy( space.collaboration, space.authorization, - communityPolicy, + space.community.roleManager, spaceSettings, levelZeroSpaceAgent ); @@ -371,7 +367,7 @@ export class SpaceAuthorizationService { private extendAuthorizationPolicyLocal( authorization: IAuthorizationPolicy, - policy: ICommunityPolicy, + roleManager: IRoleManager, spaceSettings: ISpaceSettings, deletionCredentialCriterias: ICredentialDefinition[] ): IAuthorizationPolicy { @@ -390,10 +386,10 @@ export class SpaceAuthorizationService { newRules.push(deleteSubspaces); } - const memberCriteras = this.communityPolicyService.getCredentialsForRole( - policy, - spaceSettings, - CommunityRoleType.MEMBER + const memberCriteras = this.roleManagerService.getCredentialsForRole( + roleManager, + CommunityRoleType.MEMBER, + spaceSettings ); const spaceMember = this.authorizationPolicyService.createCredentialRule( [AuthorizationPrivilege.READ], @@ -402,12 +398,11 @@ export class SpaceAuthorizationService { ); newRules.push(spaceMember); - const spaceAdminCriterias = - this.communityPolicyService.getCredentialsForRole( - policy, - spaceSettings, - CommunityRoleType.ADMIN - ); + const spaceAdminCriterias = this.roleManagerService.getCredentialsForRole( + roleManager, + CommunityRoleType.ADMIN, + spaceSettings + ); const spaceAdmin = this.authorizationPolicyService.createCredentialRule( [ AuthorizationPrivilege.CREATE, @@ -423,7 +418,7 @@ export class SpaceAuthorizationService { const collaborationSettings = spaceSettings.collaboration; if (collaborationSettings.allowMembersToCreateSubspaces) { - const criteria = this.getContributorCriteria(policy, spaceSettings); + const criteria = this.getContributorCriteria(roleManager, spaceSettings); const createSubspacePrilegeRule = this.authorizationPolicyService.createCredentialRule( [AuthorizationPrivilege.CREATE_SUBSPACE], @@ -442,25 +437,14 @@ export class SpaceAuthorizationService { return authorization; } - public getCommunityPolicy(spaceInput: ISpace): ICommunityPolicy { - if (!spaceInput.community?.policy) - throw new EntityNotInitializedException( - `Unable to load community policy on base space: ${spaceInput.id}`, - LogContext.SPACES - ); - - const communityPolicyWithFlags = spaceInput.community.policy; - return communityPolicyWithFlags; - } - private getContributorCriteria( - policy: ICommunityPolicy, + roleManager: IRoleManager, spaceSettings: ISpaceSettings ): ICredentialDefinition[] { - const memberCriteria = this.communityPolicyService.getCredentialsForRole( - policy, - spaceSettings, - CommunityRoleType.MEMBER + const memberCriteria = this.roleManagerService.getCredentialsForRole( + roleManager, + CommunityRoleType.MEMBER, + spaceSettings ); const collaborationSettings = spaceSettings.collaboration; if ( @@ -468,8 +452,8 @@ export class SpaceAuthorizationService { spaceSettings.privacy.mode === SpacePrivacyMode.PUBLIC ) { const parentCredential = - this.communityPolicyService.getDirectParentCredentialForRole( - policy, + this.roleManagerService.getDirectParentCredentialForRole( + roleManager, CommunityRoleType.MEMBER ); if (parentCredential) memberCriteria.push(parentCredential); @@ -479,21 +463,21 @@ export class SpaceAuthorizationService { private extendPrivateSubspaceAdmins( authorization: IAuthorizationPolicy | undefined, - policy: ICommunityPolicy, + roleManager: IRoleManager, spaceSettings: ISpaceSettings ): IAuthorizationPolicy { if (!authorization) throw new EntityNotInitializedException( - `Authorization definition not found for: ${JSON.stringify(policy)}`, + `Authorization definition not found for: ${JSON.stringify(roleManager)}`, LogContext.SPACES ); const rules: IAuthorizationPolicyRuleCredential[] = []; const spaceAdminCriteria = [ - ...this.communityPolicyService.getCredentialsForRoleWithParents( - policy, - spaceSettings, - CommunityRoleType.ADMIN + ...this.roleManagerService.getCredentialsForRoleWithParents( + roleManager, + CommunityRoleType.ADMIN, + spaceSettings ), ]; const subspaceSpaceAdmins = diff --git a/src/domain/space/space/space.service.ts b/src/domain/space/space/space.service.ts index 13de732e6a..3723a85201 100644 --- a/src/domain/space/space/space.service.ts +++ b/src/domain/space/space/space.service.ts @@ -27,7 +27,6 @@ import { SpacesQueryArgs } from './dto/space.args.query.spaces'; import { SpaceVisibility } from '@common/enums/space.visibility'; import { SpaceFilterService } from '@services/infrastructure/space-filter/space.filter.service'; import { LimitAndShuffleIdsQueryArgs } from '@domain/common/query-args/limit-and-shuffle.ids.query.args'; -import { ICommunityPolicy } from '@domain/community/community-policy/community.policy.interface'; import { IProfile } from '@domain/common/profile/profile.interface'; import { InnovationHub, InnovationHubType } from '@domain/innovation-hub/types'; import { OperationNotAllowedException } from '@common/exceptions/operation.not.allowed.exception'; @@ -80,6 +79,7 @@ import { CreateInnovationFlowInput } from '@domain/collaboration/innovation-flow import { TemplateService } from '@domain/template/template/template.service'; import { templatesSetDefaults } from '../space.defaults/definitions/space.defaults.templates'; import { InputCreatorService } from '@services/api/input-creator/input.creator.service'; +import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; @Injectable() export class SpaceService { @@ -101,6 +101,7 @@ export class SpaceService { private licensingService: LicensingService, private licenseEngineService: LicenseEngineService, private templateService: TemplateService, + private roleManagerService: RoleManagerService, private inputCreatorService: InputCreatorService, @InjectRepository(Space) private spaceRepository: Repository, @@ -158,16 +159,19 @@ export class SpaceService { ); space.storageAggregator = storageAggregator; - const communityPolicy = this.spaceDefaultsService.getCommunityPolicy( - space.level - ); + const roleManagerRolesData = + this.spaceDefaultsService.getRoleManagerCommunityRoles(space.level); const applicationFormData = - this.spaceDefaultsService.getCommunityApplicationForm(space.level); + this.spaceDefaultsService.getRoleManagerCommunityApplicationForm( + space.level + ); const communityData: CreateCommunityInput = { name: spaceData.profileData.displayName, - policy: communityPolicy, - applicationForm: applicationFormData, + roleManagerData: { + roles: roleManagerRolesData, + applicationForm: applicationFormData, + }, guidelines: { // TODO: get this from defaults service profile: { @@ -253,12 +257,17 @@ export class SpaceService { ////// Community // set immediate community parent + resourceID - space.community.parentID = space.id; - space.community.policy = - this.communityService.updateCommunityPolicyResourceID( - space.community, - space.id + if (!space.community || !space.community.roleManager) { + throw new RelationshipNotFoundException( + `Unable to load community with role manager: ${space.id}`, + LogContext.SPACES ); + } + space.community.parentID = space.id; + space.community.roleManager = this.roleManagerService.updateRoleResourceID( + space.community.roleManager, + space.id + ); return space; } @@ -1000,8 +1009,8 @@ export class SpaceService { await this.communityRoleService.assignContributorAgentToRole( space.community, - contributor.agent, role, + contributor.agent, type ); } @@ -1031,22 +1040,22 @@ export class SpaceService { if (agentInfo) { await this.communityRoleService.assignUserToRole( space.community, - agentInfo.userID, CommunityRoleType.MEMBER, + agentInfo.userID, agentInfo ); await this.communityRoleService.assignUserToRole( space.community, - agentInfo.userID, CommunityRoleType.LEAD, + agentInfo.userID, agentInfo ); await this.communityRoleService.assignUserToRole( space.community, - agentInfo.userID, CommunityRoleType.ADMIN, + agentInfo.userID, agentInfo ); } @@ -1342,11 +1351,6 @@ export class SpaceService { return provider; } - public async getCommunityPolicy(spaceId: string): Promise { - const community = await this.getCommunity(spaceId); - return this.communityService.getCommunityPolicy(community); - } - public async getContext(spaceID: string): Promise { const subspaceWithContext = await this.getSpaceOrFail(spaceID, { relations: { diff --git a/src/domain/timeline/event/event.module.ts b/src/domain/timeline/event/event.module.ts index b7f4062d2e..5f07761503 100644 --- a/src/domain/timeline/event/event.module.ts +++ b/src/domain/timeline/event/event.module.ts @@ -8,7 +8,6 @@ import { CalendarEventResolverMutations } from './event.resolver.mutations'; import { CalendarEventService } from './event.service'; import { CalendarEventResolverFields } from './event.resolver.fields'; import { CalendarEventAuthorizationService } from './event.service.authorization'; -import { CommunityPolicyModule } from '@domain/community/community-policy/community.policy.module'; import { ProfileModule } from '@domain/common/profile/profile.module'; import { RoomModule } from '@domain/communication/room/room.module'; import { ContributorLookupModule } from '@services/infrastructure/contributor-lookup/contributor.lookup.module'; @@ -21,7 +20,6 @@ import { ContributorLookupModule } from '@services/infrastructure/contributor-lo VisualModule, ContributorLookupModule, ProfileModule, - CommunityPolicyModule, TypeOrmModule.forFeature([CalendarEvent]), ], providers: [ diff --git a/src/platform/invitation/dto/platform.invitation.dto.create.ts b/src/platform/invitation/dto/platform.invitation.dto.create.ts index 233687f0f7..0c35311bf0 100644 --- a/src/platform/invitation/dto/platform.invitation.dto.create.ts +++ b/src/platform/invitation/dto/platform.invitation.dto.create.ts @@ -29,7 +29,7 @@ export class CreatePlatformInvitationInput { createdBy!: string; - communityID?: string; + roleManagerID?: string; communityInvitedToParent!: boolean; platformRole?: PlatformRole; } diff --git a/src/platform/invitation/platform.invitation.entity.ts b/src/platform/invitation/platform.invitation.entity.ts index 6d791699c0..bf29b240ce 100644 --- a/src/platform/invitation/platform.invitation.entity.ts +++ b/src/platform/invitation/platform.invitation.entity.ts @@ -1,5 +1,4 @@ import { Column, Entity, ManyToOne } from 'typeorm'; -import { Community } from '@domain/community/community/community.entity'; import { IPlatformInvitation } from './platform.invitation.interface'; import { AuthorizableEntity } from '@domain/common/entity/authorizable-entity'; import { PlatformRole } from '@common/enums/platform.role'; @@ -16,14 +15,6 @@ export class PlatformInvitation extends AuthorizableEntity implements IPlatformInvitation { - // Platform invitations for Community - @ManyToOne(() => Community, community => community.platformInvitations, { - eager: false, - cascade: false, - onDelete: 'CASCADE', - }) - community?: Community; - @ManyToOne(() => RoleManager, manager => manager.platformInvitations, { eager: false, cascade: false, diff --git a/src/platform/invitation/platform.invitation.interface.ts b/src/platform/invitation/platform.invitation.interface.ts index 718e0ccf2f..3bf2f28059 100644 --- a/src/platform/invitation/platform.invitation.interface.ts +++ b/src/platform/invitation/platform.invitation.interface.ts @@ -1,8 +1,8 @@ -import { ICommunity } from '@domain/community/community/community.interface'; import { Field, ObjectType } from '@nestjs/graphql'; import { IAuthorizable } from '@domain/common/entity/authorizable-entity'; import { PlatformRole } from '@common/enums/platform.role'; import { IPlatform } from '@platform/platfrom/platform.interface'; +import { IRoleManager } from '@domain/access/role-manager'; @ObjectType('PlatformInvitation') export class IPlatformInvitation extends IAuthorizable { @@ -31,7 +31,7 @@ export class IPlatformInvitation extends IAuthorizable { createdBy!: string; - community?: ICommunity; + roleManager?: IRoleManager; platform?: IPlatform; diff --git a/src/platform/invitation/platform.invitation.service.ts b/src/platform/invitation/platform.invitation.service.ts index 4ccf41dd0d..e7ec1f1130 100644 --- a/src/platform/invitation/platform.invitation.service.ts +++ b/src/platform/invitation/platform.invitation.service.ts @@ -119,7 +119,7 @@ export class PlatformInvitationService { const existingPlatformInvitations = await this.platformInvitationRepository.find({ where: { email: email }, - relations: { community: true }, + relations: { roleManager: true }, }); if (existingPlatformInvitations.length > 0) diff --git a/src/services/adapters/notification-adapter/notification.payload.builder.ts b/src/services/adapters/notification-adapter/notification.payload.builder.ts index fc402b8ab4..0aad39119c 100644 --- a/src/services/adapters/notification-adapter/notification.payload.builder.ts +++ b/src/services/adapters/notification-adapter/notification.payload.builder.ts @@ -718,7 +718,7 @@ export class NotificationPayloadBuilder { ): Promise { const basePayload = this.buildBaseEventPayload(triggeredBy); const space = - await this.communityResolverService.getSpaceForCommunityOrFail( + await this.communityResolverService.getSpaceForRoleManagerOrFail( community.id ); const url = await this.urlGeneratorService.generateUrlForProfile( diff --git a/src/services/api/conversion/conversion.service.ts b/src/services/api/conversion/conversion.service.ts index e0d64011e0..332fe5a64c 100644 --- a/src/services/api/conversion/conversion.service.ts +++ b/src/services/api/conversion/conversion.service.ts @@ -25,6 +25,7 @@ import { SpaceLevel } from '@common/enums/space.level'; import { CommunityRoleService } from '@domain/community/community-role/community.role.service'; import { CommunityService } from '@domain/community/community/community.service'; import { CreateSpaceOnAccountInput } from '@domain/space/account/dto/account.dto.create.space'; +import { IRoleManager } from '@domain/access/role-manager'; export class ConversionService { constructor( @@ -127,23 +128,24 @@ export class ConversionService { LogContext.CONVERSION ); } + const spaceRoleManager = space.community.roleManager; const userMembers = await this.communityRoleService.getUsersWithRole( - space.community, + spaceRoleManager, CommunityRoleType.MEMBER ); const userLeads = await this.communityRoleService.getUsersWithRole( - space.community, + spaceRoleManager, CommunityRoleType.LEAD ); const orgMembers = await this.communityRoleService.getOrganizationsWithRole( - space.community, + spaceRoleManager, CommunityRoleType.MEMBER ); // Remove the contributors from old roles await this.removeContributors( - space.community, + spaceRoleManager, userMembers, userLeads, orgMembers, @@ -151,9 +153,9 @@ export class ConversionService { ); await this.communityRoleService.removeUserFromRole( - space.community, - agentInfo.userID, - CommunityRoleType.MEMBER + spaceRoleManager, + CommunityRoleType.MEMBER, + agentInfo.userID ); // Swap the communications @@ -305,26 +307,27 @@ export class ConversionService { ); } + const roleManager = subsubspace.community.roleManager; const userMembers = await this.communityRoleService.getUsersWithRole( - subsubspace.community, + roleManager, CommunityRoleType.MEMBER ); const userLeads = await this.communityRoleService.getUsersWithRole( - subsubspace.community, + roleManager, CommunityRoleType.LEAD ); const orgMembers = await this.communityRoleService.getOrganizationsWithRole( - subsubspace.community, + roleManager, CommunityRoleType.MEMBER ); const orgLeads = await this.communityRoleService.getOrganizationsWithRole( - subsubspace.community, + roleManager, CommunityRoleType.LEAD ); // Remove the contributors from old roles await this.removeContributors( - subsubspace.community, + subsubspace.community.roleManager, userMembers, userLeads, orgMembers, @@ -333,14 +336,14 @@ export class ConversionService { // also remove the current user from the members of the newly created Challenge, otherwise will end up re-assigning await this.communityRoleService.removeUserFromRole( - subspace.community, - agentInfo.userID, - CommunityRoleType.MEMBER + subspace.community.roleManager, + CommunityRoleType.MEMBER, + agentInfo.userID ); await this.communityRoleService.removeUserFromRole( - subspace.community, - agentInfo.userID, - CommunityRoleType.LEAD + subspace.community.roleManager, + CommunityRoleType.LEAD, + agentInfo.userID ); // Swap the communication @@ -518,7 +521,7 @@ export class ConversionService { } private async removeContributors( - community: ICommunity, + roleManager: IRoleManager, userMembers: IUser[], userLeads: IUser[], orgMembers: IOrganization[], @@ -526,36 +529,36 @@ export class ConversionService { ) { for (const userMember of userMembers) { await this.communityRoleService.removeUserFromRole( - community, - userMember.id, - CommunityRoleType.MEMBER + roleManager, + CommunityRoleType.MEMBER, + userMember.id ); } for (const userLead of userLeads) { await this.communityRoleService.removeUserFromRole( - community, - userLead.id, - CommunityRoleType.LEAD + roleManager, + CommunityRoleType.LEAD, + userLead.id ); } for (const orgMember of orgMembers) { await this.communityRoleService.removeOrganizationFromRole( - community, - orgMember.id, - CommunityRoleType.MEMBER + roleManager, + CommunityRoleType.MEMBER, + orgMember.id ); } for (const orgLead of orgLeads) { await this.communityRoleService.removeOrganizationFromRole( - community, - orgLead.id, - CommunityRoleType.LEAD + roleManager, + CommunityRoleType.LEAD, + orgLead.id ); } } private async assignContributors( - community: ICommunity, + roleManager: IRoleManager, userMembers: IUser[], userLeads: IUser[], orgMembers: IOrganization[], @@ -563,31 +566,31 @@ export class ConversionService { ) { for (const userMember of userMembers) { await this.communityRoleService.assignUserToRole( - community, - userMember.id, - CommunityRoleType.MEMBER + roleManager, + CommunityRoleType.MEMBER, + userMember.id ); } for (const userLead of userLeads) { await this.communityRoleService.assignUserToRole( - community, - userLead.id, - CommunityRoleType.LEAD + roleManager, + CommunityRoleType.LEAD, + userLead.id ); } for (const orgMember of orgMembers) { await this.communityRoleService.assignOrganizationToRole( - community, - orgMember.id, - CommunityRoleType.MEMBER + roleManager, + CommunityRoleType.MEMBER, + orgMember.id ); } if (orgLeads) { for (const orgLead of orgLeads) { await this.communityRoleService.assignOrganizationToRole( - community, - orgLead.id, - CommunityRoleType.LEAD + roleManager, + CommunityRoleType.LEAD, + orgLead.id ); } } diff --git a/src/services/api/me/me.service.ts b/src/services/api/me/me.service.ts index ef18b3fc45..0c1c197564 100644 --- a/src/services/api/me/me.service.ts +++ b/src/services/api/me/me.service.ts @@ -40,15 +40,15 @@ export class MeService { ); const results: CommunityInvitationResult[] = []; for (const invitation of invitations) { - if (!invitation.community) { + if (!invitation.roleManager) { throw new EntityNotFoundException( `Community not found for invitation ${invitation.id}`, LogContext.COMMUNITY ); } const space = - await this.communityResolverService.getSpaceForCommunityOrFail( - invitation.community.id + await this.communityResolverService.getSpaceForRoleManagerOrFail( + invitation.roleManager.id ); results.push({ id: `${invitation.id}`, @@ -67,15 +67,15 @@ export class MeService { await this.rolesService.getCommunityApplicationsForUser(userId, states); const results: CommunityApplicationResult[] = []; for (const application of applications) { - if (!application.community) { + if (!application.roleManager) { throw new EntityNotFoundException( `Community not found for application ${application.id}`, LogContext.COMMUNITY ); } const space = - await this.communityResolverService.getSpaceForCommunityOrFail( - application.community.id + await this.communityResolverService.getSpaceForRoleManagerOrFail( + application.roleManager.id ); results.push({ id: `${application.id}`, diff --git a/src/services/api/registration/registration.service.ts b/src/services/api/registration/registration.service.ts index 6b2252a0e0..ce38ba082e 100644 --- a/src/services/api/registration/registration.service.ts +++ b/src/services/api/registration/registration.service.ts @@ -119,13 +119,13 @@ export class RegistrationService { const communityInvitations: IInvitation[] = []; for (const platformInvitation of platformInvitations) { - const community = platformInvitation.community; + const roleManager = platformInvitation.roleManager; // Process community invitations - if (community) { + if (roleManager) { const invitationInput: CreateInvitationInput = { invitedContributor: user.id, - communityID: community.id, + roleManagerID: roleManager.id, createdBy: platformInvitation.createdBy, invitedToParent: platformInvitation.communityInvitedToParent, }; @@ -140,7 +140,7 @@ export class RegistrationService { const authorization = await this.invitationAuthorizationService.applyAuthorizationPolicy( invitation, - community.authorization + roleManager.authorization ); await this.authorizationPolicyService.save(authorization); diff --git a/src/services/api/roles/roles.service.ts b/src/services/api/roles/roles.service.ts index 0dd78a7be9..8bcf3f8300 100644 --- a/src/services/api/roles/roles.service.ts +++ b/src/services/api/roles/roles.service.ts @@ -4,8 +4,6 @@ import { InjectEntityManager } from '@nestjs/typeorm'; import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; import { OrganizationService } from '@domain/community/organization/organization.service'; import { UserService } from '@domain/community/user/user.service'; -import { VirtualContributorService } from '@domain/community/virtual-contributor/virtual.contributor.service'; -import { ICommunity } from '@domain/community/community'; import { ApplicationService } from '@domain/community/application/application.service'; import { IApplication } from '@domain/community/application'; import { SpaceFilterService } from '@services/infrastructure/space-filter/space.filter.service'; @@ -23,9 +21,10 @@ import { mapOrganizationCredentialsToRoles } from './util/map.organization.crede import { RolesResultSpace } from './dto/roles.dto.result.space'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { AuthorizationService } from '@core/authorization/authorization.service'; -import { SpaceService } from '@domain/space/space/space.service'; import { ContributorLookupService } from '@services/infrastructure/contributor-lookup/contributor.lookup.service'; import { RolesVirtualContributorInput } from './dto/roles.dto.input.virtual.contributor'; +import { IRoleManager } from '@domain/access/role-manager'; +import { VirtualContributorService } from '@domain/community/virtual-contributor/virtual.contributor.service'; export class RolesService { constructor( @@ -36,7 +35,6 @@ export class RolesService { private invitationService: InvitationService, private spaceFilterService: SpaceFilterService, private communityResolverService: CommunityResolverService, - private spaceService: SpaceService, private authorizationService: AuthorizationService, private organizationService: OrganizationService, private userLookupService: ContributorLookupService, @@ -128,14 +126,14 @@ export class RolesService { ): Promise { const applicationResults: CommunityApplicationForRoleResult[] = []; for (const application of applications) { - const community = application.community; + const roleManager = application.roleManager; const state = await this.applicationService.getApplicationState( application.id ); - if (community) { + if (roleManager) { const applicationResult = - await this.buildApplicationResultForCommunityApplication( - community, + await this.buildApplicationResultForRoleManagerApplication( + roleManager, state, application ); @@ -146,24 +144,24 @@ export class RolesService { return applicationResults; } - private async buildApplicationResultForCommunityApplication( - community: ICommunity, + private async buildApplicationResultForRoleManagerApplication( + roleManager: IRoleManager, state: string, application: IApplication ): Promise { - const communityDisplayName = - await this.communityResolverService.getDisplayNameForCommunityOrFail( - community.id + const roleManagerDisplayName = + await this.communityResolverService.getDisplayNameForRoleManagerOrFail( + roleManager.id ); const space = - await this.communityResolverService.getSpaceForCommunityOrFail( - community.id + await this.communityResolverService.getSpaceForRoleManagerOrFail( + roleManager.id ); const applicationResult = new CommunityApplicationForRoleResult( - community.id, - communityDisplayName, + roleManager.id, + roleManagerDisplayName, state, application.id, space.id, @@ -202,14 +200,14 @@ export class RolesService { ): Promise { const invitationResults: CommunityInvitationForRoleResult[] = []; for (const invitation of invitations) { - const community = invitation.community; + const roleManager = invitation.roleManager; const state = await this.invitationService.getInvitationState( invitation.id ); - if (community) { + if (roleManager) { const invitationResult = - await this.buildInvitationResultForCommunityInvitation( - community, + await this.buildInvitationResultForRoleManagerInvitation( + roleManager, state, invitation ); @@ -220,23 +218,23 @@ export class RolesService { return invitationResults; } - private async buildInvitationResultForCommunityInvitation( - community: ICommunity, + private async buildInvitationResultForRoleManagerInvitation( + roleManager: IRoleManager, state: string, invitation: IInvitation ): Promise { const communityDisplayName = - await this.communityResolverService.getDisplayNameForCommunityOrFail( - community.id + await this.communityResolverService.getDisplayNameForRoleManagerOrFail( + roleManager.id ); const space = - await this.communityResolverService.getSpaceForCommunityOrFail( - community.id + await this.communityResolverService.getSpaceForRoleManagerOrFail( + roleManager.id ); const invitationResult = new CommunityInvitationForRoleResult( - community.id, + roleManager.id, communityDisplayName, state, invitation.id, diff --git a/src/services/infrastructure/entity-resolver/community.resolver.service.ts b/src/services/infrastructure/entity-resolver/community.resolver.service.ts index 9dfed14a79..0517564e4c 100644 --- a/src/services/infrastructure/entity-resolver/community.resolver.service.ts +++ b/src/services/infrastructure/entity-resolver/community.resolver.service.ts @@ -42,6 +42,23 @@ export class CommunityResolverService { return space.levelZeroSpaceID; } + async getCommunityForRoleManager(roleManagerID: string): Promise { + const community = await this.entityManager.findOne(Community, { + where: { + roleManager: { + id: roleManagerID, + }, + }, + }); + if (!community) { + throw new EntityNotFoundException( + `Unable to find Community for given RoleManager id: ${roleManagerID}`, + LogContext.COMMUNITY + ); + } + return community; + } + public async getLevelZeroSpaceIdForCollaboration( collaborationID: string ): Promise { @@ -271,13 +288,15 @@ export class CommunityResolverService { return community; } - public async getSpaceForCommunityOrFail( - communityId: string + public async getSpaceForRoleManagerOrFail( + roleManagerID: string ): Promise { const space = await this.entityManager.findOne(Space, { where: { community: { - id: communityId, + roleManager: { + id: roleManagerID, + }, }, }, relations: { @@ -286,7 +305,7 @@ export class CommunityResolverService { }); if (!space) { throw new EntityNotFoundException( - `Unable to find space for community: ${communityId}`, + `Unable to find space for community: ${roleManagerID}`, LogContext.URL_GENERATOR ); } @@ -317,10 +336,10 @@ export class CommunityResolverService { return space; } - public async getDisplayNameForCommunityOrFail( - communityId: string + public async getDisplayNameForRoleManagerOrFail( + roleManagerID: string ): Promise { - const space = await this.getSpaceForCommunityOrFail(communityId); + const space = await this.getSpaceForRoleManagerOrFail(roleManagerID); return space.profile.displayName; } diff --git a/src/services/infrastructure/naming/naming.service.ts b/src/services/infrastructure/naming/naming.service.ts index 11e46c6bfe..497bb5da41 100644 --- a/src/services/infrastructure/naming/naming.service.ts +++ b/src/services/infrastructure/naming/naming.service.ts @@ -8,7 +8,6 @@ import { EntityNotInitializedException, } from '@common/exceptions'; import { IPost } from '@domain/collaboration/post/post.interface'; -import { ICommunityPolicy } from '@domain/community/community-policy/community.policy.interface'; import { CalendarEvent, ICalendarEvent } from '@domain/timeline/event'; import { InnovationHub } from '@domain/innovation-hub/innovation.hub.entity'; import { ICallout } from '@domain/collaboration/callout'; @@ -23,6 +22,7 @@ import { IDiscussion } from '@platform/forum-discussion/discussion.interface'; import { SpaceReservedName } from '@common/enums/space.reserved.name'; import { generateNameId } from '@services/infrastructure/naming/generate.name.id'; import { Template } from '@domain/template/template/template.entity'; +import { IRoleManager } from '@domain/access/role-manager'; export class NamingService { constructor( @@ -238,10 +238,10 @@ export class NamingService { return result; } - async getCommunityPolicyAndSettingsForCollaboration( + async getRoleManagerAndSettingsForCollaboration( collaborationID: string ): Promise<{ - communityPolicy: ICommunityPolicy; + roleManager: IRoleManager; spaceSettings: ISpaceSettings; }> { const space = await this.entityManager.findOne(Space, { @@ -252,24 +252,24 @@ export class NamingService { }, relations: { community: { - policy: true, + roleManager: true, }, }, }); - if (!space || !space.community || !space.community.policy) { + if (!space || !space.community || !space.community.roleManager) { throw new EntityNotInitializedException( `Unable to load all entities for space with collaboration ${collaborationID}`, LogContext.COMMUNITY ); } // Directly parse the settings string to avoid the need to load the settings service - const communityPolicy = space.community.policy; + const roleManager = space.community.roleManager; const spaceSettings: ISpaceSettings = JSON.parse(space.settingsStr); - return { communityPolicy, spaceSettings }; + return { roleManager, spaceSettings }; } - async getCommunityPolicyAndSettingsForCallout(calloutID: string): Promise<{ - communityPolicy: ICommunityPolicy; + async getRoleManagerAndSettingsForCallout(calloutID: string): Promise<{ + roleManager: IRoleManager; spaceSettings: ISpaceSettings; }> { const space = await this.entityManager.findOne(Space, { @@ -282,11 +282,11 @@ export class NamingService { }, relations: { community: { - policy: true, + roleManager: true, }, }, }); - if (!space || !space.community || !space.community.policy) { + if (!space || !space.community || !space.community.roleManager) { throw new EntityNotInitializedException( `Unable to load all entities for space with callout ${calloutID}`, LogContext.COMMUNITY @@ -294,10 +294,10 @@ export class NamingService { } // Directly parse the settings string to avoid the need to load the settings service - const communityPolicy = space.community.policy; + const roleManager = space.community.roleManager; const spaceSettings: ISpaceSettings = JSON.parse(space.settingsStr); - return { communityPolicy, spaceSettings }; + return { roleManager: roleManager, spaceSettings }; } async getPostForRoom(roomID: string): Promise { diff --git a/test/mocks/community.resolver.service.mock.ts b/test/mocks/community.resolver.service.mock.ts index 139f0b7b50..99583272f0 100644 --- a/test/mocks/community.resolver.service.mock.ts +++ b/test/mocks/community.resolver.service.mock.ts @@ -5,7 +5,7 @@ export const MockCommunityResolverService: MockValueProvider Date: Mon, 16 Sep 2024 07:47:22 +0100 Subject: [PATCH 03/78] renamed RoleManager to RoleSet --- src/domain/access/role-manager/index.ts | 2 - .../role.manager.resolver.fields.ts | 23 -- .../role.manager.resolver.mutations.ts | 45 --- .../dto/role.set.dto.create.ts} | 6 +- .../role.set.dto.update.application.form.ts} | 4 +- src/domain/access/role-set/index.ts | 2 + .../role.set.entity.ts} | 20 +- .../role.set.interface.ts} | 6 +- .../role.set.module.ts} | 24 +- .../role-set/role.set.resolver.fields.ts | 23 ++ .../role.set.resolver.mutations.spec.ts} | 12 +- .../role-set/role.set.resolver.mutations.ts | 45 +++ .../role.set.service.authorization.ts} | 152 +++++---- .../role.set.service.spec.ts} | 14 +- .../role.set.service.ts} | 223 ++++++------- src/domain/access/role/role.entity.ts | 6 +- src/domain/access/role/role.interface.ts | 2 +- .../callout.contribution.module.ts | 4 +- ...lout.contribution.service.authorization.ts | 16 +- .../collaboration/callout/callout.module.ts | 4 +- .../callout/callout.resolver.mutations.ts | 4 +- .../callout/callout.service.authorization.ts | 6 +- .../collaboration/collaboration.module.ts | 4 +- .../collaboration.resolver.mutations.ts | 4 +- .../collaboration.service.authorization.ts | 46 +-- .../collaboration/collaboration.service.ts | 10 +- src/domain/collaboration/post/post.module.ts | 4 +- .../post/post.service.authorization.ts | 18 +- .../room/room.service.mentions.ts | 7 +- .../application/application.entity.ts | 6 +- .../application/application.interface.ts | 4 +- .../application/application.service.ts | 2 +- .../application/dto/application.dto.create.ts | 2 +- ....lifecycle.application.options.provider.ts | 8 +- ...e.lifecycle.invitation.options.provider.ts | 14 +- .../community-role/community.role.module.ts | 4 +- .../community.role.resolver.mutations.ts | 76 +++-- .../community.role.service.events.ts | 7 +- .../community-role/community.role.service.ts | 304 ++++++++---------- .../dto/community.role.dto.apply.ts | 2 +- .../community.role.dto.invite.contributor.ts | 2 +- .../dto/community.role.dto.join.ts | 2 +- ....role.dto.platform.invitation.community.ts | 2 +- ...unity.role.dto.role.assign.organization.ts | 2 +- .../community.role.dto.role.assign.user.ts | 2 +- .../community.role.dto.role.assign.virtual.ts | 2 +- ...unity.role.dto.role.remove.organization.ts | 2 +- .../community.role.dto.role.remove.user.ts | 2 +- .../community.role.dto.role.remove.virtual.ts | 2 +- .../community/community/community.entity.ts | 6 +- .../community/community.interface.ts | 4 +- .../community/community/community.module.ts | 4 +- .../community/community.resolver.fields.ts | 10 +- .../community.service.authorization.ts | 56 ++-- .../community/community/community.service.ts | 43 ++- .../community/dto/community.dto.create.ts | 4 +- .../invitation/dto/invitation.dto.create.ts | 2 +- .../community/invitation/invitation.entity.ts | 6 +- .../invitation/invitation.interface.ts | 4 +- .../invitation/invitation.service.ts | 8 +- ... space.community.role.application.form.ts} | 0 ...ager.roles.ts => space.community.roles.ts} | 0 ...bspace.community.role.application.form.ts} | 0 ...r.roles.ts => subspace.community.roles.ts} | 0 .../space.defaults/space.defaults.service.ts | 14 +- src/domain/space/space/index.ts | 4 +- src/domain/space/space/space.module.ts | 4 +- .../space/space.service.authorization.ts | 62 ++-- src/domain/space/space/space.service.ts | 23 +- .../dto/platform.invitation.dto.create.ts | 2 +- .../invitation/platform.invitation.entity.ts | 6 +- .../platform.invitation.interface.ts | 4 +- .../invitation/platform.invitation.service.ts | 2 +- .../notification.payload.builder.ts | 7 +- .../api/conversion/conversion.service.ts | 50 +-- src/services/api/me/me.service.ts | 12 +- .../api/registration/registration.service.ts | 8 +- src/services/api/roles/roles.service.ts | 56 ++-- .../community.resolver.service.ts | 24 +- .../infrastructure/naming/naming.service.ts | 26 +- 80 files changed, 785 insertions(+), 849 deletions(-) delete mode 100644 src/domain/access/role-manager/index.ts delete mode 100644 src/domain/access/role-manager/role.manager.resolver.fields.ts delete mode 100644 src/domain/access/role-manager/role.manager.resolver.mutations.ts rename src/domain/access/{role-manager/dto/role.manager.dto.create.ts => role-set/dto/role.set.dto.create.ts} (62%) rename src/domain/access/{role-manager/dto/role.manager.dto.update.application.form.ts => role-set/dto/role.set.dto.update.application.form.ts} (85%) create mode 100644 src/domain/access/role-set/index.ts rename src/domain/access/{role-manager/role.manager.entity.ts => role-set/role.set.entity.ts} (68%) rename src/domain/access/{role-manager/role.manager.interface.ts => role-set/role.set.interface.ts} (83%) rename src/domain/access/{role-manager/role.manager.module.ts => role-set/role.set.module.ts} (72%) create mode 100644 src/domain/access/role-set/role.set.resolver.fields.ts rename src/domain/access/{role-manager/role.manager.resolver.mutations.spec.ts => role-set/role.set.resolver.mutations.spec.ts} (70%) create mode 100644 src/domain/access/role-set/role.set.resolver.mutations.ts rename src/domain/access/{role-manager/role.manager.service.authorization.ts => role-set/role.set.service.authorization.ts} (81%) rename src/domain/access/{role-manager/role.manager.service.spec.ts => role-set/role.set.service.spec.ts} (67%) rename src/domain/access/{role-manager/role.manager.service.ts => role-set/role.set.service.ts} (54%) rename src/domain/space/space.defaults/definitions/{space.role.manager.application.form.ts => space.community.role.application.form.ts} (100%) rename src/domain/space/space.defaults/definitions/{space.role.manager.roles.ts => space.community.roles.ts} (100%) rename src/domain/space/space.defaults/definitions/{subspace.role.manager.application.form.ts => subspace.community.role.application.form.ts} (100%) rename src/domain/space/space.defaults/definitions/{subspace.role.manager.roles.ts => subspace.community.roles.ts} (100%) diff --git a/src/domain/access/role-manager/index.ts b/src/domain/access/role-manager/index.ts deleted file mode 100644 index 7b51db2598..0000000000 --- a/src/domain/access/role-manager/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './role.manager.entity'; -export * from './role.manager.interface'; diff --git a/src/domain/access/role-manager/role.manager.resolver.fields.ts b/src/domain/access/role-manager/role.manager.resolver.fields.ts deleted file mode 100644 index e1e93a2e06..0000000000 --- a/src/domain/access/role-manager/role.manager.resolver.fields.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { GraphqlGuard } from '@core/authorization'; -import { UseGuards } from '@nestjs/common'; -import { Parent, ResolveField, Resolver } from '@nestjs/graphql'; -import { Profiling } from '@src/common/decorators'; -import { RoleManagerService } from './role.manager.service'; -import { IForm } from '@domain/common/form/form.interface'; -import { IRoleManager } from './role.manager.interface'; -import { RoleManager } from './role.manager.entity'; - -@Resolver(() => IRoleManager) -export class RoleManagerResolverFields { - constructor(private roleManagerService: RoleManagerService) {} - - @UseGuards(GraphqlGuard) - @ResolveField('applicationForm', () => IForm, { - nullable: false, - description: 'The Form used for Applications to this roleManager.', - }) - @Profiling.api - async applicationForm(@Parent() roleManager: RoleManager): Promise { - return await this.roleManagerService.getApplicationForm(roleManager); - } -} diff --git a/src/domain/access/role-manager/role.manager.resolver.mutations.ts b/src/domain/access/role-manager/role.manager.resolver.mutations.ts deleted file mode 100644 index f4ed86314f..0000000000 --- a/src/domain/access/role-manager/role.manager.resolver.mutations.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { UseGuards } from '@nestjs/common'; -import { Args, Mutation, Resolver } from '@nestjs/graphql'; -import { RoleManagerService } from './role.manager.service'; -import { CurrentUser, Profiling } from '@src/common/decorators'; -import { GraphqlGuard } from '@core/authorization'; -import { AgentInfo } from '@core/authentication.agent.info/agent.info'; -import { AuthorizationPrivilege } from '@common/enums'; -import { AuthorizationService } from '@core/authorization/authorization.service'; -import { UpdateRoleManagerApplicationFormInput } from './dto/role.manager.dto.update.application.form'; -import { IRoleManager } from './role.manager.interface'; - -@Resolver() -export class RoleManagerResolverMutations { - constructor( - private authorizationService: AuthorizationService, - private roleManagerService: RoleManagerService - ) {} - - @UseGuards(GraphqlGuard) - @Mutation(() => IRoleManager, { - description: 'Update the Application Form used by this RoleManager.', - }) - @Profiling.api - async updateRoleManagerApplicationForm( - @CurrentUser() agentInfo: AgentInfo, - @Args('applicationFormData') - applicationFormData: UpdateRoleManagerApplicationFormInput - ): Promise { - const roleManager = await this.roleManagerService.getRoleManagerOrFail( - applicationFormData.roleManagerID - ); - - await this.authorizationService.grantAccessOrFail( - agentInfo, - roleManager.authorization, - AuthorizationPrivilege.UPDATE, - `update roleManager application form: ${roleManager.id}` - ); - - return await this.roleManagerService.updateApplicationForm( - roleManager, - applicationFormData.formData - ); - } -} diff --git a/src/domain/access/role-manager/dto/role.manager.dto.create.ts b/src/domain/access/role-set/dto/role.set.dto.create.ts similarity index 62% rename from src/domain/access/role-manager/dto/role.manager.dto.create.ts rename to src/domain/access/role-set/dto/role.set.dto.create.ts index d3d8ea9a6a..fc4b43b740 100644 --- a/src/domain/access/role-manager/dto/role.manager.dto.create.ts +++ b/src/domain/access/role-set/dto/role.set.dto.create.ts @@ -1,9 +1,9 @@ import { CreateRoleInput } from '@domain/access/role/dto/role.dto.create'; import { CreateFormInput } from '@domain/common/form/dto/form.dto.create'; -import { IRoleManager } from '../role.manager.interface'; +import { IRoleSet } from '../role.set.interface'; -export class CreateRoleManagerInput { - parentRoleManager?: IRoleManager; +export class CreateRoleSetInput { + parentRoleSet?: IRoleSet; roles!: CreateRoleInput[]; applicationForm!: CreateFormInput; } diff --git a/src/domain/access/role-manager/dto/role.manager.dto.update.application.form.ts b/src/domain/access/role-set/dto/role.set.dto.update.application.form.ts similarity index 85% rename from src/domain/access/role-manager/dto/role.manager.dto.update.application.form.ts rename to src/domain/access/role-set/dto/role.set.dto.update.application.form.ts index c3b6ab4ad3..1fddbda871 100644 --- a/src/domain/access/role-manager/dto/role.manager.dto.update.application.form.ts +++ b/src/domain/access/role-set/dto/role.set.dto.update.application.form.ts @@ -5,9 +5,9 @@ import { UpdateFormInput } from '@domain/common/form/dto/form.dto.update'; import { UUID } from '@domain/common/scalars'; @InputType() -export class UpdateRoleManagerApplicationFormInput { +export class UpdateRoleSetApplicationFormInput { @Field(() => UUID, { nullable: false }) - roleManagerID!: string; + roleSetID!: string; @Field(() => UpdateFormInput, { nullable: false }) @ValidateNested() diff --git a/src/domain/access/role-set/index.ts b/src/domain/access/role-set/index.ts new file mode 100644 index 0000000000..9499bb7463 --- /dev/null +++ b/src/domain/access/role-set/index.ts @@ -0,0 +1,2 @@ +export * from './role.set.entity'; +export * from './role.set.interface'; diff --git a/src/domain/access/role-manager/role.manager.entity.ts b/src/domain/access/role-set/role.set.entity.ts similarity index 68% rename from src/domain/access/role-manager/role.manager.entity.ts rename to src/domain/access/role-set/role.set.entity.ts index 0b08ce5493..b5ff9d4b6e 100644 --- a/src/domain/access/role-manager/role.manager.entity.ts +++ b/src/domain/access/role-set/role.set.entity.ts @@ -4,14 +4,14 @@ import { AuthorizableEntity } from '@domain/common/entity/authorizable-entity'; import { Role } from '../role/role.entity'; import { Form } from '@domain/common/form/form.entity'; import { PlatformInvitation } from '@platform/invitation/platform.invitation.entity'; -import { IRoleManager } from './role.manager.interface'; +import { IRoleSet } from './role.set.interface'; import { Application } from '@domain/community/application/application.entity'; import { Invitation } from '@domain/community/invitation/invitation.entity'; @Entity() -export class RoleManager +export class RoleSet extends AuthorizableEntity - implements IRoleManager, IGroupable + implements IRoleSet, IGroupable { @OneToOne(() => Form, { eager: false, @@ -21,19 +21,19 @@ export class RoleManager @JoinColumn() applicationForm?: Form; - @OneToMany(() => Role, role => role.manager, { + @OneToMany(() => Role, role => role.roleSet, { eager: false, cascade: true, }) roles?: Role[]; - @OneToMany(() => Application, application => application.roleManager, { + @OneToMany(() => Application, application => application.roleSet, { eager: false, cascade: true, }) applications?: Application[]; - @OneToMany(() => Invitation, invitation => invitation.roleManager, { + @OneToMany(() => Invitation, invitation => invitation.roleSet, { eager: false, cascade: true, }) @@ -41,7 +41,7 @@ export class RoleManager @OneToMany( () => PlatformInvitation, - platformInvitation => platformInvitation.roleManager, + platformInvitation => platformInvitation.roleSet, { eager: false, cascade: true, @@ -49,11 +49,11 @@ export class RoleManager ) platformInvitations?: PlatformInvitation[]; - // The parent roleManager can have many child communities; the relationship is controlled by the child. - @ManyToOne(() => RoleManager, { + // The parent roleSet can have many child communities; the relationship is controlled by the child. + @ManyToOne(() => RoleSet, { eager: false, cascade: false, onDelete: 'SET NULL', }) - parentRoleManager?: RoleManager; + parentRoleSet?: RoleSet; } diff --git a/src/domain/access/role-manager/role.manager.interface.ts b/src/domain/access/role-set/role.set.interface.ts similarity index 83% rename from src/domain/access/role-manager/role.manager.interface.ts rename to src/domain/access/role-set/role.set.interface.ts index f0cffd3135..3c4f8463ab 100644 --- a/src/domain/access/role-manager/role.manager.interface.ts +++ b/src/domain/access/role-set/role.set.interface.ts @@ -6,8 +6,8 @@ import { IApplication } from '@domain/community/application/application.interfac import { IInvitation } from '@domain/community/invitation/invitation.interface'; import { IRole } from '../role/role.interface'; -@ObjectType('RoleManager') -export abstract class IRoleManager extends IAuthorizable { +@ObjectType('RoleSet') +export abstract class IRoleSet extends IAuthorizable { roles?: IRole[]; applications?: IApplication[]; invitations?: IInvitation[]; @@ -15,5 +15,5 @@ export abstract class IRoleManager extends IAuthorizable { applicationForm?: IForm; - parentRoleManager?: IRoleManager; + parentRoleSet?: IRoleSet; } diff --git a/src/domain/access/role-manager/role.manager.module.ts b/src/domain/access/role-set/role.set.module.ts similarity index 72% rename from src/domain/access/role-manager/role.manager.module.ts rename to src/domain/access/role-set/role.set.module.ts index c1d3dad817..da10d249b4 100644 --- a/src/domain/access/role-manager/role.manager.module.ts +++ b/src/domain/access/role-set/role.set.module.ts @@ -5,11 +5,11 @@ import { CommunicationModule } from '@domain/communication/communication/communi import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { TrustRegistryAdapterModule } from '@services/external/trust-registry/trust.registry.adapter/trust.registry.adapter.module'; -import { RoleManager } from './role.manager.entity'; -import { RoleManagerResolverFields } from './role.manager.resolver.fields'; -import { RoleManagerResolverMutations } from './role.manager.resolver.mutations'; -import { RoleManagerService } from './role.manager.service'; -import { RoleManagerAuthorizationService } from './role.manager.service.authorization'; +import { RoleSet } from './role.set.entity'; +import { RoleSetResolverFields } from './role.set.resolver.fields'; +import { RoleSetResolverMutations } from './role.set.resolver.mutations'; +import { RoleSetService } from './role.set.service'; +import { RoleSetAuthorizationService } from './role.set.service.authorization'; import { FormModule } from '@domain/common/form/form.module'; import { StorageAggregatorResolverModule } from '@services/infrastructure/storage-aggregator-resolver/storage.aggregator.resolver.module'; import { LicenseEngineModule } from '@core/license-engine/license.engine.module'; @@ -34,15 +34,15 @@ import { RoleModule } from '../role/role.module'; ApplicationModule, PlatformInvitationModule, VirtualContributorModule, - TypeOrmModule.forFeature([RoleManager]), + TypeOrmModule.forFeature([RoleSet]), TrustRegistryAdapterModule, ], providers: [ - RoleManagerService, - RoleManagerAuthorizationService, - RoleManagerResolverMutations, - RoleManagerResolverFields, + RoleSetService, + RoleSetAuthorizationService, + RoleSetResolverMutations, + RoleSetResolverFields, ], - exports: [RoleManagerService, RoleManagerAuthorizationService], + exports: [RoleSetService, RoleSetAuthorizationService], }) -export class RoleManagerModule {} +export class RoleSetModule {} diff --git a/src/domain/access/role-set/role.set.resolver.fields.ts b/src/domain/access/role-set/role.set.resolver.fields.ts new file mode 100644 index 0000000000..efd24181f2 --- /dev/null +++ b/src/domain/access/role-set/role.set.resolver.fields.ts @@ -0,0 +1,23 @@ +import { GraphqlGuard } from '@core/authorization'; +import { UseGuards } from '@nestjs/common'; +import { Parent, ResolveField, Resolver } from '@nestjs/graphql'; +import { Profiling } from '@src/common/decorators'; +import { RoleSetService } from './role.set.service'; +import { IForm } from '@domain/common/form/form.interface'; +import { IRoleSet } from './role.set.interface'; +import { RoleSet } from './role.set.entity'; + +@Resolver(() => IRoleSet) +export class RoleSetResolverFields { + constructor(private roleSetService: RoleSetService) {} + + @UseGuards(GraphqlGuard) + @ResolveField('applicationForm', () => IForm, { + nullable: false, + description: 'The Form used for Applications to this roleSet.', + }) + @Profiling.api + async applicationForm(@Parent() roleSet: RoleSet): Promise { + return await this.roleSetService.getApplicationForm(roleSet); + } +} diff --git a/src/domain/access/role-manager/role.manager.resolver.mutations.spec.ts b/src/domain/access/role-set/role.set.resolver.mutations.spec.ts similarity index 70% rename from src/domain/access/role-manager/role.manager.resolver.mutations.spec.ts rename to src/domain/access/role-set/role.set.resolver.mutations.spec.ts index 5bef36c77e..84b0ac5364 100644 --- a/src/domain/access/role-manager/role.manager.resolver.mutations.spec.ts +++ b/src/domain/access/role-set/role.set.resolver.mutations.spec.ts @@ -1,17 +1,17 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { RoleManagerResolverMutations } from './role.manager.resolver.mutations'; +import { RoleSetResolverMutations } from './role.set.resolver.mutations'; import { MockCacheManager } from '@test/mocks/cache-manager.mock'; import { MockWinstonProvider } from '@test/mocks/winston.provider.mock'; import { MockNotificationsService } from '@test/mocks/notifications.service.mock'; import { defaultMockerFactory } from '@test/utils/default.mocker.factory'; -describe('RoleManagerResolver', () => { - let resolver: RoleManagerResolverMutations; +describe('RoleSetResolver', () => { + let resolver: RoleSetResolverMutations; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ - RoleManagerResolverMutations, + RoleSetResolverMutations, MockCacheManager, MockWinstonProvider, MockNotificationsService, @@ -20,9 +20,7 @@ describe('RoleManagerResolver', () => { .useMocker(defaultMockerFactory) .compile(); - resolver = module.get( - RoleManagerResolverMutations - ); + resolver = module.get(RoleSetResolverMutations); }); it('should be defined', () => { diff --git a/src/domain/access/role-set/role.set.resolver.mutations.ts b/src/domain/access/role-set/role.set.resolver.mutations.ts new file mode 100644 index 0000000000..19b5595e6c --- /dev/null +++ b/src/domain/access/role-set/role.set.resolver.mutations.ts @@ -0,0 +1,45 @@ +import { UseGuards } from '@nestjs/common'; +import { Args, Mutation, Resolver } from '@nestjs/graphql'; +import { RoleSetService } from './role.set.service'; +import { CurrentUser, Profiling } from '@src/common/decorators'; +import { GraphqlGuard } from '@core/authorization'; +import { AgentInfo } from '@core/authentication.agent.info/agent.info'; +import { AuthorizationPrivilege } from '@common/enums'; +import { AuthorizationService } from '@core/authorization/authorization.service'; +import { UpdateRoleSetApplicationFormInput } from './dto/role.set.dto.update.application.form'; +import { IRoleSet } from './role.set.interface'; + +@Resolver() +export class RoleSetResolverMutations { + constructor( + private authorizationService: AuthorizationService, + private roleSetService: RoleSetService + ) {} + + @UseGuards(GraphqlGuard) + @Mutation(() => IRoleSet, { + description: 'Update the Application Form used by this RoleSet.', + }) + @Profiling.api + async updateRoleSetApplicationForm( + @CurrentUser() agentInfo: AgentInfo, + @Args('applicationFormData') + applicationFormData: UpdateRoleSetApplicationFormInput + ): Promise { + const roleSet = await this.roleSetService.getRoleSetOrFail( + applicationFormData.roleSetID + ); + + await this.authorizationService.grantAccessOrFail( + agentInfo, + roleSet.authorization, + AuthorizationPrivilege.UPDATE, + `update roleSet application form: ${roleSet.id}` + ); + + return await this.roleSetService.updateApplicationForm( + roleSet, + applicationFormData.formData + ); + } +} diff --git a/src/domain/access/role-manager/role.manager.service.authorization.ts b/src/domain/access/role-set/role.set.service.authorization.ts similarity index 81% rename from src/domain/access/role-manager/role.manager.service.authorization.ts rename to src/domain/access/role-set/role.set.service.authorization.ts index c2117b3eff..044b900afa 100644 --- a/src/domain/access/role-manager/role.manager.service.authorization.ts +++ b/src/domain/access/role-set/role.set.service.authorization.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { RoleManagerService } from './role.manager.service'; +import { RoleSetService } from './role.set.service'; import { AuthorizationCredential, AuthorizationPrivilege, @@ -39,13 +39,13 @@ import { InvitationAuthorizationService } from '@domain/community/invitation/inv import { VirtualContributorService } from '@domain/community/virtual-contributor/virtual.contributor.service'; import { CommunityMembershipPolicy } from '@common/enums/community.membership.policy'; import { CommunityRoleType } from '@common/enums/community.role'; -import { IRoleManager } from './role.manager.interface'; +import { IRoleSet } from './role.set.interface'; @Injectable() -export class RoleManagerAuthorizationService { +export class RoleSetAuthorizationService { constructor( private licenseEngineService: LicenseEngineService, - private roleManagerService: RoleManagerService, + private roleSetService: RoleSetService, private authorizationPolicyService: AuthorizationPolicyService, private applicationAuthorizationService: ApplicationAuthorizationService, private invitationAuthorizationService: InvitationAuthorizationService, @@ -54,101 +54,95 @@ export class RoleManagerAuthorizationService { ) {} async applyAuthorizationPolicy( - roleManagerID: string, + roleSetID: string, parentAuthorization: IAuthorizationPolicy, levelZeroSpaceAgent: IAgent, spaceSettings: ISpaceSettings, spaceMembershipAllowed: boolean, isSubspace: boolean ): Promise { - const roleManager = await this.roleManagerService.getRoleManagerOrFail( - roleManagerID, - { - relations: { - roles: true, - applications: true, - invitations: true, - platformInvitations: true, - }, - } - ); + const roleSet = await this.roleSetService.getRoleSetOrFail(roleSetID, { + relations: { + roles: true, + applications: true, + invitations: true, + platformInvitations: true, + }, + }); if ( - !roleManager.roles || - !roleManager.applications || - !roleManager.invitations || - !roleManager.platformInvitations + !roleSet.roles || + !roleSet.applications || + !roleSet.invitations || + !roleSet.platformInvitations ) { throw new RelationshipNotFoundException( - `Unable to load child entities for roleManager authorization: ${roleManager.id} `, + `Unable to load child entities for roleSet authorization: ${roleSet.id} `, LogContext.COMMUNITY ); } const updatedAuthorizations: IAuthorizationPolicy[] = []; - roleManager.authorization = + roleSet.authorization = this.authorizationPolicyService.inheritParentAuthorization( - roleManager.authorization, + roleSet.authorization, parentAuthorization ); - roleManager.authorization = this.appendPrivilegeRules( - roleManager.authorization - ); + roleSet.authorization = this.appendPrivilegeRules(roleSet.authorization); - roleManager.authorization = await this.extendAuthorizationPolicy( - roleManager, - roleManager.authorization, + roleSet.authorization = await this.extendAuthorizationPolicy( + roleSet, + roleSet.authorization, parentAuthorization?.anonymousReadAccess, levelZeroSpaceAgent, spaceSettings ); - roleManager.authorization = this.appendVerifiedCredentialRules( - roleManager.authorization + roleSet.authorization = this.appendVerifiedCredentialRules( + roleSet.authorization ); if (spaceMembershipAllowed) { - roleManager.authorization = - this.extendRoleManagerAuthorizationPolicySpace( - roleManager, - roleManager.authorization, - spaceSettings - ); + roleSet.authorization = this.extendRoleSetAuthorizationPolicySpace( + roleSet, + roleSet.authorization, + spaceSettings + ); } if (isSubspace) { - roleManager.authorization = this.extendAuthorizationPolicySubspace( - roleManager, - roleManager.authorization, + roleSet.authorization = this.extendAuthorizationPolicySubspace( + roleSet, + roleSet.authorization, spaceSettings ); } // always false - roleManager.authorization.anonymousReadAccess = false; + roleSet.authorization.anonymousReadAccess = false; - updatedAuthorizations.push(roleManager.authorization); + updatedAuthorizations.push(roleSet.authorization); - for (const application of roleManager.applications) { + for (const application of roleSet.applications) { const applicationAuthReset = await this.applicationAuthorizationService.applyAuthorizationPolicy( application, - roleManager.authorization + roleSet.authorization ); application.authorization = applicationAuthReset; } - for (const invitation of roleManager.invitations) { + for (const invitation of roleSet.invitations) { const invitationReset = await this.invitationAuthorizationService.applyAuthorizationPolicy( invitation, - roleManager.authorization + roleSet.authorization ); invitation.authorization = invitationReset; } - for (const externalInvitation of roleManager.platformInvitations) { + for (const externalInvitation of roleSet.platformInvitations) { const platformInvitationAuthorization = await this.platformInvitationAuthorizationService.applyAuthorizationPolicy( externalInvitation, - roleManager.authorization + roleSet.authorization ); updatedAuthorizations.push(platformInvitationAuthorization); } @@ -156,14 +150,14 @@ export class RoleManagerAuthorizationService { return updatedAuthorizations; } - private extendRoleManagerAuthorizationPolicySpace( - roleManager: IRoleManager, - roleManagerAuthorization: IAuthorizationPolicy | undefined, + private extendRoleSetAuthorizationPolicySpace( + roleSet: IRoleSet, + roleSetAuthorization: IAuthorizationPolicy | undefined, spaceSettings: ISpaceSettings ): IAuthorizationPolicy { - if (!roleManagerAuthorization) + if (!roleSetAuthorization) throw new EntityNotInitializedException( - `Authorization definition not found for: ${roleManager.id}`, + `Authorization definition not found for: ${roleSet.id}`, LogContext.SPACES ); @@ -212,13 +206,13 @@ export class RoleManagerAuthorizationService { } return this.authorizationPolicyService.appendCredentialAuthorizationRules( - roleManagerAuthorization, + roleSetAuthorization, newRules ); } private async extendAuthorizationPolicy( - roleManager: IRoleManager, + roleSet: IRoleSet, authorization: IAuthorizationPolicy | undefined, allowGlobalRegisteredReadAccess: boolean | undefined, levelZeroSpaceAgent: IAgent, @@ -238,16 +232,16 @@ export class RoleManagerAuthorizationService { newRules.push(globalAdminAddMembers); const inviteMembersCriterias: ICredentialDefinition[] = - this.roleManagerService.getCredentialsForRoleWithParents( - roleManager, + this.roleSetService.getCredentialsForRoleWithParents( + roleSet, CommunityRoleType.ADMIN, spaceSettings ); if (spaceSettings.membership.allowSubspaceAdminsToInviteMembers) { // use the member credential to create subspace admin credential const subspaceAdminCredential: ICredentialDefinition = - this.roleManagerService.getCredentialForRole( - roleManager, + this.roleSetService.getCredentialForRole( + roleSet, CommunityRoleType.MEMBER ); subspaceAdminCredential.type = @@ -283,8 +277,8 @@ export class RoleManagerAuthorizationService { ); if (accessVirtualContributors) { const criterias: ICredentialDefinition[] = - this.roleManagerService.getCredentialsForRoleWithParents( - roleManager, + this.roleSetService.getCredentialsForRoleWithParents( + roleSet, CommunityRoleType.ADMIN, spaceSettings ); @@ -313,7 +307,7 @@ export class RoleManagerAuthorizationService { } private extendAuthorizationPolicySubspace( - roleManager: IRoleManager, + roleSet: IRoleSet, authorization: IAuthorizationPolicy | undefined, spaceSettings: ISpaceSettings ): IAuthorizationPolicy { @@ -325,21 +319,21 @@ export class RoleManagerAuthorizationService { const newRules: IAuthorizationPolicyRuleCredential[] = []; - const parentRoleManagerCredential = - this.roleManagerService.getDirectParentCredentialForRole( - roleManager, + const parentRoleSetCredential = + this.roleSetService.getDirectParentCredentialForRole( + roleSet, CommunityRoleType.MEMBER ); - // Allow member of the parent roleManager to Apply - if (parentRoleManagerCredential) { + // Allow member of the parent roleSet to Apply + if (parentRoleSetCredential) { const membershipSettings = spaceSettings.membership; switch (membershipSettings.policy) { case CommunityMembershipPolicy.APPLICATIONS: const spaceMemberCanApply = this.authorizationPolicyService.createCredentialRule( [AuthorizationPrivilege.COMMUNITY_APPLY], - [parentRoleManagerCredential], + [parentRoleSetCredential], CREDENTIAL_RULE_SUBSPACE_PARENT_MEMBER_APPLY ); spaceMemberCanApply.cascade = false; @@ -349,7 +343,7 @@ export class RoleManagerAuthorizationService { const spaceMemberCanJoin = this.authorizationPolicyService.createCredentialRule( [AuthorizationPrivilege.COMMUNITY_JOIN], - [parentRoleManagerCredential], + [parentRoleSetCredential], CREDENTIAL_RULE_SUBSPACE_PARENT_MEMBER_JOIN ); spaceMemberCanJoin.cascade = false; @@ -359,8 +353,8 @@ export class RoleManagerAuthorizationService { } const adminCredentials = - this.roleManagerService.getCredentialsForRoleWithParents( - roleManager, + this.roleSetService.getCredentialsForRoleWithParents( + roleSet, CommunityRoleType.ADMIN, spaceSettings ); @@ -394,7 +388,7 @@ export class RoleManagerAuthorizationService { } public extendAuthorizationPolicyForSelfRemoval( - roleManager: IRoleManager, + roleSet: IRoleSet, userToBeRemovedID: string ): IAuthorizationPolicy { const newRules: IAuthorizationPolicyRuleCredential[] = []; @@ -412,14 +406,14 @@ export class RoleManagerAuthorizationService { ); newRules.push(userSelfRemovalRule); - const clonedRoleManagerAuthorization = + const clonedRoleSetAuthorization = this.authorizationPolicyService.cloneAuthorizationPolicy( - roleManager.authorization + roleSet.authorization ); const updatedAuthorization = this.authorizationPolicyService.appendCredentialAuthorizationRules( - clonedRoleManagerAuthorization, + clonedRoleSetAuthorization, newRules ); @@ -427,7 +421,7 @@ export class RoleManagerAuthorizationService { } public async extendAuthorizationPolicyForVirtualContributorRemoval( - roleManager: IRoleManager, + roleSet: IRoleSet, virtualContributorToBeRemoved: string ): Promise { const newRules: IAuthorizationPolicyRuleCredential[] = []; @@ -445,14 +439,14 @@ export class RoleManagerAuthorizationService { ); newRules.push(userSelfRemovalRule); - const clonedRoleManagerAuthorization = + const clonedRoleSetAuthorization = this.authorizationPolicyService.cloneAuthorizationPolicy( - roleManager.authorization + roleSet.authorization ); const updatedAuthorization = this.authorizationPolicyService.appendCredentialAuthorizationRules( - clonedRoleManagerAuthorization, + clonedRoleSetAuthorization, newRules ); diff --git a/src/domain/access/role-manager/role.manager.service.spec.ts b/src/domain/access/role-set/role.set.service.spec.ts similarity index 67% rename from src/domain/access/role-manager/role.manager.service.spec.ts rename to src/domain/access/role-set/role.set.service.spec.ts index a2cf419046..a3d96ca6a9 100644 --- a/src/domain/access/role-manager/role.manager.service.spec.ts +++ b/src/domain/access/role-set/role.set.service.spec.ts @@ -1,19 +1,19 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { RoleManager } from './role.manager.entity'; -import { RoleManagerService } from './role.manager.service'; +import { RoleSet } from './role.set.entity'; +import { RoleSetService } from './role.set.service'; import { MockCacheManager } from '@test/mocks/cache-manager.mock'; import { MockWinstonProvider } from '@test/mocks/winston.provider.mock'; import { defaultMockerFactory } from '@test/utils/default.mocker.factory'; import { repositoryProviderMockFactory } from '@test/utils/repository.provider.mock.factory'; -describe('RoleManagerService', () => { - let service: RoleManagerService; +describe('RoleSetService', () => { + let service: RoleSetService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ - RoleManagerService, - repositoryProviderMockFactory(RoleManager), + RoleSetService, + repositoryProviderMockFactory(RoleSet), MockCacheManager, MockWinstonProvider, ], @@ -21,7 +21,7 @@ describe('RoleManagerService', () => { .useMocker(defaultMockerFactory) .compile(); - service = module.get(RoleManagerService); + service = module.get(RoleSetService); }); it('should be defined', () => { diff --git a/src/domain/access/role-manager/role.manager.service.ts b/src/domain/access/role-set/role.set.service.ts similarity index 54% rename from src/domain/access/role-manager/role.manager.service.ts rename to src/domain/access/role-set/role.set.service.ts index 61b4539872..06646ff647 100644 --- a/src/domain/access/role-manager/role.manager.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -12,13 +12,13 @@ import { LogContext } from '@common/enums/logging.context'; import { IForm } from '@domain/common/form/form.interface'; import { FormService } from '@domain/common/form/form.service'; import { UpdateFormInput } from '@domain/common/form/dto/form.dto.update'; -import { CreateRoleManagerInput } from './dto/role.manager.dto.create'; +import { CreateRoleSetInput } from './dto/role.set.dto.create'; import { PlatformInvitationService } from '@platform/invitation/platform.invitation.service'; import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type'; import { ApplicationService } from '@domain/community/application/application.service'; import { InvitationService } from '@domain/community/invitation/invitation.service'; -import { RoleManager } from './role.manager.entity'; -import { IRoleManager } from './role.manager.interface'; +import { RoleSet } from './role.set.entity'; +import { IRoleSet } from './role.set.interface'; import { RoleService } from '../role/role.service'; import { CommunityRoleType } from '@common/enums/community.role'; import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; @@ -27,7 +27,7 @@ import { AuthorizationCredential } from '@common/enums/authorization.credential' import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; @Injectable() -export class RoleManagerService { +export class RoleSetService { constructor( private authorizationPolicyService: AuthorizationPolicyService, private applicationService: ApplicationService, @@ -35,56 +35,54 @@ export class RoleManagerService { private platformInvitationService: PlatformInvitationService, private formService: FormService, private roleService: RoleService, - @InjectRepository(RoleManager) - private roleManagerRepository: Repository, + @InjectRepository(RoleSet) + private roleSetRepository: Repository, @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService ) {} - async createRoleManager( - roleManagerData: CreateRoleManagerInput - ): Promise { - const roleManager: IRoleManager = new RoleManager(); - roleManager.authorization = new AuthorizationPolicy( + async createRoleSet(roleSetData: CreateRoleSetInput): Promise { + const roleSet: IRoleSet = new RoleSet(); + roleSet.authorization = new AuthorizationPolicy( AuthorizationPolicyType.ROLE_MANAGER ); - roleManager.roles = []; - roleManager.applications = []; - roleManager.invitations = []; - roleManager.platformInvitations = []; + roleSet.roles = []; + roleSet.applications = []; + roleSet.invitations = []; + roleSet.platformInvitations = []; - roleManager.parentRoleManager = roleManagerData.parentRoleManager; + roleSet.parentRoleSet = roleSetData.parentRoleSet; - for (const roleData of roleManagerData.roles) { + for (const roleData of roleSetData.roles) { const role = this.roleService.createRole(roleData); - roleManager.roles.push(role); + roleSet.roles.push(role); } - roleManager.applicationForm = this.formService.createForm( - roleManagerData.applicationForm + roleSet.applicationForm = this.formService.createForm( + roleSetData.applicationForm ); - return roleManager; + return roleSet; } - async getRoleManagerOrFail( - roleManagerID: string, - options?: FindOneOptions - ): Promise { - const roleManager = await this.roleManagerRepository.findOne({ - where: { id: roleManagerID }, + async getRoleSetOrFail( + roleSetID: string, + options?: FindOneOptions + ): Promise { + const roleSet = await this.roleSetRepository.findOne({ + where: { id: roleSetID }, ...options, }); - if (!roleManager) + if (!roleSet) throw new EntityNotFoundException( - `Unable to find RoleManager with ID: ${roleManagerID}`, + `Unable to find RoleSet with ID: ${roleSetID}`, LogContext.COMMUNITY ); - return roleManager; + return roleSet; } - async removeRoleManager(roleManagerID: string): Promise { + async removeRoleSet(roleSetID: string): Promise { // Note need to load it in with all contained entities so can remove fully - const roleManager = await this.getRoleManagerOrFail(roleManagerID, { + const roleSet = await this.getRoleSetOrFail(roleSetID, { relations: { roles: true, applications: true, @@ -94,92 +92,87 @@ export class RoleManagerService { }, }); if ( - !roleManager.roles || - !roleManager.applications || - !roleManager.invitations || - !roleManager.platformInvitations || - !roleManager.applicationForm + !roleSet.roles || + !roleSet.applications || + !roleSet.invitations || + !roleSet.platformInvitations || + !roleSet.applicationForm ) { throw new RelationshipNotFoundException( - `Unable to load child entities for roleManager for deletion: ${roleManager.id} `, + `Unable to load child entities for roleSet for deletion: ${roleSet.id} `, LogContext.COMMUNITY ); } - for (const role of roleManager.roles) { + for (const role of roleSet.roles) { await this.roleService.removeRole(role); } - if (roleManager.authorization) - await this.authorizationPolicyService.delete(roleManager.authorization); + if (roleSet.authorization) + await this.authorizationPolicyService.delete(roleSet.authorization); // Remove all applications - for (const application of roleManager.applications) { + for (const application of roleSet.applications) { await this.applicationService.deleteApplication({ ID: application.id, }); } // Remove all invitations - for (const invitation of roleManager.invitations) { + for (const invitation of roleSet.invitations) { await this.invitationService.deleteInvitation({ ID: invitation.id, }); } - for (const externalInvitation of roleManager.platformInvitations) { + for (const externalInvitation of roleSet.platformInvitations) { await this.platformInvitationService.deletePlatformInvitation({ ID: externalInvitation.id, }); } - await this.formService.removeForm(roleManager.applicationForm); + await this.formService.removeForm(roleSet.applicationForm); - await this.roleManagerRepository.remove(roleManager as RoleManager); + await this.roleSetRepository.remove(roleSet as RoleSet); return true; } - async save(roleManager: IRoleManager): Promise { - return await this.roleManagerRepository.save(roleManager); + async save(roleSet: IRoleSet): Promise { + return await this.roleSetRepository.save(roleSet); } - async getParentRoleManager( - roleManager: IRoleManager - ): Promise { - const roleManagerWithParent = await this.getRoleManagerOrFail( - roleManager.id, - { - relations: { parentRoleManager: true }, - } - ); + async getParentRoleSet(roleSet: IRoleSet): Promise { + const roleSetWithParent = await this.getRoleSetOrFail(roleSet.id, { + relations: { parentRoleSet: true }, + }); - const parentRoleManager = roleManagerWithParent?.parentRoleManager; - if (parentRoleManager) { - return await this.getRoleManagerOrFail(parentRoleManager.id); + const parentRoleSet = roleSetWithParent?.parentRoleSet; + if (parentRoleSet) { + return await this.getRoleSetOrFail(parentRoleSet.id); } return undefined; } async updateApplicationForm( - roleManager: IRoleManager, + roleSet: IRoleSet, formData: UpdateFormInput - ): Promise { - const applicationForm = await this.getApplicationForm(roleManager); - roleManager.applicationForm = await this.formService.updateForm( + ): Promise { + const applicationForm = await this.getApplicationForm(roleSet); + roleSet.applicationForm = await this.formService.updateForm( applicationForm, formData ); - return await this.save(roleManager); + return await this.save(roleSet); } - async getApplicationForm(roleManager: IRoleManager): Promise { - const roleManagerForm = await this.getRoleManagerOrFail(roleManager.id, { + async getApplicationForm(roleSet: IRoleSet): Promise { + const roleSetForm = await this.getRoleSetOrFail(roleSet.id, { relations: { applicationForm: true }, }); - const applicationForm = roleManagerForm.applicationForm; + const applicationForm = roleSetForm.applicationForm; if (!applicationForm) { throw new EntityNotFoundException( - `Unable to find Application Form for RoleManager with ID: ${roleManager.id}`, + `Unable to find Application Form for RoleSet with ID: ${roleSet.id}`, LogContext.COMMUNITY ); } @@ -187,53 +180,49 @@ export class RoleManagerService { } // Update the Community policy to have the right resource ID - public updateRoleResourceID( - roleManager: IRoleManager, - resourceID: string - ): IRoleManager { - const roleDefinitions = this.getRoleDefinitions(roleManager); + public updateRoleResourceID(roleSet: IRoleSet, resourceID: string): IRoleSet { + const roleDefinitions = this.getRoleDefinitions(roleSet); for (const roleDefinition of roleDefinitions) { const credential = this.roleService.getCredentialForRole(roleDefinition); credential.resourceID = resourceID; } - return roleManager; + return roleSet; } - public async getPeerRoleManagers( - parentRoleManager: IRoleManager, - childRoleManager: IRoleManager - ): Promise { - const peerRoleManagers: IRoleManager[] = - await this.roleManagerRepository.find({ - where: { - parentRoleManager: { - id: parentRoleManager.id, - }, + public async getPeerRoleSets( + parentRoleSet: IRoleSet, + childRoleSet: IRoleSet + ): Promise { + const peerRoleSets: IRoleSet[] = await this.roleSetRepository.find({ + where: { + parentRoleSet: { + id: parentRoleSet.id, }, - }); - const result: IRoleManager[] = []; - for (const roleManager of peerRoleManagers) { - if (roleManager && !(roleManager.id === childRoleManager.id)) { - result.push(roleManager); + }, + }); + const result: IRoleSet[] = []; + for (const roleSet of peerRoleSets) { + if (roleSet && !(roleSet.id === childRoleSet.id)) { + result.push(roleSet); } } return result; } - public inheritParentCredentials(roleManager: IRoleManager): IRoleManager { - const roleManagerParent = roleManager.parentRoleManager; - if (!roleManagerParent) { + public inheritParentCredentials(roleSet: IRoleSet): IRoleSet { + const roleSetParent = roleSet.parentRoleSet; + if (!roleSetParent) { throw new RelationshipNotFoundException( - `Unable to inherit parent credentials for role Manager ${roleManager.id}`, + `Unable to inherit parent credentials for role Manager ${roleSet.id}`, LogContext.ROLES ); } - const roleDefinitions = this.getRoleDefinitions(roleManager); + const roleDefinitions = this.getRoleDefinitions(roleSet); for (const roleDefinition of roleDefinitions) { const parentRoleDefinition = this.getRoleDefinition( - roleManagerParent, + roleSetParent, roleDefinition.type ); const parentCredentials: ICredentialDefinition[] = []; @@ -248,15 +237,15 @@ export class RoleManagerService { roleDefinition.parentCredentials = JSON.stringify(parentCredentials); } - return roleManager; + return roleSet; } getDirectParentCredentialForRole( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType ): ICredentialDefinition | undefined { const parentCredentials = this.getParentCredentialsForRole( - roleManager, + roleSet, roleType ); @@ -269,34 +258,28 @@ export class RoleManagerService { } public getParentCredentialsForRole( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType ): ICredentialDefinition[] { - const roleDefinition = this.getRoleDefinition(roleManager, roleType); + const roleDefinition = this.getRoleDefinition(roleSet, roleType); return this.roleService.getParentCredentialsForRole(roleDefinition); } public getCredentialsForRoleWithParents( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType, spaceSettings: ISpaceSettings ): ICredentialDefinition[] { - const result = this.getCredentialsForRole( - roleManager, - roleType, - spaceSettings - ); - return result.concat( - this.getParentCredentialsForRole(roleManager, roleType) - ); + const result = this.getCredentialsForRole(roleSet, roleType, spaceSettings); + return result.concat(this.getParentCredentialsForRole(roleSet, roleType)); } public getCredentialsForRole( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType, spaceSettings: ISpaceSettings // TODO: would like not to have this here; for later ): ICredentialDefinition[] { - const result = [this.getCredentialForRole(roleManager, roleType)]; + const result = [this.getCredentialForRole(roleSet, roleType)]; if ( roleType === CommunityRoleType.ADMIN && spaceSettings.privacy.allowPlatformSupportAsAdmin @@ -310,18 +293,18 @@ export class RoleManagerService { } public getCredentialForRole( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType ): ICredentialDefinition { - const roleDefinition = this.getRoleDefinition(roleManager, roleType); + const roleDefinition = this.getRoleDefinition(roleSet, roleType); return this.roleService.getCredentialForRole(roleDefinition); } - public getRoleDefinitions(roleManager: IRoleManager): IRole[] { - const roleDefinitions = roleManager.roles; + public getRoleDefinitions(roleSet: IRoleSet): IRole[] { + const roleDefinitions = roleSet.roles; if (!roleDefinitions) { throw new RelationshipNotFoundException( - `Unable to load roles for RoleManager: ${roleManager.id}`, + `Unable to load roles for RoleSet: ${roleSet.id}`, LogContext.COMMUNITY ); } @@ -329,14 +312,14 @@ export class RoleManagerService { } public getRoleDefinition( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType ): IRole { - const roleDefinitions = this.getRoleDefinitions(roleManager); + const roleDefinitions = this.getRoleDefinitions(roleSet); const role = roleDefinitions.find(rd => rd.type === roleType); if (!role) { throw new RelationshipNotFoundException( - `Unable to find Role for type ${roleType} for RoleManager: ${roleManager.id}`, + `Unable to find Role for type ${roleType} for RoleSet: ${roleSet.id}`, LogContext.COMMUNITY ); } diff --git a/src/domain/access/role/role.entity.ts b/src/domain/access/role/role.entity.ts index 6ad554d292..568eeb5e01 100644 --- a/src/domain/access/role/role.entity.ts +++ b/src/domain/access/role/role.entity.ts @@ -3,16 +3,16 @@ import { Column, Entity, ManyToOne } from 'typeorm'; import { IRole } from './role.interface'; import { CommunityRoleType } from '@common/enums/community.role'; import { ENUM_LENGTH } from '@common/constants/entity.field.length.constants'; -import { RoleManager } from '../role-manager'; +import { RoleSet } from '../role-set'; @Entity() export class Role extends BaseAlkemioEntity implements IRole { - @ManyToOne(() => RoleManager, manager => manager.roles, { + @ManyToOne(() => RoleSet, manager => manager.roles, { eager: false, cascade: false, onDelete: 'CASCADE', }) - manager?: RoleManager; + roleSet?: RoleSet; @Column('varchar', { length: ENUM_LENGTH, nullable: false }) type!: CommunityRoleType; diff --git a/src/domain/access/role/role.interface.ts b/src/domain/access/role/role.interface.ts index 3153334f71..9ecbfd0381 100644 --- a/src/domain/access/role/role.interface.ts +++ b/src/domain/access/role/role.interface.ts @@ -20,7 +20,7 @@ export abstract class IRole extends IBaseAlkemio { @Field(() => Boolean, { nullable: false, description: - 'Flag to indicate if this Role requires having the same role in the Parent RoleManager.', + 'Flag to indicate if this Role requires having the same role in the Parent RoleSet.', }) requiresParentRole!: boolean; diff --git a/src/domain/collaboration/callout-contribution/callout.contribution.module.ts b/src/domain/collaboration/callout-contribution/callout.contribution.module.ts index 7ce4caf59e..534da241ff 100644 --- a/src/domain/collaboration/callout-contribution/callout.contribution.module.ts +++ b/src/domain/collaboration/callout-contribution/callout.contribution.module.ts @@ -11,7 +11,7 @@ import { NamingModule } from '@services/infrastructure/naming/naming.module'; import { PostModule } from '../post/post.module'; import { ContributorLookupModule } from '@services/infrastructure/contributor-lookup/contributor.lookup.module'; import { LinkModule } from '../link/link.module'; -import { RoleManagerModule } from '@domain/access/role-manager/role.manager.module'; +import { RoleSetModule } from '@domain/access/role-set/role.set.module'; @Module({ imports: [ @@ -22,7 +22,7 @@ import { RoleManagerModule } from '@domain/access/role-manager/role.manager.modu NamingModule, LinkModule, ContributorLookupModule, - RoleManagerModule, + RoleSetModule, TypeOrmModule.forFeature([CalloutContribution]), ], providers: [ diff --git a/src/domain/collaboration/callout-contribution/callout.contribution.service.authorization.ts b/src/domain/collaboration/callout-contribution/callout.contribution.service.authorization.ts index e42484000b..5d75b54d6a 100644 --- a/src/domain/collaboration/callout-contribution/callout.contribution.service.authorization.ts +++ b/src/domain/collaboration/callout-contribution/callout.contribution.service.authorization.ts @@ -18,8 +18,8 @@ import { import { LinkAuthorizationService } from '../link/link.service.authorization'; import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; -import { IRoleManager } from '@domain/access/role-manager/role.manager.interface'; -import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; +import { IRoleSet } from '@domain/access/role-set/role.set.interface'; +import { RoleSetService } from '@domain/access/role-set/role.set.service'; @Injectable() export class CalloutContributionAuthorizationService { @@ -29,13 +29,13 @@ export class CalloutContributionAuthorizationService { private postAuthorizationService: PostAuthorizationService, private whiteboardAuthorizationService: WhiteboardAuthorizationService, private linkAuthorizationService: LinkAuthorizationService, - private roleManagerService: RoleManagerService + private roleSetService: RoleSetService ) {} public async applyAuthorizationPolicy( contributionID: string, parentAuthorization: IAuthorizationPolicy | undefined, - roleManager?: IRoleManager, + roleSet?: IRoleSet, spaceSettings?: ISpaceSettings ): Promise { const contribution = @@ -111,7 +111,7 @@ export class CalloutContributionAuthorizationService { // Extend to give the user creating the contribution more rights contribution.authorization = this.appendCredentialRules( contribution, - roleManager, + roleSet, spaceSettings ); updatedAuthorizations.push(contribution.authorization); @@ -121,7 +121,7 @@ export class CalloutContributionAuthorizationService { await this.postAuthorizationService.applyAuthorizationPolicy( contribution.post, contribution.authorization, - roleManager, + roleSet, spaceSettings ); updatedAuthorizations.push(...postAuthorizations); @@ -150,7 +150,7 @@ export class CalloutContributionAuthorizationService { private appendCredentialRules( contribution: ICalloutContribution, - communityPolicy?: IRoleManager, + communityPolicy?: IRoleSet, spaceSettings?: ISpaceSettings ): IAuthorizationPolicy { const authorization = contribution.authorization; @@ -204,7 +204,7 @@ export class CalloutContributionAuthorizationService { ]; if (communityPolicy && spaceSettings) { const roleCredentials = - this.roleManagerService.getCredentialsForRoleWithParents( + this.roleSetService.getCredentialsForRoleWithParents( communityPolicy, CommunityRoleType.ADMIN, spaceSettings diff --git a/src/domain/collaboration/callout/callout.module.ts b/src/domain/collaboration/callout/callout.module.ts index 73757a17f6..b3f9faa768 100644 --- a/src/domain/collaboration/callout/callout.module.ts +++ b/src/domain/collaboration/callout/callout.module.ts @@ -23,7 +23,7 @@ import { CalloutContributionDefaultsModule } from '../callout-contribution-defau import { CalloutContributionPolicyModule } from '../callout-contribution-policy/callout.contribution.policy.module'; import { CalloutContributionModule } from '../callout-contribution/callout.contribution.module'; import { PostModule } from '../post/post.module'; -import { RoleManagerModule } from '@domain/access/role-manager/role.manager.module'; +import { RoleSetModule } from '@domain/access/role-set/role.set.module'; @Module({ imports: [ @@ -34,7 +34,7 @@ import { RoleManagerModule } from '@domain/access/role-manager/role.manager.modu AuthorizationPolicyModule, AuthorizationModule, RoomModule, - RoleManagerModule, + RoleSetModule, EntityResolverModule, ContributorLookupModule, NamingModule, diff --git a/src/domain/collaboration/callout/callout.resolver.mutations.ts b/src/domain/collaboration/callout/callout.resolver.mutations.ts index cb83e91b18..1ca93ad742 100644 --- a/src/domain/collaboration/callout/callout.resolver.mutations.ts +++ b/src/domain/collaboration/callout/callout.resolver.mutations.ts @@ -228,8 +228,8 @@ export class CalloutResolverMutations { agentInfo.userID ); - const { roleManager: communityPolicy, spaceSettings } = - await this.namingService.getRoleManagerAndSettingsForCallout(callout.id); + const { roleSet: communityPolicy, spaceSettings } = + await this.namingService.getRoleSetAndSettingsForCallout(callout.id); contribution = await this.calloutContributionService.save(contribution); // Ensure settings are available const updatedAuthorizations = diff --git a/src/domain/collaboration/callout/callout.service.authorization.ts b/src/domain/collaboration/callout/callout.service.authorization.ts index e7313f4be4..f7f2170d6b 100644 --- a/src/domain/collaboration/callout/callout.service.authorization.ts +++ b/src/domain/collaboration/callout/callout.service.authorization.ts @@ -22,7 +22,7 @@ import { RoomAuthorizationService } from '@domain/communication/room/room.servic import { CalloutFramingAuthorizationService } from '../callout-framing/callout.framing.service.authorization'; import { CalloutContributionAuthorizationService } from '../callout-contribution/callout.contribution.service.authorization'; import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; -import { IRoleManager } from '@domain/access/role-manager/role.manager.interface'; +import { IRoleSet } from '@domain/access/role-set/role.set.interface'; @Injectable() export class CalloutAuthorizationService { @@ -37,7 +37,7 @@ export class CalloutAuthorizationService { public async applyAuthorizationPolicy( calloutInput: ICallout, parentAuthorization: IAuthorizationPolicy | undefined, - roleManager?: IRoleManager, + roleSet?: IRoleSet, spaceSettings?: ISpaceSettings ): Promise { const callout = await this.calloutService.getCalloutOrFail( @@ -90,7 +90,7 @@ export class CalloutAuthorizationService { await this.contributionAuthorizationService.applyAuthorizationPolicy( contribution.id, callout.authorization, - roleManager, + roleSet, spaceSettings ); updatedAuthorizations.push(...updatedContributionAuthorizations); diff --git a/src/domain/collaboration/collaboration/collaboration.module.ts b/src/domain/collaboration/collaboration/collaboration.module.ts index c5e972c918..8aa198d920 100644 --- a/src/domain/collaboration/collaboration/collaboration.module.ts +++ b/src/domain/collaboration/collaboration/collaboration.module.ts @@ -22,7 +22,7 @@ import { InnovationFlowModule } from '../innovation-flow/innovation.flow.module' import { SpaceDefaultsModule } from '@domain/space/space.defaults/space.defaults.module'; import { CalloutGroupsModule } from '../callout-groups/callout.group.module'; import { LicenseEngineModule } from '@core/license-engine/license.engine.module'; -import { RoleManagerModule } from '@domain/access/role-manager/role.manager.module'; +import { RoleSetModule } from '@domain/access/role-set/role.set.module'; @Module({ imports: [ @@ -32,7 +32,7 @@ import { RoleManagerModule } from '@domain/access/role-manager/role.manager.modu AuthorizationPolicyModule, AuthorizationModule, CalloutModule, - RoleManagerModule, + RoleSetModule, NamingModule, EntityResolverModule, StorageAggregatorResolverModule, diff --git a/src/domain/collaboration/collaboration/collaboration.resolver.mutations.ts b/src/domain/collaboration/collaboration/collaboration.resolver.mutations.ts index ce37e93ef2..f5ad1a5f01 100644 --- a/src/domain/collaboration/collaboration/collaboration.resolver.mutations.ts +++ b/src/domain/collaboration/collaboration/collaboration.resolver.mutations.ts @@ -85,8 +85,8 @@ export class CollaborationResolverMutations { agentInfo.userID ); - const { roleManager: communityPolicy, spaceSettings } = - await this.namingService.getRoleManagerAndSettingsForCollaboration( + const { roleSet: communityPolicy, spaceSettings } = + await this.namingService.getRoleSetAndSettingsForCollaboration( collaboration.id ); // callout needs to be saved to apply the authorization policy diff --git a/src/domain/collaboration/collaboration/collaboration.service.authorization.ts b/src/domain/collaboration/collaboration/collaboration.service.authorization.ts index c9a297ce56..0702551989 100644 --- a/src/domain/collaboration/collaboration/collaboration.service.authorization.ts +++ b/src/domain/collaboration/collaboration/collaboration.service.authorization.ts @@ -27,15 +27,15 @@ import { LicenseEngineService } from '@core/license-engine/license.engine.servic import { LicensePrivilege } from '@common/enums/license.privilege'; import { IAgent } from '@domain/agent/agent/agent.interface'; import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; -import { IRoleManager } from '@domain/access/role-manager'; -import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; +import { IRoleSet } from '@domain/access/role-set'; +import { RoleSetService } from '@domain/access/role-set/role.set.service'; @Injectable() export class CollaborationAuthorizationService { constructor( private licenseEngineService: LicenseEngineService, private collaborationService: CollaborationService, - private roleManagerService: RoleManagerService, + private roleSetService: RoleSetService, private authorizationPolicyService: AuthorizationPolicyService, private timelineAuthorizationService: TimelineAuthorizationService, private calloutAuthorizationService: CalloutAuthorizationService, @@ -45,7 +45,7 @@ export class CollaborationAuthorizationService { public async applyAuthorizationPolicy( collaborationInput: ICollaboration, parentAuthorization: IAuthorizationPolicy, - roleManager?: IRoleManager, + roleSet?: IRoleSet, spaceSettings?: ISpaceSettings, spaceAgent?: IAgent ): Promise { @@ -82,14 +82,14 @@ export class CollaborationAuthorizationService { collaboration.authorization = await this.appendCredentialRules( collaboration.authorization, - roleManager, + roleSet, spaceSettings, spaceAgent ); - if (roleManager && spaceSettings && spaceAgent) { + if (roleSet && spaceSettings && spaceAgent) { collaboration.authorization = this.appendCredentialRulesForContributors( collaboration.authorization, - roleManager, + roleSet, spaceSettings ); @@ -104,7 +104,7 @@ export class CollaborationAuthorizationService { const childUpdatedAuthorizations = await this.propagateAuthorizationToChildEntities( collaboration, - roleManager, + roleSet, spaceSettings ); updatedAuthorizations.push(...childUpdatedAuthorizations); @@ -114,7 +114,7 @@ export class CollaborationAuthorizationService { private async propagateAuthorizationToChildEntities( collaboration: ICollaboration, - roleManager?: IRoleManager, + roleSet?: IRoleSet, spaceSettings?: ISpaceSettings ): Promise { if ( @@ -135,7 +135,7 @@ export class CollaborationAuthorizationService { await this.calloutAuthorizationService.applyAuthorizationPolicy( callout, collaboration.authorization, - roleManager, + roleSet, spaceSettings ); updatedAuthorizations.push(...updatedCalloutAuthorizations); @@ -147,11 +147,11 @@ export class CollaborationAuthorizationService { collaboration.authorization ); - if (roleManager && spaceSettings) { + if (roleSet && spaceSettings) { const extendedAuthorizationContributors = this.appendCredentialRulesForContributors( clonedAuthorization, - roleManager, + roleSet, spaceSettings ); const timelineAuthorizations = @@ -173,20 +173,20 @@ export class CollaborationAuthorizationService { } private getContributorCredentials( - roleManager: IRoleManager, + roleSet: IRoleSet, spaceSettings: ISpaceSettings ): ICredentialDefinition[] { // add challenge members - let contributorCriterias = this.roleManagerService.getCredentialsForRole( - roleManager, + let contributorCriterias = this.roleSetService.getCredentialsForRole( + roleSet, CommunityRoleType.MEMBER, spaceSettings ); // optionally add space members if (spaceSettings.collaboration.inheritMembershipRights) { contributorCriterias = - this.roleManagerService.getCredentialsForRoleWithParents( - roleManager, + this.roleSetService.getCredentialsForRoleWithParents( + roleSet, CommunityRoleType.MEMBER, spaceSettings ); @@ -201,14 +201,14 @@ export class CollaborationAuthorizationService { private async appendCredentialRules( authorization: IAuthorizationPolicy | undefined, - roleManager?: IRoleManager, + roleSet?: IRoleSet, spaceSettings?: ISpaceSettings, spaceAgent?: IAgent ): Promise { if (!authorization) throw new EntityNotInitializedException( `Authorization definition not found for Context: ${JSON.stringify( - roleManager + roleSet )}`, LogContext.COLLABORATION ); @@ -216,7 +216,7 @@ export class CollaborationAuthorizationService { const newRules: IAuthorizationPolicyRuleCredential[] = []; // For templates these will not be available - if (!roleManager || !spaceSettings || !spaceAgent) { + if (!roleSet || !spaceSettings || !spaceAgent) { return authorization; } const saveAsTemplateEnabled = @@ -225,8 +225,8 @@ export class CollaborationAuthorizationService { spaceAgent ); if (saveAsTemplateEnabled) { - const adminCriterias = this.roleManagerService.getCredentialsForRole( - roleManager, + const adminCriterias = this.roleSetService.getCredentialsForRole( + roleSet, CommunityRoleType.ADMIN, spaceSettings ); @@ -253,7 +253,7 @@ export class CollaborationAuthorizationService { public appendCredentialRulesForContributors( authorization: IAuthorizationPolicy | undefined, - policy: IRoleManager, + policy: IRoleSet, spaceSettings: ISpaceSettings ): IAuthorizationPolicy { if (!authorization) diff --git a/src/domain/collaboration/collaboration/collaboration.service.ts b/src/domain/collaboration/collaboration/collaboration.service.ts index cdb0dcc0c7..9e81a0705b 100644 --- a/src/domain/collaboration/collaboration/collaboration.service.ts +++ b/src/domain/collaboration/collaboration/collaboration.service.ts @@ -53,7 +53,7 @@ import { SpaceLevel } from '@common/enums/space.level'; import { Callout } from '@domain/collaboration/callout'; import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type'; import { CreateInnovationFlowInput } from '../innovation-flow/dto/innovation.flow.dto.create'; -import { IRoleManager } from '@domain/access/role-manager'; +import { IRoleSet } from '@domain/access/role-set'; @Injectable() export class CollaborationService { @@ -690,12 +690,12 @@ export class CollaborationService { return result.whiteboardsCount; } - public async getRoleManager(collaborationID: string): Promise { - const { roleManager } = - await this.namingService.getRoleManagerAndSettingsForCollaboration( + public async getRoleSet(collaborationID: string): Promise { + const { roleSet } = + await this.namingService.getRoleSetAndSettingsForCollaboration( collaborationID ); - return roleManager; + return roleSet; } public async updateCalloutsSortOrder( diff --git a/src/domain/collaboration/post/post.module.ts b/src/domain/collaboration/post/post.module.ts index 6838734c87..30b280a55d 100644 --- a/src/domain/collaboration/post/post.module.ts +++ b/src/domain/collaboration/post/post.module.ts @@ -11,14 +11,14 @@ import { PostAuthorizationService } from './post.service.authorization'; import { ProfileModule } from '@domain/common/profile/profile.module'; import { RoomModule } from '@domain/communication/room/room.module'; import { ContributorLookupModule } from '@services/infrastructure/contributor-lookup/contributor.lookup.module'; -import { RoleManagerModule } from '@domain/access/role-manager/role.manager.module'; +import { RoleSetModule } from '@domain/access/role-set/role.set.module'; @Module({ imports: [ AuthorizationPolicyModule, AuthorizationModule, RoomModule, - RoleManagerModule, + RoleSetModule, VisualModule, ContributorLookupModule, ProfileModule, diff --git a/src/domain/collaboration/post/post.service.authorization.ts b/src/domain/collaboration/post/post.service.authorization.ts index e4e1b22c0e..fdd8928f0c 100644 --- a/src/domain/collaboration/post/post.service.authorization.ts +++ b/src/domain/collaboration/post/post.service.authorization.ts @@ -19,22 +19,22 @@ import { CommunityRoleType } from '@common/enums/community.role'; import { RelationshipNotFoundException } from '@common/exceptions/relationship.not.found.exception'; import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; -import { IRoleManager } from '@domain/access/role-manager/role.manager.interface'; -import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; +import { IRoleSet } from '@domain/access/role-set/role.set.interface'; +import { RoleSetService } from '@domain/access/role-set/role.set.service'; @Injectable() export class PostAuthorizationService { constructor( private authorizationPolicyService: AuthorizationPolicyService, private roomAuthorizationService: RoomAuthorizationService, - private roleManagerService: RoleManagerService, + private roleSetService: RoleSetService, private profileAuthorizationService: ProfileAuthorizationService ) {} async applyAuthorizationPolicy( post: IPost, parentAuthorization: IAuthorizationPolicy | undefined, - roleManager?: IRoleManager, + roleSet?: IRoleSet, spaceSettings?: ISpaceSettings ): Promise { if (!post.profile) { @@ -73,7 +73,7 @@ export class PostAuthorizationService { // Extend to give the user creating the post more rights post.authorization = this.appendCredentialRules( post, - roleManager, + roleSet, spaceSettings ); updatedAuthorizations.push(post.authorization); @@ -91,7 +91,7 @@ export class PostAuthorizationService { private appendCredentialRules( post: IPost, - roleManager?: IRoleManager, + roleSet?: IRoleSet, spaceSettings?: ISpaceSettings ): IAuthorizationPolicy { const authorization = post.authorization; @@ -124,10 +124,10 @@ export class PostAuthorizationService { }, ]; - if (roleManager && spaceSettings) { + if (roleSet && spaceSettings) { const roleCredentials = - this.roleManagerService.getCredentialsForRoleWithParents( - roleManager, + this.roleSetService.getCredentialsForRoleWithParents( + roleSet, CommunityRoleType.ADMIN, spaceSettings ); diff --git a/src/domain/communication/room/room.service.mentions.ts b/src/domain/communication/room/room.service.mentions.ts index 077bbd01c9..9fd073f4b4 100644 --- a/src/domain/communication/room/room.service.mentions.ts +++ b/src/domain/communication/room/room.service.mentions.ts @@ -50,10 +50,9 @@ export class RoomServiceMentions { ); // The ID of the actual community where the question is being asked - const space = - await this.communityResolverService.getSpaceForRoleManagerOrFail( - community.id - ); + const space = await this.communityResolverService.getSpaceForRoleSetOrFail( + community.id + ); return space.id; } diff --git a/src/domain/community/application/application.entity.ts b/src/domain/community/application/application.entity.ts index 7628c8e1da..2c767a3b67 100644 --- a/src/domain/community/application/application.entity.ts +++ b/src/domain/community/application/application.entity.ts @@ -12,7 +12,7 @@ import { NVP } from '@domain/common/nvp/nvp.entity'; import { User } from '@domain/community/user/user.entity'; import { AuthorizableEntity } from '@domain/common/entity/authorizable-entity'; import { IQuestion } from '@domain/common/question/question.interface'; -import { RoleManager } from '@domain/access/role-manager'; +import { RoleSet } from '@domain/access/role-set'; @Entity() export class Application extends AuthorizableEntity implements IApplication { @OneToOne(() => Lifecycle, { @@ -34,10 +34,10 @@ export class Application extends AuthorizableEntity implements IApplication { }) user?: User; - @ManyToOne(() => RoleManager, manager => manager.applications, { + @ManyToOne(() => RoleSet, manager => manager.applications, { eager: false, cascade: false, onDelete: 'CASCADE', }) - roleManager?: RoleManager; + roleSet?: RoleSet; } diff --git a/src/domain/community/application/application.interface.ts b/src/domain/community/application/application.interface.ts index d508a63581..9722b9a06a 100644 --- a/src/domain/community/application/application.interface.ts +++ b/src/domain/community/application/application.interface.ts @@ -3,13 +3,13 @@ import { IUser } from '@domain/community/user/user.interface'; import { Field, ObjectType } from '@nestjs/graphql'; import { IAuthorizable } from '@domain/common/entity/authorizable-entity'; import { IQuestion } from '@domain/common/question/question.interface'; -import { IRoleManager } from '@domain/access/role-manager'; +import { IRoleSet } from '@domain/access/role-set'; @ObjectType('Application') export abstract class IApplication extends IAuthorizable { user?: IUser; - roleManager?: IRoleManager; + roleSet?: IRoleSet; @Field(() => ILifecycle, { nullable: false }) lifecycle?: ILifecycle; diff --git a/src/domain/community/application/application.service.ts b/src/domain/community/application/application.service.ts index b624798df4..ee7ca49cd2 100644 --- a/src/domain/community/application/application.service.ts +++ b/src/domain/community/application/application.service.ts @@ -153,7 +153,7 @@ export class ApplicationService { states: string[] = [] ): Promise { const findOpts: FindManyOptions = { - relations: { roleManager: true }, + relations: { roleSet: true }, where: { user: { id: userID } }, }; diff --git a/src/domain/community/application/dto/application.dto.create.ts b/src/domain/community/application/dto/application.dto.create.ts index 37a7406b8c..d508a7ace4 100644 --- a/src/domain/community/application/dto/application.dto.create.ts +++ b/src/domain/community/application/dto/application.dto.create.ts @@ -2,7 +2,7 @@ import { CreateNVPInput } from '@domain/common/nvp'; export class CreateApplicationInput { userID!: string; - roleManagerID!: string; + roleSetID!: string; questions!: CreateNVPInput[]; } diff --git a/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts b/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts index e388355727..a3e4e53479 100644 --- a/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts +++ b/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts @@ -65,19 +65,19 @@ export class CommunityRoleApplicationLifecycleOptionsProvider { const application = await this.applicationService.getApplicationOrFail( event.parentID, { - relations: { roleManager: true, user: true }, + relations: { roleSet: true, user: true }, } ); const userID = application.user?.id; - const roleManager = application.roleManager; - if (!userID || !roleManager) + const roleSet = application.roleSet; + if (!userID || !roleSet) throw new EntityNotInitializedException( `Lifecycle not initialized on Application: ${application.id}`, LogContext.COMMUNITY ); await this.communityRoleService.assignUserToRole( - roleManager, + roleSet, CommunityRoleType.MEMBER, userID, event.agentInfo, diff --git a/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts b/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts index c255adb757..d1b82bfcc0 100644 --- a/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts +++ b/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts @@ -88,15 +88,15 @@ export class CommunityRoleInvitationLifecycleOptionsProvider { event.parentID, { relations: { - roleManager: { - parentRoleManager: true, + roleSet: { + parentRoleSet: true, }, }, } ); const contributorID = invitation.invitedContributor; - const roleManager = invitation.roleManager; - if (!contributorID || !roleManager) { + const roleSet = invitation.roleSet; + if (!contributorID || !roleSet) { throw new EntityNotInitializedException( `Lifecycle not initialized on Invitation: ${invitation.id}`, LogContext.COMMUNITY @@ -104,14 +104,14 @@ export class CommunityRoleInvitationLifecycleOptionsProvider { } if (invitation.invitedToParent) { - if (!roleManager.parentRoleManager) { + if (!roleSet.parentRoleSet) { throw new EntityNotInitializedException( `Unable to load parent community when flag to add is set: ${invitation.id}`, LogContext.COMMUNITY ); } await this.communityRoleService.assignContributorToRole( - roleManager.parentRoleManager, + roleSet.parentRoleSet, CommunityRoleType.MEMBER, contributorID, invitation.contributorType, @@ -120,7 +120,7 @@ export class CommunityRoleInvitationLifecycleOptionsProvider { ); } await this.communityRoleService.assignContributorToRole( - roleManager, + roleSet, CommunityRoleType.MEMBER, contributorID, invitation.contributorType, diff --git a/src/domain/community/community-role/community.role.module.ts b/src/domain/community/community-role/community.role.module.ts index 199b686963..c13aab9c4f 100644 --- a/src/domain/community/community-role/community.role.module.ts +++ b/src/domain/community/community-role/community.role.module.ts @@ -22,7 +22,7 @@ import { ContributionReporterModule } from '@services/external/elasticsearch/con import { NotificationAdapterModule } from '@services/adapters/notification-adapter/notification.adapter.module'; import { ActivityAdapterModule } from '@services/adapters/activity-adapter/activity.adapter.module'; import { LifecycleModule } from '@domain/common/lifecycle/lifecycle.module'; -import { RoleManagerModule } from '@domain/access/role-manager/role.manager.module'; +import { RoleSetModule } from '@domain/access/role-set/role.set.module'; import { RoleModule } from '@domain/access/role/role.module'; @Module({ @@ -32,7 +32,7 @@ import { RoleModule } from '@domain/access/role/role.module'; EntityResolverModule, AgentModule, CommunityModule, - RoleManagerModule, + RoleSetModule, RoleModule, UserModule, ContributorModule, diff --git a/src/domain/community/community-role/community.role.resolver.mutations.ts b/src/domain/community/community-role/community.role.resolver.mutations.ts index f50dc2bfed..3f11099dd4 100644 --- a/src/domain/community/community-role/community.role.resolver.mutations.ts +++ b/src/domain/community/community-role/community.role.resolver.mutations.ts @@ -56,7 +56,7 @@ import { CommunityRoleInvitationLifecycleOptionsProvider } from './community.rol import { CommunityRoleApplicationLifecycleOptionsProvider } from './community.role.lifecycle.application.options.provider'; import { IVirtualContributor } from '../virtual-contributor/virtual.contributor.interface'; import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service'; -import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; +import { RoleSetService } from '@domain/access/role-set/role.set.service'; import { CommunityResolverService } from '@services/infrastructure/entity-resolver/community.resolver.service'; @Resolver() @@ -70,7 +70,7 @@ export class CommunityRoleResolverMutations { private virtualContributorService: VirtualContributorService, private communityRoleService: CommunityRoleService, private communityService: CommunityService, - private roleManagerService: RoleManagerService, + private roleSetService: RoleSetService, private communityResolverService: CommunityResolverService, private communityAuthorizationService: CommunityAuthorizationService, private communityLifecycleApplicationOptionsProvider: CommunityRoleApplicationLifecycleOptionsProvider, @@ -94,8 +94,8 @@ export class CommunityRoleResolverMutations { @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: AssignCommunityRoleToUserInput ): Promise { - const roleManager = await this.roleManagerService.getRoleManagerOrFail( - roleData.roleManagerID + const roleSet = await this.roleSetService.getRoleSetOrFail( + roleData.roleSetID ); let requiredPrivilege = AuthorizationPrivilege.GRANT; @@ -105,13 +105,13 @@ export class CommunityRoleResolverMutations { this.authorizationService.grantAccessOrFail( agentInfo, - roleManager.authorization, + roleSet.authorization, requiredPrivilege, - `assign user community role: ${roleManager.id}` + `assign user community role: ${roleSet.id}` ); await this.communityRoleService.assignUserToRole( - roleManager, + roleSet, roleData.role, roleData.userID, agentInfo, @@ -136,18 +136,18 @@ export class CommunityRoleResolverMutations { @Args('roleData') roleData: AssignCommunityRoleToOrganizationInput ): Promise { - const roleManager = await this.roleManagerService.getRoleManagerOrFail( - roleData.roleManagerID + const roleSet = await this.roleSetService.getRoleSetOrFail( + roleData.roleSetID ); this.authorizationService.grantAccessOrFail( agentInfo, - roleManager.authorization, + roleSet.authorization, AuthorizationPrivilege.GRANT, - `assign organization community role: ${roleManager.id}` + `assign organization community role: ${roleSet.id}` ); return await this.communityRoleService.assignOrganizationToRole( - roleManager, + roleSet, roleData.role, roleData.organizationID ); @@ -163,15 +163,15 @@ export class CommunityRoleResolverMutations { @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: AssignCommunityRoleToVirtualInput ): Promise { - const roleManager = await this.roleManagerService.getRoleManagerOrFail( - roleData.roleManagerID + const roleSet = await this.roleSetService.getRoleSetOrFail( + roleData.roleSetID ); let requiredPrivilege = AuthorizationPrivilege.GRANT; if (roleData.role === CommunityRoleType.MEMBER) { const sameAccount = await this.communityRoleService.isCommunityAccountMatchingVcAccount( - roleManager.id, + roleSet.id, roleData.virtualContributorID ); if (sameAccount) { @@ -184,21 +184,21 @@ export class CommunityRoleResolverMutations { this.authorizationService.grantAccessOrFail( agentInfo, - roleManager.authorization, + roleSet.authorization, requiredPrivilege, - `assign virtual community role: ${roleManager.id}` + `assign virtual community role: ${roleSet.id}` ); // Also require ACCESS_VIRTUAL_CONTRIBUTORS to assign a virtual contributor this.authorizationService.grantAccessOrFail( agentInfo, - roleManager.authorization, + roleSet.authorization, AuthorizationPrivilege.ACCESS_VIRTUAL_CONTRIBUTOR, - `assign virtual community role VC privilege: ${roleManager.id}` + `assign virtual community role VC privilege: ${roleSet.id}` ); await this.communityRoleService.assignVirtualToRole( - roleManager, + roleSet, roleData.role, roleData.virtualContributorID, agentInfo, @@ -219,17 +219,15 @@ export class CommunityRoleResolverMutations { @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: RemoveCommunityRoleFromUserInput ): Promise { - const roleManager = await this.roleManagerService.getRoleManagerOrFail( - roleData.roleManagerID + const roleSet = await this.roleSetService.getRoleSetOrFail( + roleData.roleSetID ); // Extend the authorization policy with a credential rule to assign the GRANT privilege // to the user specified in the incoming mutation. Then if it is the same user as is logged // in then the user will have the GRANT privilege + so can carry out the mutation const community = - await this.communityResolverService.getCommunityForRoleManager( - roleManager.id - ); + await this.communityResolverService.getCommunityForRoleSet(roleSet.id); const extendedAuthorization = this.communityAuthorizationService.extendAuthorizationPolicyForSelfRemoval( @@ -241,11 +239,11 @@ export class CommunityRoleResolverMutations { agentInfo, extendedAuthorization, AuthorizationPrivilege.GRANT, - `remove user from community role: ${roleManager.id}` + `remove user from community role: ${roleSet.id}` ); await this.communityRoleService.removeUserFromRole( - roleManager, + roleSet, roleData.role, roleData.userID ); @@ -268,18 +266,18 @@ export class CommunityRoleResolverMutations { @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: RemoveCommunityRoleFromOrganizationInput ): Promise { - const roleManager = await this.roleManagerService.getRoleManagerOrFail( - roleData.roleManagerID + const roleSet = await this.roleSetService.getRoleSetOrFail( + roleData.roleSetID ); await this.authorizationService.grantAccessOrFail( agentInfo, - roleManager.authorization, + roleSet.authorization, AuthorizationPrivilege.GRANT, - `remove community role organization: ${roleManager.id}` + `remove community role organization: ${roleSet.id}` ); return await this.communityRoleService.removeOrganizationFromRole( - roleManager, + roleSet, roleData.role, roleData.organizationID ); @@ -295,7 +293,7 @@ export class CommunityRoleResolverMutations { @Args('roleData') roleData: RemoveCommunityRoleFromVirtualInput ): Promise { const community = await this.communityService.getCommunityOrFail( - roleData.roleManagerID + roleData.roleSetID ); // Extend the authorization policy with a credential rule to assign the GRANT privilege @@ -335,7 +333,7 @@ export class CommunityRoleResolverMutations { @Args('applicationData') applicationData: CommunityRoleApplyInput ): Promise { const community = await this.communityService.getCommunityOrFail( - applicationData.roleManagerID, + applicationData.roleSetID, { relations: { parentCommunity: true, @@ -368,7 +366,7 @@ export class CommunityRoleResolverMutations { } let application = await this.communityRoleService.createApplication({ - roleManagerID: community.id, + roleSetID: community.id, questions: applicationData.questions, userID: agentInfo.userID, }); @@ -404,7 +402,7 @@ export class CommunityRoleResolverMutations { invitationData: CreateInvitationForContributorsOnCommunityInput ): Promise { const community = await this.communityService.getCommunityOrFail( - invitationData.roleManagerID, + invitationData.roleSetID, { relations: { parentCommunity: { @@ -498,7 +496,7 @@ export class CommunityRoleResolverMutations { welcomeMessage?: string ): Promise { const input: CreateInvitationInput = { - roleManagerID: community.id, + roleSetID: community.id, invitedContributor: invitedContributor.id, createdBy: agentInfo.userID, invitedToParent, @@ -561,7 +559,7 @@ export class CommunityRoleResolverMutations { invitationData: CreatePlatformInvitationOnCommunityInput ): Promise { const community = await this.communityService.getCommunityOrFail( - invitationData.roleManagerID, + invitationData.roleSetID, { relations: { parentCommunity: { @@ -652,7 +650,7 @@ export class CommunityRoleResolverMutations { @Args('joinCommunityData') joiningData: CommunityJoinInput ): Promise { const community = await this.communityService.getCommunityOrFail( - joiningData.roleManagerID + joiningData.roleSetID ); const membershipStatus = await this.communityRoleService.getMembershipStatus(agentInfo, community); diff --git a/src/domain/community/community-role/community.role.service.events.ts b/src/domain/community/community-role/community.role.service.events.ts index 7418419491..352734ba06 100644 --- a/src/domain/community/community-role/community.role.service.events.ts +++ b/src/domain/community/community-role/community.role.service.events.ts @@ -49,10 +49,9 @@ export class CommunityRoleEventsService { await this.notificationAdapter.communityNewMember(notificationInput); // Record the contribution events - const space = - await this.communityResolverService.getSpaceForRoleManagerOrFail( - community.id - ); + const space = await this.communityResolverService.getSpaceForRoleSetOrFail( + community.id + ); switch (space.type) { case SpaceType.SPACE: this.contributionReporter.spaceJoined( diff --git a/src/domain/community/community-role/community.role.service.ts b/src/domain/community/community-role/community.role.service.ts index 162b1928be..8471b8c21c 100644 --- a/src/domain/community/community-role/community.role.service.ts +++ b/src/domain/community/community-role/community.role.service.ts @@ -42,8 +42,8 @@ import { IPlatformInvitation } from '@platform/invitation'; import { CreatePlatformInvitationInput } from '@platform/invitation/dto/platform.invitation.dto.create'; import { AiServerAdapter } from '@services/adapters/ai-server-adapter/ai.server.adapter'; import { CommunityService } from '../community/community.service'; -import { IRoleManager } from '@domain/access/role-manager'; -import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; +import { IRoleSet } from '@domain/access/role-set'; +import { RoleSetService } from '@domain/access/role-set/role.set.service'; import { RoleService } from '@domain/access/role/role.service'; @Injectable() @@ -60,27 +60,27 @@ export class CommunityRoleService { private communityResolverService: CommunityResolverService, private communityRoleEventsService: CommunityRoleEventsService, private communityService: CommunityService, - private roleManagerService: RoleManagerService, + private roleSetService: RoleSetService, private roleService: RoleService, private aiServerAdapter: AiServerAdapter, //TODO: remove this asap @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService ) {} - public async removeAllCommunityRoles(roleManager: IRoleManager) { + public async removeAllCommunityRoles(roleSet: IRoleSet) { // Remove all issued role credentials for contributors for (const roleType of Object.values(CommunityRoleType)) { - const users = await this.getUsersWithRole(roleManager, roleType); + const users = await this.getUsersWithRole(roleSet, roleType); for (const user of users) { - await this.removeUserFromRole(roleManager, roleType, user.id, false); + await this.removeUserFromRole(roleSet, roleType, user.id, false); } const organizations = await this.getOrganizationsWithRole( - roleManager, + roleSet, roleType ); for (const organization of organizations) { await this.removeOrganizationFromRole( - roleManager, + roleSet, roleType, organization.id, false @@ -161,11 +161,11 @@ export class CommunityRoleService { private async findOpenApplication( userID: string, - roleManagerID: string + roleSetID: string ): Promise { const applications = await this.applicationService.findExistingApplications( userID, - roleManagerID + roleSetID ); for (const application of applications) { // skip any finalized applications; only want to return pending applications @@ -180,11 +180,11 @@ export class CommunityRoleService { private async findOpenInvitation( contributorID: string, - roleManagerID: string + roleSetID: string ): Promise { const invitations = await this.invitationService.findExistingInvitations( contributorID, - roleManagerID + roleSetID ); for (const invitation of invitations) { // skip any finalized applications; only want to return pending applications @@ -198,12 +198,12 @@ export class CommunityRoleService { } async getUsersWithRole( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType, limit?: number ): Promise { const membershipCredential = this.getCredentialDefinitionForRole( - roleManager, + roleSet, roleType ); return await this.userService.usersWithCredentials( @@ -216,11 +216,11 @@ export class CommunityRoleService { } async getVirtualContributorsWithRole( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType ): Promise { const membershipCredential = this.getCredentialDefinitionForRole( - roleManager, + roleSet, roleType ); return await this.virtualContributorService.virtualContributorsWithCredentials( @@ -232,11 +232,11 @@ export class CommunityRoleService { } async getOrganizationsWithRole( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType ): Promise { const membershipCredential = this.getCredentialDefinitionForRole( - roleManager, + roleSet, roleType ); return await this.organizationService.organizationsWithCredentials({ @@ -246,12 +246,12 @@ export class CommunityRoleService { } async countContributorsPerRole( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType, contributorType: CommunityContributorType ): Promise { const membershipCredential = this.getCredentialDefinitionForRole( - roleManager, + roleSet, roleType ); @@ -273,18 +273,15 @@ export class CommunityRoleService { } getCredentialDefinitionForRole( - roleManager: IRoleManager, + roleSet: IRoleSet, role: CommunityRoleType ): CredentialDefinition { - const credential = this.roleManagerService.getCredentialForRole( - roleManager, - role - ); + const credential = this.roleSetService.getCredentialForRole(roleSet, role); return credential; } async assignContributorToRole( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType, contributorID: string, contributorType: CommunityContributorType, @@ -294,7 +291,7 @@ export class CommunityRoleService { switch (contributorType) { case CommunityContributorType.USER: return await this.assignUserToRole( - roleManager, + roleSet, roleType, contributorID, agentInfo, @@ -302,13 +299,13 @@ export class CommunityRoleService { ); case CommunityContributorType.ORGANIZATION: return await this.assignOrganizationToRole( - roleManager, + roleSet, roleType, contributorID ); case CommunityContributorType.VIRTUAL: return await this.assignVirtualToRole( - roleManager, + roleSet, roleType, contributorID, agentInfo, @@ -323,7 +320,7 @@ export class CommunityRoleService { } async assignUserToRole( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType, userID: string, agentInfo?: AgentInfo, @@ -331,25 +328,21 @@ export class CommunityRoleService { ): Promise { const { user, agent } = await this.userService.getUserAndAgent(userID); const { isMember: hasMemberRoleInParent, parentCommunity } = - await this.isMemberInParentCommunity(agent, roleManager.id); + await this.isMemberInParentCommunity(agent, roleSet.id); if (!hasMemberRoleInParent) { throw new ValidationException( - `Unable to assign Agent (${agent.id}) to community (${roleManager.id}): agent is not a member of parent community ${parentCommunity?.id}`, + `Unable to assign Agent (${agent.id}) to community (${roleSet.id}): agent is not a member of parent community ${parentCommunity?.id}`, LogContext.SPACES ); } - const userAlreadyHasRole = await this.isInRole( - agent, - roleManager, - roleType - ); + const userAlreadyHasRole = await this.isInRole(agent, roleSet, roleType); if (userAlreadyHasRole) { return user; } user.agent = await this.assignContributorAgentToRole( - roleManager, + roleSet, roleType, agent, CommunityContributorType.USER @@ -373,7 +366,7 @@ export class CommunityRoleService { await this.contributorAddedToRole( user, - roleManager, + roleSet, roleType, agentInfo, triggerNewMemberEvents @@ -384,16 +377,14 @@ export class CommunityRoleService { private async contributorAddedToRole( contributor: IContributor, - roleManager: IRoleManager, + roleSet: IRoleSet, role: CommunityRoleType, agentInfo?: AgentInfo, triggerNewMemberEvents = false ) { if (role === CommunityRoleType.MEMBER) { const community = - await this.communityResolverService.getCommunityForRoleManager( - roleManager.id - ); + await this.communityResolverService.getCommunityForRoleSet(roleSet.id); this.communityService.addMemberToCommunication(contributor, community); if (agentInfo) { @@ -406,7 +397,7 @@ export class CommunityRoleService { if (triggerNewMemberEvents) { const levelZeroSpaceID = await this.communityService.getLevelZeroSpaceIdForCommunity( - roleManager + roleSet ); const displayName = await this.communityService.getDisplayName(community); @@ -423,7 +414,7 @@ export class CommunityRoleService { } async assignVirtualToRole( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType, virtualContributorID: string, agentInfo?: AgentInfo, @@ -434,31 +425,27 @@ export class CommunityRoleService { virtualContributorID ); const { isMember: hasMemberRoleInParent, parentCommunity } = - await this.isMemberInParentCommunity(agent, roleManager.id); + await this.isMemberInParentCommunity(agent, roleSet.id); if (!hasMemberRoleInParent) { if (!parentCommunity) { throw new ValidationException( - `Unable to find parent community for community ${roleManager.id}`, + `Unable to find parent community for community ${roleSet.id}`, LogContext.SPACES ); } throw new ValidationException( - `Unable to assign Agent (${agent.id}) to community (${roleManager.id}): agent is not a member of parent community ${parentCommunity.id}`, + `Unable to assign Agent (${agent.id}) to community (${roleSet.id}): agent is not a member of parent community ${parentCommunity.id}`, LogContext.SPACES ); } - const virtualAlreadyHasRole = await this.isInRole( - agent, - roleManager, - roleType - ); + const virtualAlreadyHasRole = await this.isInRole(agent, roleSet, roleType); if (virtualAlreadyHasRole) { return virtualContributor; } virtualContributor.agent = await this.assignContributorAgentToRole( - roleManager, + roleSet, roleType, agent, CommunityContributorType.VIRTUAL @@ -466,16 +453,15 @@ export class CommunityRoleService { await this.contributorAddedToRole( virtualContributor, - roleManager, + roleSet, roleType, agentInfo, triggerNewMemberEvents ); // TO: THIS BREAKS THE DECOUPLING - const space = - await this.communityResolverService.getSpaceForRoleManagerOrFail( - roleManager.id - ); + const space = await this.communityResolverService.getSpaceForRoleSetOrFail( + roleSet.id + ); this.aiServerAdapter.ensureContextIsLoaded(space.id); return virtualContributor; } @@ -509,7 +495,7 @@ export class CommunityRoleService { } async assignOrganizationToRole( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType, organizationID: string ): Promise { @@ -517,7 +503,7 @@ export class CommunityRoleService { await this.organizationService.getOrganizationAndAgent(organizationID); organization.agent = await this.assignContributorAgentToRole( - roleManager, + roleSet, roleType, agent, CommunityContributorType.ORGANIZATION @@ -527,7 +513,7 @@ export class CommunityRoleService { } async removeUserFromRole( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType, userID: string, validatePolicyLimits = true @@ -535,29 +521,27 @@ export class CommunityRoleService { const { user, agent } = await this.userService.getUserAndAgent(userID); user.agent = await this.removeContributorFromRole( - roleManager, + roleSet, roleType, agent, CommunityContributorType.USER, validatePolicyLimits ); - const parentRoleManager = - await this.roleManagerService.getParentRoleManager(roleManager); - if (roleType === CommunityRoleType.ADMIN && parentRoleManager) { + const parentRoleSet = await this.roleSetService.getParentRoleSet(roleSet); + if (roleType === CommunityRoleType.ADMIN && parentRoleSet) { // Check if an admin anywhere else in the community - const peerRoleManagers = - await this.roleManagerService.getPeerRoleManagers( - parentRoleManager, - roleManager - ); - const hasAnotherAdminRole = peerRoleManagers.some(pc => + const peerRoleSets = await this.roleSetService.getPeerRoleSets( + parentRoleSet, + roleSet + ); + const hasAnotherAdminRole = peerRoleSets.some(pc => this.isInRole(agent, pc, CommunityRoleType.ADMIN) ); if (!hasAnotherAdminRole) { await this.removeContributorToImplicitRole( - parentRoleManager, + parentRoleSet, agent, CommunityRoleImplicit.SUBSPACE_ADMIN ); @@ -566,9 +550,7 @@ export class CommunityRoleService { if (roleType === CommunityRoleType.MEMBER) { const community = - await this.communityResolverService.getCommunityForRoleManager( - roleManager.id - ); + await this.communityResolverService.getCommunityForRoleSet(roleSet.id); await this.communityService.removeMemberFromCommunication( community, @@ -580,7 +562,7 @@ export class CommunityRoleService { } async removeOrganizationFromRole( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType, organizationID: string, validatePolicyLimits = true @@ -589,7 +571,7 @@ export class CommunityRoleService { await this.organizationService.getOrganizationAndAgent(organizationID); organization.agent = await this.removeContributorFromRole( - roleManager, + roleSet, roleType, agent, CommunityContributorType.ORGANIZATION, @@ -600,7 +582,7 @@ export class CommunityRoleService { } async removeVirtualFromRole( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType, virtualContributorID: string, validatePolicyLimits = true @@ -611,7 +593,7 @@ export class CommunityRoleService { ); virtualContributor.agent = await this.removeContributorFromRole( - roleManager, + roleSet, roleType, agent, CommunityContributorType.VIRTUAL, @@ -632,18 +614,18 @@ export class CommunityRoleService { } private async validateUserCommunityPolicy( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType, action: CommunityContributorsUpdateType ) { const userMembersCount = await this.countContributorsPerRole( - roleManager, + roleSet, roleType, CommunityContributorType.USER ); - const roleDefinition = this.roleManagerService.getRoleDefinition( - roleManager, + const roleDefinition = this.roleSetService.getRoleDefinition( + roleSet, roleType ); @@ -669,18 +651,18 @@ export class CommunityRoleService { } private async validateOrganizationCommunityPolicy( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType, action: CommunityContributorsUpdateType ) { const orgMemberCount = await this.countContributorsPerRole( - roleManager, + roleSet, roleType, CommunityContributorType.ORGANIZATION ); - const roleDefinition = this.roleManagerService.getRoleDefinition( - roleManager, + const roleDefinition = this.roleSetService.getRoleDefinition( + roleSet, roleType ); @@ -707,37 +689,33 @@ export class CommunityRoleService { } private async validateCommunityPolicyLimits( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType, action: CommunityContributorsUpdateType, contributorType: CommunityContributorType ) { if (contributorType === CommunityContributorType.USER) - await this.validateUserCommunityPolicy(roleManager, roleType, action); + await this.validateUserCommunityPolicy(roleSet, roleType, action); if (contributorType === CommunityContributorType.ORGANIZATION) - await this.validateOrganizationCommunityPolicy( - roleManager, - roleType, - action - ); + await this.validateOrganizationCommunityPolicy(roleSet, roleType, action); } public async assignContributorAgentToRole( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType, agent: IAgent, contributorType: CommunityContributorType ): Promise { await this.validateCommunityPolicyLimits( - roleManager, + roleSet, roleType, CommunityContributorsUpdateType.ASSIGN, contributorType ); - const roleCredential = this.roleManagerService.getCredentialForRole( - roleManager, + const roleCredential = this.roleSetService.getCredentialForRole( + roleSet, roleType ); @@ -749,11 +727,11 @@ export class CommunityRoleService { } private async assignContributorToImplicitRole( - roleManager: IRoleManager, + roleSet: IRoleSet, agent: IAgent, role: CommunityRoleImplicit ): Promise { - const credential = this.getCredentialForImplicitRole(roleManager, role); + const credential = this.getCredentialForImplicitRole(roleSet, role); return await this.agentService.grantCredential({ agentID: agent.id, @@ -763,11 +741,11 @@ export class CommunityRoleService { } private async removeContributorToImplicitRole( - roleManager: IRoleManager, + roleSet: IRoleSet, agent: IAgent, role: CommunityRoleImplicit ): Promise { - const credential = this.getCredentialForImplicitRole(roleManager, role); + const credential = this.getCredentialForImplicitRole(roleSet, role); return await this.agentService.revokeCredential({ agentID: agent.id, @@ -777,12 +755,12 @@ export class CommunityRoleService { } private getCredentialForImplicitRole( - roleManager: IRoleManager, + roleSet: IRoleSet, role: CommunityRoleImplicit ): CredentialDefinition { // Use the admin credential to get the resourceID const adminCredential = this.getCredentialDefinitionForRole( - roleManager, + roleSet, CommunityRoleType.ADMIN ); const resourceID = adminCredential.resourceID; @@ -802,7 +780,7 @@ export class CommunityRoleService { } private async removeContributorFromRole( - roleManager: IRoleManager, + roleSet: IRoleSet, roleType: CommunityRoleType, agent: IAgent, contributorType: CommunityContributorType, @@ -810,15 +788,15 @@ export class CommunityRoleService { ): Promise { if (validatePolicyLimits) { await this.validateCommunityPolicyLimits( - roleManager, + roleSet, roleType, CommunityContributorsUpdateType.REMOVE, contributorType ); } - const roleCredential = this.roleManagerService.getCredentialForRole( - roleManager, + const roleCredential = this.roleSetService.getCredentialForRole( + roleSet, roleType ); @@ -829,9 +807,9 @@ export class CommunityRoleService { }); } - async isMember(agent: IAgent, roleManager: IRoleManager): Promise { + async isMember(agent: IAgent, roleSet: IRoleSet): Promise { const membershipCredential = this.getCredentialDefinitionForRole( - roleManager, + roleSet, CommunityRoleType.MEMBER ); @@ -847,11 +825,11 @@ export class CommunityRoleService { async isInRole( agent: IAgent, - roleManager: IRoleManager, + roleSet: IRoleSet, role: CommunityRoleType ): Promise { const membershipCredential = this.getCredentialDefinitionForRole( - roleManager, + roleSet, role ); @@ -867,11 +845,11 @@ export class CommunityRoleService { async isInRoleImplicit( agent: IAgent, - roleManager: IRoleManager, + roleSet: IRoleSet, role: CommunityRoleImplicit ): Promise { const membershipCredential = this.getCredentialForImplicitRole( - roleManager, + roleSet, role ); @@ -891,20 +869,20 @@ export class CommunityRoleService { const { user, agent } = await this.userService.getUserAndAgent( applicationData.userID ); - const roleManager = await this.roleManagerService.getRoleManagerOrFail( - applicationData.roleManagerID, + const roleSet = await this.roleSetService.getRoleSetOrFail( + applicationData.roleSetID, { relations: { - parentRoleManager: true, + parentRoleSet: true, }, } ); - await this.validateApplicationFromUser(user, agent, roleManager); + await this.validateApplicationFromUser(user, agent, roleSet); const application = await this.applicationService.createApplication(applicationData); - application.roleManager = roleManager; + application.roleSet = roleSet; return await this.applicationService.save(application); } @@ -915,21 +893,21 @@ export class CommunityRoleService { await this.contributorService.getContributorAndAgent( invitationData.invitedContributor ); - const roleManager = await this.roleManagerService.getRoleManagerOrFail( - invitationData.roleManagerID + const roleSet = await this.roleSetService.getRoleSetOrFail( + invitationData.roleSetID ); await this.validateInvitationToExistingContributor( contributor, agent, - roleManager + roleSet ); const invitation = await this.invitationService.createInvitation( invitationData, contributor ); - invitation.roleManager = roleManager; + invitation.roleSet = roleSet; return await this.invitationService.save(invitation); } @@ -940,10 +918,10 @@ export class CommunityRoleService { ): Promise { await this.validateInvitationToExternalUser( invitationData.email, - invitationData.roleManagerID + invitationData.roleSetID ); - const roleManager = await this.roleManagerService.getRoleManagerOrFail( - invitationData.roleManagerID, + const roleSet = await this.roleSetService.getRoleSetOrFail( + invitationData.roleSetID, { relations: {}, } @@ -957,42 +935,36 @@ export class CommunityRoleService { await this.platformInvitationService.createPlatformInvitation( externalInvitationInput ); - externalInvitation.roleManager = roleManager; + externalInvitation.roleSet = roleSet; return await this.platformInvitationService.save(externalInvitation); } private async validateApplicationFromUser( user: IUser, agent: IAgent, - roleManager: IRoleManager + roleSet: IRoleSet ) { - const openApplication = await this.findOpenApplication( - user.id, - roleManager.id - ); + const openApplication = await this.findOpenApplication(user.id, roleSet.id); if (openApplication) { throw new CommunityMembershipException( - `Application not possible: An open application (ID: ${openApplication.id}) already exists for contributor ${openApplication.user?.id} on Community: ${roleManager.id}.`, + `Application not possible: An open application (ID: ${openApplication.id}) already exists for contributor ${openApplication.user?.id} on Community: ${roleSet.id}.`, LogContext.COMMUNITY ); } - const openInvitation = await this.findOpenInvitation( - user.id, - roleManager.id - ); + const openInvitation = await this.findOpenInvitation(user.id, roleSet.id); if (openInvitation) { throw new CommunityMembershipException( - `Application not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on Community: ${roleManager.id}.`, + `Application not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on Community: ${roleSet.id}.`, LogContext.COMMUNITY ); } // Check if the user is already a member; if so do not allow an application - const isExistingMember = await this.isMember(agent, roleManager); + const isExistingMember = await this.isMember(agent, roleSet); if (isExistingMember) throw new CommunityMembershipException( - `Application not possible: Contributor ${user.id} is already a member of the Community: ${roleManager.id}.`, + `Application not possible: Contributor ${user.id} is already a member of the Community: ${roleSet.id}.`, LogContext.COMMUNITY ); } @@ -1000,42 +972,42 @@ export class CommunityRoleService { private async validateInvitationToExistingContributor( contributor: IContributor, agent: IAgent, - roleManager: IRoleManager + roleSet: IRoleSet ) { const openInvitation = await this.findOpenInvitation( contributor.id, - roleManager.id + roleSet.id ); if (openInvitation) { throw new CommunityMembershipException( - `Invitation not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on Community: ${roleManager.id}.`, + `Invitation not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on Community: ${roleSet.id}.`, LogContext.COMMUNITY ); } const openApplication = await this.findOpenApplication( contributor.id, - roleManager.id + roleSet.id ); if (openApplication) { throw new CommunityMembershipException( - `Invitation not possible: An open application (ID: ${openApplication.id}) already exists for contributor ${openApplication.user?.id} on Community: ${roleManager.id}.`, + `Invitation not possible: An open application (ID: ${openApplication.id}) already exists for contributor ${openApplication.user?.id} on Community: ${roleSet.id}.`, LogContext.COMMUNITY ); } // Check if the user is already a member; if so do not allow an application - const isExistingMember = await this.isMember(agent, roleManager); + const isExistingMember = await this.isMember(agent, roleSet); if (isExistingMember) throw new CommunityMembershipException( - `Invitation not possible: Contributor ${contributor.id} is already a member of the Community: ${roleManager.id}.`, + `Invitation not possible: Contributor ${contributor.id} is already a member of the Community: ${roleSet.id}.`, LogContext.COMMUNITY ); } private async validateInvitationToExternalUser( email: string, - roleManagerID: string + roleSetID: string ) { // Check if a user with the provided email address already exists or not const isExistingUser = await this.userService.isRegisteredUser(email); @@ -1052,38 +1024,42 @@ export class CommunityRoleService { ); for (const platformInvitation of platformInvitations) { if ( - platformInvitation.roleManager && - platformInvitation.roleManager.id === roleManagerID + platformInvitation.roleSet && + platformInvitation.roleSet.id === roleSetID ) { throw new CommunityMembershipException( - `An invitation with the provided email address (${email}) already exists for the specified community: ${roleManagerID}`, + `An invitation with the provided email address (${email}) already exists for the specified community: ${roleSetID}`, LogContext.COMMUNITY ); } } } - async getApplications(roleManager: IRoleManager): Promise { - const communityApplications = - await this.roleManagerService.getRoleManagerOrFail(roleManager.id, { + async getApplications(roleSet: IRoleSet): Promise { + const communityApplications = await this.roleSetService.getRoleSetOrFail( + roleSet.id, + { relations: { applications: true }, - }); + } + ); return communityApplications?.applications || []; } - async getInvitations(roleManager: IRoleManager): Promise { - const roleManagerInvitations = - await this.roleManagerService.getRoleManagerOrFail(roleManager.id, { + async getInvitations(roleSet: IRoleSet): Promise { + const roleSetInvitations = await this.roleSetService.getRoleSetOrFail( + roleSet.id, + { relations: { invitations: true }, - }); - return roleManagerInvitations?.invitations || []; + } + ); + return roleSetInvitations?.invitations || []; } async getPlatformInvitations( - roleManager: IRoleManager + roleSet: IRoleSet ): Promise { - const communityInvs = await this.roleManagerService.getRoleManagerOrFail( - roleManager.id, + const communityInvs = await this.roleSetService.getRoleSetOrFail( + roleSet.id, { relations: { platformInvitations: true }, } diff --git a/src/domain/community/community-role/dto/community.role.dto.apply.ts b/src/domain/community/community-role/dto/community.role.dto.apply.ts index f247f51b97..8c10ff789f 100644 --- a/src/domain/community/community-role/dto/community.role.dto.apply.ts +++ b/src/domain/community/community-role/dto/community.role.dto.apply.ts @@ -9,7 +9,7 @@ import { UUID_LENGTH } from '@common/constants'; export class CommunityRoleApplyInput { @Field(() => UUID, { nullable: false }) @MaxLength(UUID_LENGTH) - roleManagerID!: string; + roleSetID!: string; @Field(() => [CreateNVPInput], { nullable: false }) @ValidateNested({ each: true }) diff --git a/src/domain/community/community-role/dto/community.role.dto.invite.contributor.ts b/src/domain/community/community-role/dto/community.role.dto.invite.contributor.ts index 37f58c97a7..0a215ce1e8 100644 --- a/src/domain/community/community-role/dto/community.role.dto.invite.contributor.ts +++ b/src/domain/community/community-role/dto/community.role.dto.invite.contributor.ts @@ -7,7 +7,7 @@ import { MID_TEXT_LENGTH, UUID_LENGTH } from '@common/constants'; export class CreateInvitationForContributorsOnCommunityInput { @Field(() => UUID, { nullable: false }) @MaxLength(UUID_LENGTH) - roleManagerID!: string; + roleSetID!: string; @Field(() => [UUID], { nullable: false, diff --git a/src/domain/community/community-role/dto/community.role.dto.join.ts b/src/domain/community/community-role/dto/community.role.dto.join.ts index 46406cfbd2..a5861d6f69 100644 --- a/src/domain/community/community-role/dto/community.role.dto.join.ts +++ b/src/domain/community/community-role/dto/community.role.dto.join.ts @@ -7,5 +7,5 @@ import { UUID_LENGTH } from '@common/constants'; export class CommunityJoinInput { @Field(() => UUID, { nullable: false }) @MaxLength(UUID_LENGTH) - roleManagerID!: string; + roleSetID!: string; } diff --git a/src/domain/community/community-role/dto/community.role.dto.platform.invitation.community.ts b/src/domain/community/community-role/dto/community.role.dto.platform.invitation.community.ts index 056cc48abf..6767ef483b 100644 --- a/src/domain/community/community-role/dto/community.role.dto.platform.invitation.community.ts +++ b/src/domain/community/community-role/dto/community.role.dto.platform.invitation.community.ts @@ -8,5 +8,5 @@ import { CreatePlatformInvitationInput } from '@platform/invitation/dto/platform export class CreatePlatformInvitationOnCommunityInput extends CreatePlatformInvitationInput { @Field(() => UUID, { nullable: false }) @MaxLength(UUID_LENGTH) - roleManagerID!: string; + roleSetID!: string; } diff --git a/src/domain/community/community-role/dto/community.role.dto.role.assign.organization.ts b/src/domain/community/community-role/dto/community.role.dto.role.assign.organization.ts index 25d3921368..fc260f34a8 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.assign.organization.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.assign.organization.ts @@ -5,7 +5,7 @@ import { Field, InputType } from '@nestjs/graphql'; @InputType() export class AssignCommunityRoleToOrganizationInput { @Field(() => UUID, { nullable: false }) - roleManagerID!: string; + roleSetID!: string; @Field(() => UUID_NAMEID, { nullable: false }) organizationID!: string; diff --git a/src/domain/community/community-role/dto/community.role.dto.role.assign.user.ts b/src/domain/community/community-role/dto/community.role.dto.role.assign.user.ts index 0c6e7e105a..7e632cffec 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.assign.user.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.assign.user.ts @@ -5,7 +5,7 @@ import { Field, InputType } from '@nestjs/graphql'; @InputType() export class AssignCommunityRoleToUserInput { @Field(() => UUID, { nullable: false }) - roleManagerID!: string; + roleSetID!: string; @Field(() => UUID_NAMEID_EMAIL, { nullable: false }) userID!: string; diff --git a/src/domain/community/community-role/dto/community.role.dto.role.assign.virtual.ts b/src/domain/community/community-role/dto/community.role.dto.role.assign.virtual.ts index b4e15d4c6c..e9b0214b15 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.assign.virtual.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.assign.virtual.ts @@ -5,7 +5,7 @@ import { Field, InputType } from '@nestjs/graphql'; @InputType() export class AssignCommunityRoleToVirtualInput { @Field(() => UUID, { nullable: false }) - roleManagerID!: string; + roleSetID!: string; @Field(() => UUID_NAMEID, { nullable: false }) virtualContributorID!: string; diff --git a/src/domain/community/community-role/dto/community.role.dto.role.remove.organization.ts b/src/domain/community/community-role/dto/community.role.dto.role.remove.organization.ts index 1417c6e019..b01feca053 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.remove.organization.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.remove.organization.ts @@ -5,7 +5,7 @@ import { Field, InputType } from '@nestjs/graphql'; @InputType() export class RemoveCommunityRoleFromOrganizationInput { @Field(() => UUID, { nullable: false }) - roleManagerID!: string; + roleSetID!: string; @Field(() => UUID_NAMEID, { nullable: false }) organizationID!: string; diff --git a/src/domain/community/community-role/dto/community.role.dto.role.remove.user.ts b/src/domain/community/community-role/dto/community.role.dto.role.remove.user.ts index 1411ee9ffe..01e1c70663 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.remove.user.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.remove.user.ts @@ -5,7 +5,7 @@ import { Field, InputType } from '@nestjs/graphql'; @InputType() export class RemoveCommunityRoleFromUserInput { @Field(() => UUID, { nullable: false }) - roleManagerID!: string; + roleSetID!: string; @Field(() => UUID_NAMEID_EMAIL, { nullable: false }) userID!: string; diff --git a/src/domain/community/community-role/dto/community.role.dto.role.remove.virtual.ts b/src/domain/community/community-role/dto/community.role.dto.role.remove.virtual.ts index c2a60d0428..97917357d5 100644 --- a/src/domain/community/community-role/dto/community.role.dto.role.remove.virtual.ts +++ b/src/domain/community/community-role/dto/community.role.dto.role.remove.virtual.ts @@ -5,7 +5,7 @@ import { Field, InputType } from '@nestjs/graphql'; @InputType() export class RemoveCommunityRoleFromVirtualInput { @Field(() => UUID, { nullable: false }) - roleManagerID!: string; + roleSetID!: string; @Field(() => UUID_NAMEID, { nullable: false }) virtualContributorID!: string; diff --git a/src/domain/community/community/community.entity.ts b/src/domain/community/community/community.entity.ts index b272ed0ce2..313a84abe8 100644 --- a/src/domain/community/community/community.entity.ts +++ b/src/domain/community/community/community.entity.ts @@ -13,7 +13,7 @@ import { AuthorizableEntity } from '@domain/common/entity/authorizable-entity'; import { Communication } from '@domain/communication/communication/communication.entity'; import { UUID_LENGTH } from '@src/common/constants/entity.field.length.constants'; import { CommunityGuidelines } from '../community-guidelines/community.guidelines.entity'; -import { RoleManager } from '@domain/access/role-manager'; +import { RoleSet } from '@domain/access/role-set'; @Entity() export class Community @@ -42,13 +42,13 @@ export class Community }) groups?: UserGroup[]; - @OneToOne(() => RoleManager, { + @OneToOne(() => RoleSet, { eager: false, cascade: true, onDelete: 'SET NULL', }) @JoinColumn() - roleManager!: RoleManager; + roleSet!: RoleSet; // The parent community can have many child communities; the relationship is controlled by the child. @ManyToOne(() => Community, { diff --git a/src/domain/community/community/community.interface.ts b/src/domain/community/community/community.interface.ts index c7dc692b1d..fda92b55bb 100644 --- a/src/domain/community/community/community.interface.ts +++ b/src/domain/community/community/community.interface.ts @@ -4,7 +4,7 @@ import { IGroupable } from '@domain/common/interfaces/groupable.interface'; import { IAuthorizable } from '@domain/common/entity/authorizable-entity'; import { ICommunication } from '@domain/communication/communication'; import { ICommunityGuidelines } from '../community-guidelines/community.guidelines.interface'; -import { IRoleManager } from '@domain/access/role-manager'; +import { IRoleSet } from '@domain/access/role-set'; @ObjectType('Community', { implements: () => [IGroupable], @@ -14,7 +14,7 @@ export abstract class ICommunity extends IAuthorizable { parentCommunity?: ICommunity; - roleManager!: IRoleManager; + roleSet!: IRoleSet; guidelines?: ICommunityGuidelines; diff --git a/src/domain/community/community/community.module.ts b/src/domain/community/community/community.module.ts index 48be6be2ec..2da41a5ac1 100644 --- a/src/domain/community/community/community.module.ts +++ b/src/domain/community/community/community.module.ts @@ -16,7 +16,7 @@ import { CommunityGuidelinesModule } from '../community-guidelines/community.gui import { LicenseEngineModule } from '@core/license-engine/license.engine.module'; import { EntityResolverModule } from '@services/infrastructure/entity-resolver/entity.resolver.module'; import { VirtualContributorModule } from '../virtual-contributor/virtual.contributor.module'; -import { RoleManagerModule } from '@domain/access/role-manager/role.manager.module'; +import { RoleSetModule } from '@domain/access/role-set/role.set.module'; @Module({ imports: [ @@ -25,7 +25,7 @@ import { RoleManagerModule } from '@domain/access/role-manager/role.manager.modu AgentModule, EntityResolverModule, UserGroupModule, - RoleManagerModule, + RoleSetModule, CommunicationModule, CommunityGuidelinesModule, LicenseEngineModule, diff --git a/src/domain/community/community/community.resolver.fields.ts b/src/domain/community/community/community.resolver.fields.ts index 54e13e7c3a..7f41b7e60f 100644 --- a/src/domain/community/community/community.resolver.fields.ts +++ b/src/domain/community/community/community.resolver.fields.ts @@ -9,7 +9,7 @@ import { AuthorizationPrivilege } from '@common/enums'; import { ICommunication } from '@domain/communication/communication/communication.interface'; import { UUID } from '@domain/common/scalars/scalar.uuid'; import { ICommunityGuidelines } from '../community-guidelines/community.guidelines.interface'; -import { IRoleManager } from '@domain/access/role-manager'; +import { IRoleSet } from '@domain/access/role-set'; @Resolver(() => ICommunity) export class CommunityResolverFields { @@ -54,12 +54,12 @@ export class CommunityResolverFields { @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) @UseGuards(GraphqlGuard) - @ResolveField('roleManager', () => IRoleManager, { + @ResolveField('roleSet', () => IRoleSet, { nullable: false, - description: 'The RoleManager for this Community.', + description: 'The RoleSet for this Community.', }) - async policy(@Parent() community: Community): Promise { - return this.communityService.getRoleManager(community); + async policy(@Parent() community: Community): Promise { + return this.communityService.getRoleSet(community); } @UseGuards(GraphqlGuard) diff --git a/src/domain/community/community/community.service.authorization.ts b/src/domain/community/community/community.service.authorization.ts index 1141f5e69c..be53b5e7f6 100644 --- a/src/domain/community/community/community.service.authorization.ts +++ b/src/domain/community/community/community.service.authorization.ts @@ -40,9 +40,9 @@ import { VirtualContributorService } from '../virtual-contributor/virtual.contri import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; import { CommunityMembershipPolicy } from '@common/enums/community.membership.policy'; import { EntityNotInitializedException } from '@common/exceptions/entity.not.initialized.exception'; -import { RoleManagerAuthorizationService } from '@domain/access/role-manager/role.manager.service.authorization'; -import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; -import { IRoleManager } from '@domain/access/role-manager'; +import { RoleSetAuthorizationService } from '@domain/access/role-set/role.set.service.authorization'; +import { RoleSetService } from '@domain/access/role-set/role.set.service'; +import { IRoleSet } from '@domain/access/role-set'; @Injectable() export class CommunityAuthorizationService { @@ -53,8 +53,8 @@ export class CommunityAuthorizationService { private userGroupAuthorizationService: UserGroupAuthorizationService, private communicationAuthorizationService: CommunicationAuthorizationService, private virtualContributorService: VirtualContributorService, - private roleManagerService: RoleManagerService, - private roleManagerAuthorizationService: RoleManagerAuthorizationService, + private roleSetService: RoleSetService, + private roleSetAuthorizationService: RoleSetAuthorizationService, private communityGuidelinesAuthorizationService: CommunityGuidelinesAuthorizationService ) {} @@ -73,7 +73,7 @@ export class CommunityAuthorizationService { communication: { updates: true, }, - roleManager: true, + roleSet: true, groups: true, guidelines: { profile: true, @@ -84,7 +84,7 @@ export class CommunityAuthorizationService { if ( !community.communication || !community.communication.updates || - !community.roleManager || + !community.roleSet || !community.groups ) { throw new RelationshipNotFoundException( @@ -108,7 +108,7 @@ export class CommunityAuthorizationService { community.authorization, parentAuthorization?.anonymousReadAccess, levelZeroSpaceAgent, - community.roleManager, + community.roleSet, spaceSettings ); community.authorization = this.appendVerifiedCredentialRules( @@ -117,14 +117,14 @@ export class CommunityAuthorizationService { if (spaceMembershipAllowed) { community.authorization = this.extendCommunityAuthorizationPolicySpace( community.authorization, - community.roleManager, + community.roleSet, spaceSettings ); } if (isSubspace) { community.authorization = this.extendAuthorizationPolicySubspace( community.authorization, - community.roleManager, + community.roleSet, spaceSettings ); } @@ -151,16 +151,16 @@ export class CommunityAuthorizationService { updatedAuthorizations.push(...groupAuthorizations); } - const roleManagerAuthorizations = - await this.roleManagerAuthorizationService.applyAuthorizationPolicy( - community.roleManager.id, + const roleSetAuthorizations = + await this.roleSetAuthorizationService.applyAuthorizationPolicy( + community.roleSet.id, community.authorization, levelZeroSpaceAgent, spaceSettings, spaceMembershipAllowed, isSubspace ); - updatedAuthorizations.push(...roleManagerAuthorizations); + updatedAuthorizations.push(...roleSetAuthorizations); if (community.guidelines) { const guidelineAuthorizations = @@ -176,12 +176,12 @@ export class CommunityAuthorizationService { private extendCommunityAuthorizationPolicySpace( communityAuthorization: IAuthorizationPolicy | undefined, - roleManager: IRoleManager, + roleSet: IRoleSet, spaceSettings: ISpaceSettings ): IAuthorizationPolicy { if (!communityAuthorization) throw new EntityNotInitializedException( - `Authorization definition not found for: ${JSON.stringify(roleManager)}`, + `Authorization definition not found for: ${JSON.stringify(roleSet)}`, LogContext.SPACES ); @@ -239,7 +239,7 @@ export class CommunityAuthorizationService { authorization: IAuthorizationPolicy | undefined, allowGlobalRegisteredReadAccess: boolean | undefined, levelZeroSpaceAgent: IAgent, - roleManager: IRoleManager, + roleSet: IRoleSet, spaceSettings: ISpaceSettings ): Promise { const newRules: IAuthorizationPolicyRuleCredential[] = []; @@ -256,16 +256,16 @@ export class CommunityAuthorizationService { newRules.push(globalAdminAddMembers); const inviteMembersCriterias: ICredentialDefinition[] = - this.roleManagerService.getCredentialsForRoleWithParents( - roleManager, + this.roleSetService.getCredentialsForRoleWithParents( + roleSet, CommunityRoleType.ADMIN, spaceSettings ); if (spaceSettings.membership.allowSubspaceAdminsToInviteMembers) { // use the member credential to create subspace admin credential const subspaceAdminCredential: ICredentialDefinition = - this.roleManagerService.getCredentialForRole( - roleManager, + this.roleSetService.getCredentialForRole( + roleSet, CommunityRoleType.MEMBER ); subspaceAdminCredential.type = @@ -301,8 +301,8 @@ export class CommunityAuthorizationService { ); if (accessVirtualContributors) { const criterias: ICredentialDefinition[] = - this.roleManagerService.getCredentialsForRoleWithParents( - roleManager, + this.roleSetService.getCredentialsForRoleWithParents( + roleSet, CommunityRoleType.ADMIN, spaceSettings ); @@ -332,7 +332,7 @@ export class CommunityAuthorizationService { private extendAuthorizationPolicySubspace( authorization: IAuthorizationPolicy | undefined, - roleManager: IRoleManager, + roleSet: IRoleSet, spaceSettings: ISpaceSettings ): IAuthorizationPolicy { if (!authorization) @@ -344,8 +344,8 @@ export class CommunityAuthorizationService { const newRules: IAuthorizationPolicyRuleCredential[] = []; const parentCommunityCredential = - this.roleManagerService.getDirectParentCredentialForRole( - roleManager, + this.roleSetService.getDirectParentCredentialForRole( + roleSet, CommunityRoleType.MEMBER ); @@ -377,8 +377,8 @@ export class CommunityAuthorizationService { } const adminCredentials = - this.roleManagerService.getCredentialsForRoleWithParents( - roleManager, + this.roleSetService.getCredentialsForRoleWithParents( + roleSet, CommunityRoleType.ADMIN, spaceSettings ); diff --git a/src/domain/community/community/community.service.ts b/src/domain/community/community/community.service.ts index fe0c0f0f0c..c85984771e 100644 --- a/src/domain/community/community/community.service.ts +++ b/src/domain/community/community/community.service.ts @@ -25,8 +25,8 @@ import { ICommunityGuidelines } from '../community-guidelines/community.guidelin import { IContributor } from '../contributor/contributor.interface'; import { IUser } from '../user/user.interface'; import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type'; -import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; -import { IRoleManager } from '@domain/access/role-manager'; +import { RoleSetService } from '@domain/access/role-set/role.set.service'; +import { IRoleSet } from '@domain/access/role-set'; @Injectable() export class CommunityService { @@ -36,7 +36,7 @@ export class CommunityService { private communicationService: CommunicationService, private communityResolverService: CommunityResolverService, private communityGuidelinesService: CommunityGuidelinesService, - private roleManagerService: RoleManagerService, + private roleSetService: RoleSetService, private storageAggregatorResolverService: StorageAggregatorResolverService, @InjectRepository(Community) private communityRepository: Repository, @@ -51,8 +51,8 @@ export class CommunityService { community.authorization = new AuthorizationPolicy( AuthorizationPolicyType.COMMUNITY ); - community.roleManager = await this.roleManagerService.createRoleManager( - communityData.roleManagerData + community.roleSet = await this.roleSetService.createRoleSet( + communityData.roleSetData ); community.guidelines = @@ -114,7 +114,7 @@ export class CommunityService { // Loads the group into the Community entity if not already present async getUserGroup( - community: IRoleManager, + community: IRoleSet, groupID: string ): Promise { const communityWithGroups = await this.getCommunityOrFail(community.id, { @@ -159,7 +159,7 @@ export class CommunityService { // Note need to load it in with all contained entities so can remove fully const community = await this.getCommunityOrFail(communityID, { relations: { - roleManager: true, + roleSet: true, groups: true, communication: true, guidelines: true, @@ -168,7 +168,7 @@ export class CommunityService { if ( !community.communication || !community.communication.updates || - !community.roleManager || + !community.roleSet || !community.groups || !community.guidelines ) { @@ -192,7 +192,7 @@ export class CommunityService { community.communication.id ); - await this.roleManagerService.removeRoleManager(community.roleManager.id); + await this.roleSetService.removeRoleSet(community.roleSet.id); await this.communityGuidelinesService.deleteCommunityGuidelines( community.guidelines.id @@ -202,7 +202,7 @@ export class CommunityService { return true; } - async save(community: IRoleManager): Promise { + async save(community: IRoleSet): Promise { return await this.communityRepository.save(community); } @@ -232,15 +232,15 @@ export class CommunityService { } community.parentCommunity = parentCommunity; // Also update the communityPolicy - community.roleManager = this.roleManagerService.inheritParentCredentials( - community.roleManager + community.roleSet = this.roleSetService.inheritParentCredentials( + community.roleSet ); return community; } public async getDisplayName(community: ICommunity): Promise { - return await this.communityResolverService.getDisplayNameForRoleManagerOrFail( + return await this.communityResolverService.getDisplayNameForRoleSetOrFail( community.id ); } @@ -281,21 +281,18 @@ export class CommunityService { ); } - public async getRoleManager(community: ICommunity): Promise { - const communityWithRoleManager = await this.getCommunityOrFail( - community.id, - { - relations: { roleManager: true }, - } - ); + public async getRoleSet(community: ICommunity): Promise { + const communityWithRoleSet = await this.getCommunityOrFail(community.id, { + relations: { roleSet: true }, + }); - if (!communityWithRoleManager.roleManager) { + if (!communityWithRoleSet.roleSet) { throw new EntityNotInitializedException( `Unable to locate Role Manager for community: ${community.id}`, LogContext.COMMUNITY ); } - return communityWithRoleManager.roleManager; + return communityWithRoleSet.roleSet; } public async getCommunityGuidelines( @@ -346,7 +343,7 @@ export class CommunityService { } public async getLevelZeroSpaceIdForCommunity( - community: IRoleManager + community: IRoleSet ): Promise { return await this.communityResolverService.getLevelZeroSpaceIdForCommunity( community.id diff --git a/src/domain/community/community/dto/community.dto.create.ts b/src/domain/community/community/dto/community.dto.create.ts index 154a17eb6a..4a4538abcf 100644 --- a/src/domain/community/community/dto/community.dto.create.ts +++ b/src/domain/community/community/dto/community.dto.create.ts @@ -1,4 +1,4 @@ -import { CreateRoleManagerInput } from '@domain/access/role-manager/dto/role.manager.dto.create'; +import { CreateRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.create'; import { CreateCommunityGuidelinesInput } from '@domain/community/community-guidelines/dto/community.guidelines.dto.create'; export class CreateCommunityInput { @@ -6,5 +6,5 @@ export class CreateCommunityInput { name!: string; - roleManagerData!: CreateRoleManagerInput; + roleSetData!: CreateRoleSetInput; } diff --git a/src/domain/community/invitation/dto/invitation.dto.create.ts b/src/domain/community/invitation/dto/invitation.dto.create.ts index ae33b23993..cde00875f8 100644 --- a/src/domain/community/invitation/dto/invitation.dto.create.ts +++ b/src/domain/community/invitation/dto/invitation.dto.create.ts @@ -20,6 +20,6 @@ export class CreateInvitationInput { createdBy!: string; - roleManagerID!: string; + roleSetID!: string; invitedToParent!: boolean; } diff --git a/src/domain/community/invitation/invitation.entity.ts b/src/domain/community/invitation/invitation.entity.ts index 266aad9e5e..5983600a6b 100644 --- a/src/domain/community/invitation/invitation.entity.ts +++ b/src/domain/community/invitation/invitation.entity.ts @@ -4,7 +4,7 @@ import { IInvitation } from './invitation.interface'; import { AuthorizableEntity } from '@domain/common/entity/authorizable-entity'; import { CommunityContributorType } from '@common/enums/community.contributor.type'; import { ENUM_LENGTH, MID_TEXT_LENGTH, UUID_LENGTH } from '@common/constants'; -import { RoleManager } from '@domain/access/role-manager/role.manager.entity'; +import { RoleSet } from '@domain/access/role-set/role.set.entity'; @Entity() export class Invitation extends AuthorizableEntity implements IInvitation { // todo ID in migration is varchar - must be char(36) @@ -31,10 +31,10 @@ export class Invitation extends AuthorizableEntity implements IInvitation { @Column('varchar', { length: ENUM_LENGTH, nullable: false }) contributorType!: CommunityContributorType; - @ManyToOne(() => RoleManager, manager => manager.invitations, { + @ManyToOne(() => RoleSet, manager => manager.invitations, { eager: false, cascade: false, onDelete: 'CASCADE', }) - roleManager?: RoleManager; + roleSet?: RoleSet; } diff --git a/src/domain/community/invitation/invitation.interface.ts b/src/domain/community/invitation/invitation.interface.ts index 1fd82c3799..b550ad6473 100644 --- a/src/domain/community/invitation/invitation.interface.ts +++ b/src/domain/community/invitation/invitation.interface.ts @@ -2,14 +2,14 @@ import { ILifecycle } from '@domain/common/lifecycle/lifecycle.interface'; import { Field, ObjectType } from '@nestjs/graphql'; import { IAuthorizable } from '@domain/common/entity/authorizable-entity'; import { CommunityContributorType } from '@common/enums/community.contributor.type'; -import { IRoleManager } from '@domain/access/role-manager'; +import { IRoleSet } from '@domain/access/role-set'; @ObjectType('Invitation') export class IInvitation extends IAuthorizable { invitedContributor!: string; createdBy!: string; - roleManager?: IRoleManager; + roleSet?: IRoleSet; @Field(() => ILifecycle, { nullable: false }) lifecycle!: ILifecycle; diff --git a/src/domain/community/invitation/invitation.service.ts b/src/domain/community/invitation/invitation.service.ts index 479e1bd001..58d6e0f3ee 100644 --- a/src/domain/community/invitation/invitation.service.ts +++ b/src/domain/community/invitation/invitation.service.ts @@ -133,14 +133,14 @@ export class InvitationService { async findExistingInvitations( contributorID: string, - roleManagerID: string + roleSetID: string ): Promise { const existingInvitations = await this.invitationRepository.find({ where: { invitedContributor: contributorID, - roleManager: { id: roleManagerID }, + roleSet: { id: roleSetID }, }, - relations: { roleManager: true }, + relations: { roleSet: true }, }); if (existingInvitations.length > 0) return existingInvitations; @@ -152,7 +152,7 @@ export class InvitationService { states: string[] = [] ): Promise { const findOpts: FindManyOptions = { - relations: { roleManager: true }, + relations: { roleSet: true }, where: { invitedContributor: contributorID }, }; diff --git a/src/domain/space/space.defaults/definitions/space.role.manager.application.form.ts b/src/domain/space/space.defaults/definitions/space.community.role.application.form.ts similarity index 100% rename from src/domain/space/space.defaults/definitions/space.role.manager.application.form.ts rename to src/domain/space/space.defaults/definitions/space.community.role.application.form.ts diff --git a/src/domain/space/space.defaults/definitions/space.role.manager.roles.ts b/src/domain/space/space.defaults/definitions/space.community.roles.ts similarity index 100% rename from src/domain/space/space.defaults/definitions/space.role.manager.roles.ts rename to src/domain/space/space.defaults/definitions/space.community.roles.ts diff --git a/src/domain/space/space.defaults/definitions/subspace.role.manager.application.form.ts b/src/domain/space/space.defaults/definitions/subspace.community.role.application.form.ts similarity index 100% rename from src/domain/space/space.defaults/definitions/subspace.role.manager.application.form.ts rename to src/domain/space/space.defaults/definitions/subspace.community.role.application.form.ts diff --git a/src/domain/space/space.defaults/definitions/subspace.role.manager.roles.ts b/src/domain/space/space.defaults/definitions/subspace.community.roles.ts similarity index 100% rename from src/domain/space/space.defaults/definitions/subspace.role.manager.roles.ts rename to src/domain/space/space.defaults/definitions/subspace.community.roles.ts diff --git a/src/domain/space/space.defaults/space.defaults.service.ts b/src/domain/space/space.defaults/space.defaults.service.ts index 6dc1b6d518..b798c4b45a 100644 --- a/src/domain/space/space.defaults/space.defaults.service.ts +++ b/src/domain/space/space.defaults/space.defaults.service.ts @@ -11,11 +11,11 @@ import { SpaceDefaults } from './space.defaults.entity'; import { CreateCalloutInput } from '@domain/collaboration/callout/dto/callout.dto.create'; import { ISpaceSettings } from '../space.settings/space.settings.interface'; import { ICalloutGroup } from '@domain/collaboration/callout-groups/callout.group.interface'; -import { subspaceCommunityRoles } from './definitions/subspace.role.manager.roles'; -import { spaceCommunityRoles } from './definitions/space.role.manager.roles'; +import { subspaceCommunityRoles } from './definitions/subspace.community.roles'; +import { spaceCommunityRoles } from './definitions/space.community.roles'; import { CreateFormInput } from '@domain/common/form/dto/form.dto.create'; -import { subspceCommunityApplicationForm } from './definitions/subspace.role.manager.application.form'; -import { spaceCommunityApplicationForm } from './definitions/space.role.manager.application.form'; +import { subspceCommunityApplicationForm } from './definitions/subspace.community.role.application.form'; +import { spaceCommunityApplicationForm } from './definitions/space.community.role.application.form'; import { ProfileType } from '@common/enums'; import { CalloutGroupName } from '@common/enums/callout.group.name'; import { SpaceLevel } from '@common/enums/space.level'; @@ -153,9 +153,7 @@ export class SpaceDefaultsService { } } - public getRoleManagerCommunityRoles( - spaceLevel: SpaceLevel - ): CreateRoleInput[] { + public getRoleSetCommunityRoles(spaceLevel: SpaceLevel): CreateRoleInput[] { switch (spaceLevel) { case SpaceLevel.CHALLENGE: case SpaceLevel.OPPORTUNITY: @@ -181,7 +179,7 @@ export class SpaceDefaultsService { } } - public getRoleManagerCommunityApplicationForm( + public getRoleSetCommunityApplicationForm( spaceLevel: SpaceLevel ): CreateFormInput { switch (spaceLevel) { diff --git a/src/domain/space/space/index.ts b/src/domain/space/space/index.ts index 0bfb5799ff..8ba66cf5c0 100644 --- a/src/domain/space/space/index.ts +++ b/src/domain/space/space/index.ts @@ -1,5 +1,5 @@ export * from './dto/space.dto.update'; export * from './dto/space.dto.delete'; export * from './dto/space.dto.create'; -export * from '../space.defaults/definitions/space.role.manager.roles'; -export * from '../space.defaults/definitions/space.role.manager.application.form'; +export * from '../space.defaults/definitions/space.community.roles'; +export * from '../space.defaults/definitions/space.community.role.application.form'; diff --git a/src/domain/space/space/space.module.ts b/src/domain/space/space/space.module.ts index b79b4a33c0..1d5e2c5a90 100644 --- a/src/domain/space/space/space.module.ts +++ b/src/domain/space/space/space.module.ts @@ -32,7 +32,7 @@ import { LicenseEngineModule } from '@core/license-engine/license.engine.module' import { LicenseIssuerModule } from '@platform/license-issuer/license.issuer.module'; import { TemplateModule } from '@domain/template/template/template.module'; import { InputCreatorModule } from '@services/api/input-creator/input.creator.module'; -import { RoleManagerModule } from '@domain/access/role-manager/role.manager.module'; +import { RoleSetModule } from '@domain/access/role-set/role.set.module'; @Module({ imports: [ @@ -60,7 +60,7 @@ import { RoleManagerModule } from '@domain/access/role-manager/role.manager.modu ActivityAdapterModule, LoaderCreatorModule, TemplateModule, - RoleManagerModule, + RoleSetModule, InputCreatorModule, NameReporterModule, TypeOrmModule.forFeature([Space]), diff --git a/src/domain/space/space/space.service.authorization.ts b/src/domain/space/space/space.service.authorization.ts index 6f30c6cb34..4517b54369 100644 --- a/src/domain/space/space/space.service.authorization.ts +++ b/src/domain/space/space/space.service.authorization.ts @@ -36,15 +36,15 @@ import { AgentAuthorizationService } from '@domain/agent/agent/agent.service.aut import { IAgent } from '@domain/agent/agent/agent.interface'; import { ISpaceSettings } from '../space.settings/space.settings.interface'; import { TemplatesSetAuthorizationService } from '@domain/template/templates-set/templates.set.service.authorization'; -import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; -import { IRoleManager } from '@domain/access/role-manager'; +import { RoleSetService } from '@domain/access/role-set/role.set.service'; +import { IRoleSet } from '@domain/access/role-set'; @Injectable() export class SpaceAuthorizationService { constructor( private authorizationPolicyService: AuthorizationPolicyService, private agentAuthorizationService: AgentAuthorizationService, - private roleManagerService: RoleManagerService, + private roleSetService: RoleSetService, private storageAggregatorAuthorizationService: StorageAggregatorAuthorizationService, private profileAuthorizationService: ProfileAuthorizationService, private contextAuthorizationService: ContextAuthorizationService, @@ -63,13 +63,13 @@ export class SpaceAuthorizationService { parentSpace: { authorization: true, community: { - roleManager: true, + roleSet: true, }, }, agent: true, authorization: true, community: { - roleManager: true, + roleSet: true, }, collaboration: true, context: true, @@ -83,7 +83,7 @@ export class SpaceAuthorizationService { if ( !space.authorization || !space.community || - !space.community.roleManager || + !space.community.roleSet || !space.subspaces ) { throw new RelationshipNotFoundException( @@ -104,9 +104,9 @@ export class SpaceAuthorizationService { let parentSpaceAdminCredentialCriterias: ICredentialDefinition[] = []; if (space.parentSpace) { const parentSpaceCommunity = space.parentSpace.community; - if (!parentSpaceCommunity || !parentSpaceCommunity.roleManager) { + if (!parentSpaceCommunity || !parentSpaceCommunity.roleSet) { throw new RelationshipNotFoundException( - `Unable to load Space with parent RoleManager in auth reset: ${space.id} `, + `Unable to load Space with parent RoleSet in auth reset: ${space.id} `, LogContext.SPACES ); } @@ -115,8 +115,8 @@ export class SpaceAuthorizationService { spaceInput.settingsStr ); parentSpaceAdminCredentialCriterias = - this.roleManagerService.getCredentialsForRole( - parentSpaceCommunity.roleManager, + this.roleSetService.getCredentialsForRole( + parentSpaceCommunity.roleSet, CommunityRoleType.ADMIN, spaceSettings ); @@ -164,7 +164,7 @@ export class SpaceAuthorizationService { case SpaceVisibility.DEMO: space.authorization = this.extendAuthorizationPolicyLocal( space.authorization, - space.community.roleManager, + space.community.roleSet, spaceSettings, parentSpaceAdminCredentialCriterias ); @@ -175,7 +175,7 @@ export class SpaceAuthorizationService { if (space.level !== SpaceLevel.SPACE) { space.authorization = this.extendPrivateSubspaceAdmins( space.authorization, - space.community.roleManager, + space.community.roleSet, spaceSettings ); } @@ -205,7 +205,7 @@ export class SpaceAuthorizationService { const childAuthorzations = await this.propagateAuthorizationToChildEntities( space, levelZeroSpaceAgent, - space.community.roleManager, + space.community.roleSet, spaceSettings, spaceMembershipAllowed ); @@ -239,7 +239,7 @@ export class SpaceAuthorizationService { public async propagateAuthorizationToChildEntities( space: ISpace, levelZeroSpaceAgent: IAgent, - roleManager: IRoleManager, + roleSet: IRoleSet, spaceSettings: ISpaceSettings, spaceMembershipAllowed: boolean ): Promise { @@ -248,7 +248,7 @@ export class SpaceAuthorizationService { !space.agent || !space.collaboration || !space.community || - !space.community.roleManager || + !space.community.roleSet || !space.context || !space.profile || !space.storageAggregator @@ -278,7 +278,7 @@ export class SpaceAuthorizationService { await this.collaborationAuthorizationService.applyAuthorizationPolicy( space.collaboration, space.authorization, - space.community.roleManager, + space.community.roleSet, spaceSettings, levelZeroSpaceAgent ); @@ -367,7 +367,7 @@ export class SpaceAuthorizationService { private extendAuthorizationPolicyLocal( authorization: IAuthorizationPolicy, - roleManager: IRoleManager, + roleSet: IRoleSet, spaceSettings: ISpaceSettings, deletionCredentialCriterias: ICredentialDefinition[] ): IAuthorizationPolicy { @@ -386,8 +386,8 @@ export class SpaceAuthorizationService { newRules.push(deleteSubspaces); } - const memberCriteras = this.roleManagerService.getCredentialsForRole( - roleManager, + const memberCriteras = this.roleSetService.getCredentialsForRole( + roleSet, CommunityRoleType.MEMBER, spaceSettings ); @@ -398,8 +398,8 @@ export class SpaceAuthorizationService { ); newRules.push(spaceMember); - const spaceAdminCriterias = this.roleManagerService.getCredentialsForRole( - roleManager, + const spaceAdminCriterias = this.roleSetService.getCredentialsForRole( + roleSet, CommunityRoleType.ADMIN, spaceSettings ); @@ -418,7 +418,7 @@ export class SpaceAuthorizationService { const collaborationSettings = spaceSettings.collaboration; if (collaborationSettings.allowMembersToCreateSubspaces) { - const criteria = this.getContributorCriteria(roleManager, spaceSettings); + const criteria = this.getContributorCriteria(roleSet, spaceSettings); const createSubspacePrilegeRule = this.authorizationPolicyService.createCredentialRule( [AuthorizationPrivilege.CREATE_SUBSPACE], @@ -438,11 +438,11 @@ export class SpaceAuthorizationService { } private getContributorCriteria( - roleManager: IRoleManager, + roleSet: IRoleSet, spaceSettings: ISpaceSettings ): ICredentialDefinition[] { - const memberCriteria = this.roleManagerService.getCredentialsForRole( - roleManager, + const memberCriteria = this.roleSetService.getCredentialsForRole( + roleSet, CommunityRoleType.MEMBER, spaceSettings ); @@ -452,8 +452,8 @@ export class SpaceAuthorizationService { spaceSettings.privacy.mode === SpacePrivacyMode.PUBLIC ) { const parentCredential = - this.roleManagerService.getDirectParentCredentialForRole( - roleManager, + this.roleSetService.getDirectParentCredentialForRole( + roleSet, CommunityRoleType.MEMBER ); if (parentCredential) memberCriteria.push(parentCredential); @@ -463,19 +463,19 @@ export class SpaceAuthorizationService { private extendPrivateSubspaceAdmins( authorization: IAuthorizationPolicy | undefined, - roleManager: IRoleManager, + roleSet: IRoleSet, spaceSettings: ISpaceSettings ): IAuthorizationPolicy { if (!authorization) throw new EntityNotInitializedException( - `Authorization definition not found for: ${JSON.stringify(roleManager)}`, + `Authorization definition not found for: ${JSON.stringify(roleSet)}`, LogContext.SPACES ); const rules: IAuthorizationPolicyRuleCredential[] = []; const spaceAdminCriteria = [ - ...this.roleManagerService.getCredentialsForRoleWithParents( - roleManager, + ...this.roleSetService.getCredentialsForRoleWithParents( + roleSet, CommunityRoleType.ADMIN, spaceSettings ), diff --git a/src/domain/space/space/space.service.ts b/src/domain/space/space/space.service.ts index 3723a85201..e8e08c81af 100644 --- a/src/domain/space/space/space.service.ts +++ b/src/domain/space/space/space.service.ts @@ -79,7 +79,7 @@ import { CreateInnovationFlowInput } from '@domain/collaboration/innovation-flow import { TemplateService } from '@domain/template/template/template.service'; import { templatesSetDefaults } from '../space.defaults/definitions/space.defaults.templates'; import { InputCreatorService } from '@services/api/input-creator/input.creator.service'; -import { RoleManagerService } from '@domain/access/role-manager/role.manager.service'; +import { RoleSetService } from '@domain/access/role-set/role.set.service'; @Injectable() export class SpaceService { @@ -101,7 +101,7 @@ export class SpaceService { private licensingService: LicensingService, private licenseEngineService: LicenseEngineService, private templateService: TemplateService, - private roleManagerService: RoleManagerService, + private roleSetService: RoleSetService, private inputCreatorService: InputCreatorService, @InjectRepository(Space) private spaceRepository: Repository, @@ -159,17 +159,16 @@ export class SpaceService { ); space.storageAggregator = storageAggregator; - const roleManagerRolesData = - this.spaceDefaultsService.getRoleManagerCommunityRoles(space.level); + const roleSetRolesData = this.spaceDefaultsService.getRoleSetCommunityRoles( + space.level + ); const applicationFormData = - this.spaceDefaultsService.getRoleManagerCommunityApplicationForm( - space.level - ); + this.spaceDefaultsService.getRoleSetCommunityApplicationForm(space.level); const communityData: CreateCommunityInput = { name: spaceData.profileData.displayName, - roleManagerData: { - roles: roleManagerRolesData, + roleSetData: { + roles: roleSetRolesData, applicationForm: applicationFormData, }, guidelines: { @@ -257,15 +256,15 @@ export class SpaceService { ////// Community // set immediate community parent + resourceID - if (!space.community || !space.community.roleManager) { + if (!space.community || !space.community.roleSet) { throw new RelationshipNotFoundException( `Unable to load community with role manager: ${space.id}`, LogContext.SPACES ); } space.community.parentID = space.id; - space.community.roleManager = this.roleManagerService.updateRoleResourceID( - space.community.roleManager, + space.community.roleSet = this.roleSetService.updateRoleResourceID( + space.community.roleSet, space.id ); diff --git a/src/platform/invitation/dto/platform.invitation.dto.create.ts b/src/platform/invitation/dto/platform.invitation.dto.create.ts index 0c35311bf0..068454ea82 100644 --- a/src/platform/invitation/dto/platform.invitation.dto.create.ts +++ b/src/platform/invitation/dto/platform.invitation.dto.create.ts @@ -29,7 +29,7 @@ export class CreatePlatformInvitationInput { createdBy!: string; - roleManagerID?: string; + roleSetID?: string; communityInvitedToParent!: boolean; platformRole?: PlatformRole; } diff --git a/src/platform/invitation/platform.invitation.entity.ts b/src/platform/invitation/platform.invitation.entity.ts index bf29b240ce..f7887d84d9 100644 --- a/src/platform/invitation/platform.invitation.entity.ts +++ b/src/platform/invitation/platform.invitation.entity.ts @@ -9,18 +9,18 @@ import { SMALL_TEXT_LENGTH, UUID_LENGTH, } from '@common/constants'; -import { RoleManager } from '@domain/access/role-manager/role.manager.entity'; +import { RoleSet } from '@domain/access/role-set/role.set.entity'; @Entity() export class PlatformInvitation extends AuthorizableEntity implements IPlatformInvitation { - @ManyToOne(() => RoleManager, manager => manager.platformInvitations, { + @ManyToOne(() => RoleSet, roleSet => roleSet.platformInvitations, { eager: false, cascade: false, onDelete: 'CASCADE', }) - roleManager?: RoleManager; + roleSet?: RoleSet; @Column('boolean', { default: false }) communityInvitedToParent!: boolean; diff --git a/src/platform/invitation/platform.invitation.interface.ts b/src/platform/invitation/platform.invitation.interface.ts index 3bf2f28059..dabcb7e2d5 100644 --- a/src/platform/invitation/platform.invitation.interface.ts +++ b/src/platform/invitation/platform.invitation.interface.ts @@ -2,7 +2,7 @@ import { Field, ObjectType } from '@nestjs/graphql'; import { IAuthorizable } from '@domain/common/entity/authorizable-entity'; import { PlatformRole } from '@common/enums/platform.role'; import { IPlatform } from '@platform/platfrom/platform.interface'; -import { IRoleManager } from '@domain/access/role-manager'; +import { IRoleSet } from '@domain/access/role-set'; @ObjectType('PlatformInvitation') export class IPlatformInvitation extends IAuthorizable { @@ -31,7 +31,7 @@ export class IPlatformInvitation extends IAuthorizable { createdBy!: string; - roleManager?: IRoleManager; + roleSet?: IRoleSet; platform?: IPlatform; diff --git a/src/platform/invitation/platform.invitation.service.ts b/src/platform/invitation/platform.invitation.service.ts index e7ec1f1130..4b18516ecc 100644 --- a/src/platform/invitation/platform.invitation.service.ts +++ b/src/platform/invitation/platform.invitation.service.ts @@ -119,7 +119,7 @@ export class PlatformInvitationService { const existingPlatformInvitations = await this.platformInvitationRepository.find({ where: { email: email }, - relations: { roleManager: true }, + relations: { roleSet: true }, }); if (existingPlatformInvitations.length > 0) diff --git a/src/services/adapters/notification-adapter/notification.payload.builder.ts b/src/services/adapters/notification-adapter/notification.payload.builder.ts index 0aad39119c..e1db5c2f1d 100644 --- a/src/services/adapters/notification-adapter/notification.payload.builder.ts +++ b/src/services/adapters/notification-adapter/notification.payload.builder.ts @@ -717,10 +717,9 @@ export class NotificationPayloadBuilder { triggeredBy: string ): Promise { const basePayload = this.buildBaseEventPayload(triggeredBy); - const space = - await this.communityResolverService.getSpaceForRoleManagerOrFail( - community.id - ); + const space = await this.communityResolverService.getSpaceForRoleSetOrFail( + community.id + ); const url = await this.urlGeneratorService.generateUrlForProfile( space.profile ); diff --git a/src/services/api/conversion/conversion.service.ts b/src/services/api/conversion/conversion.service.ts index 332fe5a64c..d8aa447852 100644 --- a/src/services/api/conversion/conversion.service.ts +++ b/src/services/api/conversion/conversion.service.ts @@ -25,7 +25,7 @@ import { SpaceLevel } from '@common/enums/space.level'; import { CommunityRoleService } from '@domain/community/community-role/community.role.service'; import { CommunityService } from '@domain/community/community/community.service'; import { CreateSpaceOnAccountInput } from '@domain/space/account/dto/account.dto.create.space'; -import { IRoleManager } from '@domain/access/role-manager'; +import { IRoleSet } from '@domain/access/role-set'; export class ConversionService { constructor( @@ -128,24 +128,24 @@ export class ConversionService { LogContext.CONVERSION ); } - const spaceRoleManager = space.community.roleManager; + const spaceRoleSet = space.community.roleSet; const userMembers = await this.communityRoleService.getUsersWithRole( - spaceRoleManager, + spaceRoleSet, CommunityRoleType.MEMBER ); const userLeads = await this.communityRoleService.getUsersWithRole( - spaceRoleManager, + spaceRoleSet, CommunityRoleType.LEAD ); const orgMembers = await this.communityRoleService.getOrganizationsWithRole( - spaceRoleManager, + spaceRoleSet, CommunityRoleType.MEMBER ); // Remove the contributors from old roles await this.removeContributors( - spaceRoleManager, + spaceRoleSet, userMembers, userLeads, orgMembers, @@ -153,7 +153,7 @@ export class ConversionService { ); await this.communityRoleService.removeUserFromRole( - spaceRoleManager, + spaceRoleSet, CommunityRoleType.MEMBER, agentInfo.userID ); @@ -307,27 +307,27 @@ export class ConversionService { ); } - const roleManager = subsubspace.community.roleManager; + const roleSet = subsubspace.community.roleSet; const userMembers = await this.communityRoleService.getUsersWithRole( - roleManager, + roleSet, CommunityRoleType.MEMBER ); const userLeads = await this.communityRoleService.getUsersWithRole( - roleManager, + roleSet, CommunityRoleType.LEAD ); const orgMembers = await this.communityRoleService.getOrganizationsWithRole( - roleManager, + roleSet, CommunityRoleType.MEMBER ); const orgLeads = await this.communityRoleService.getOrganizationsWithRole( - roleManager, + roleSet, CommunityRoleType.LEAD ); // Remove the contributors from old roles await this.removeContributors( - subsubspace.community.roleManager, + subsubspace.community.roleSet, userMembers, userLeads, orgMembers, @@ -336,12 +336,12 @@ export class ConversionService { // also remove the current user from the members of the newly created Challenge, otherwise will end up re-assigning await this.communityRoleService.removeUserFromRole( - subspace.community.roleManager, + subspace.community.roleSet, CommunityRoleType.MEMBER, agentInfo.userID ); await this.communityRoleService.removeUserFromRole( - subspace.community.roleManager, + subspace.community.roleSet, CommunityRoleType.LEAD, agentInfo.userID ); @@ -521,7 +521,7 @@ export class ConversionService { } private async removeContributors( - roleManager: IRoleManager, + roleSet: IRoleSet, userMembers: IUser[], userLeads: IUser[], orgMembers: IOrganization[], @@ -529,28 +529,28 @@ export class ConversionService { ) { for (const userMember of userMembers) { await this.communityRoleService.removeUserFromRole( - roleManager, + roleSet, CommunityRoleType.MEMBER, userMember.id ); } for (const userLead of userLeads) { await this.communityRoleService.removeUserFromRole( - roleManager, + roleSet, CommunityRoleType.LEAD, userLead.id ); } for (const orgMember of orgMembers) { await this.communityRoleService.removeOrganizationFromRole( - roleManager, + roleSet, CommunityRoleType.MEMBER, orgMember.id ); } for (const orgLead of orgLeads) { await this.communityRoleService.removeOrganizationFromRole( - roleManager, + roleSet, CommunityRoleType.LEAD, orgLead.id ); @@ -558,7 +558,7 @@ export class ConversionService { } private async assignContributors( - roleManager: IRoleManager, + roleSet: IRoleSet, userMembers: IUser[], userLeads: IUser[], orgMembers: IOrganization[], @@ -566,21 +566,21 @@ export class ConversionService { ) { for (const userMember of userMembers) { await this.communityRoleService.assignUserToRole( - roleManager, + roleSet, CommunityRoleType.MEMBER, userMember.id ); } for (const userLead of userLeads) { await this.communityRoleService.assignUserToRole( - roleManager, + roleSet, CommunityRoleType.LEAD, userLead.id ); } for (const orgMember of orgMembers) { await this.communityRoleService.assignOrganizationToRole( - roleManager, + roleSet, CommunityRoleType.MEMBER, orgMember.id ); @@ -588,7 +588,7 @@ export class ConversionService { if (orgLeads) { for (const orgLead of orgLeads) { await this.communityRoleService.assignOrganizationToRole( - roleManager, + roleSet, CommunityRoleType.LEAD, orgLead.id ); diff --git a/src/services/api/me/me.service.ts b/src/services/api/me/me.service.ts index 0c1c197564..4a55c85ba1 100644 --- a/src/services/api/me/me.service.ts +++ b/src/services/api/me/me.service.ts @@ -40,15 +40,15 @@ export class MeService { ); const results: CommunityInvitationResult[] = []; for (const invitation of invitations) { - if (!invitation.roleManager) { + if (!invitation.roleSet) { throw new EntityNotFoundException( `Community not found for invitation ${invitation.id}`, LogContext.COMMUNITY ); } const space = - await this.communityResolverService.getSpaceForRoleManagerOrFail( - invitation.roleManager.id + await this.communityResolverService.getSpaceForRoleSetOrFail( + invitation.roleSet.id ); results.push({ id: `${invitation.id}`, @@ -67,15 +67,15 @@ export class MeService { await this.rolesService.getCommunityApplicationsForUser(userId, states); const results: CommunityApplicationResult[] = []; for (const application of applications) { - if (!application.roleManager) { + if (!application.roleSet) { throw new EntityNotFoundException( `Community not found for application ${application.id}`, LogContext.COMMUNITY ); } const space = - await this.communityResolverService.getSpaceForRoleManagerOrFail( - application.roleManager.id + await this.communityResolverService.getSpaceForRoleSetOrFail( + application.roleSet.id ); results.push({ id: `${application.id}`, diff --git a/src/services/api/registration/registration.service.ts b/src/services/api/registration/registration.service.ts index ce38ba082e..33014bae7d 100644 --- a/src/services/api/registration/registration.service.ts +++ b/src/services/api/registration/registration.service.ts @@ -119,13 +119,13 @@ export class RegistrationService { const communityInvitations: IInvitation[] = []; for (const platformInvitation of platformInvitations) { - const roleManager = platformInvitation.roleManager; + const roleSet = platformInvitation.roleSet; // Process community invitations - if (roleManager) { + if (roleSet) { const invitationInput: CreateInvitationInput = { invitedContributor: user.id, - roleManagerID: roleManager.id, + roleSetID: roleSet.id, createdBy: platformInvitation.createdBy, invitedToParent: platformInvitation.communityInvitedToParent, }; @@ -140,7 +140,7 @@ export class RegistrationService { const authorization = await this.invitationAuthorizationService.applyAuthorizationPolicy( invitation, - roleManager.authorization + roleSet.authorization ); await this.authorizationPolicyService.save(authorization); diff --git a/src/services/api/roles/roles.service.ts b/src/services/api/roles/roles.service.ts index 8bcf3f8300..245b8c2d25 100644 --- a/src/services/api/roles/roles.service.ts +++ b/src/services/api/roles/roles.service.ts @@ -23,7 +23,7 @@ import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { AuthorizationService } from '@core/authorization/authorization.service'; import { ContributorLookupService } from '@services/infrastructure/contributor-lookup/contributor.lookup.service'; import { RolesVirtualContributorInput } from './dto/roles.dto.input.virtual.contributor'; -import { IRoleManager } from '@domain/access/role-manager'; +import { IRoleSet } from '@domain/access/role-set'; import { VirtualContributorService } from '@domain/community/virtual-contributor/virtual.contributor.service'; export class RolesService { @@ -126,14 +126,14 @@ export class RolesService { ): Promise { const applicationResults: CommunityApplicationForRoleResult[] = []; for (const application of applications) { - const roleManager = application.roleManager; + const roleSet = application.roleSet; const state = await this.applicationService.getApplicationState( application.id ); - if (roleManager) { + if (roleSet) { const applicationResult = - await this.buildApplicationResultForRoleManagerApplication( - roleManager, + await this.buildApplicationResultForRoleSetApplication( + roleSet, state, application ); @@ -144,24 +144,23 @@ export class RolesService { return applicationResults; } - private async buildApplicationResultForRoleManagerApplication( - roleManager: IRoleManager, + private async buildApplicationResultForRoleSetApplication( + roleSet: IRoleSet, state: string, application: IApplication ): Promise { - const roleManagerDisplayName = - await this.communityResolverService.getDisplayNameForRoleManagerOrFail( - roleManager.id + const roleSetDisplayName = + await this.communityResolverService.getDisplayNameForRoleSetOrFail( + roleSet.id ); - const space = - await this.communityResolverService.getSpaceForRoleManagerOrFail( - roleManager.id - ); + const space = await this.communityResolverService.getSpaceForRoleSetOrFail( + roleSet.id + ); const applicationResult = new CommunityApplicationForRoleResult( - roleManager.id, - roleManagerDisplayName, + roleSet.id, + roleSetDisplayName, state, application.id, space.id, @@ -200,14 +199,14 @@ export class RolesService { ): Promise { const invitationResults: CommunityInvitationForRoleResult[] = []; for (const invitation of invitations) { - const roleManager = invitation.roleManager; + const roleSet = invitation.roleSet; const state = await this.invitationService.getInvitationState( invitation.id ); - if (roleManager) { + if (roleSet) { const invitationResult = - await this.buildInvitationResultForRoleManagerInvitation( - roleManager, + await this.buildInvitationResultForRoleSetInvitation( + roleSet, state, invitation ); @@ -218,23 +217,22 @@ export class RolesService { return invitationResults; } - private async buildInvitationResultForRoleManagerInvitation( - roleManager: IRoleManager, + private async buildInvitationResultForRoleSetInvitation( + roleSet: IRoleSet, state: string, invitation: IInvitation ): Promise { const communityDisplayName = - await this.communityResolverService.getDisplayNameForRoleManagerOrFail( - roleManager.id + await this.communityResolverService.getDisplayNameForRoleSetOrFail( + roleSet.id ); - const space = - await this.communityResolverService.getSpaceForRoleManagerOrFail( - roleManager.id - ); + const space = await this.communityResolverService.getSpaceForRoleSetOrFail( + roleSet.id + ); const invitationResult = new CommunityInvitationForRoleResult( - roleManager.id, + roleSet.id, communityDisplayName, state, invitation.id, diff --git a/src/services/infrastructure/entity-resolver/community.resolver.service.ts b/src/services/infrastructure/entity-resolver/community.resolver.service.ts index 0517564e4c..5a456e0396 100644 --- a/src/services/infrastructure/entity-resolver/community.resolver.service.ts +++ b/src/services/infrastructure/entity-resolver/community.resolver.service.ts @@ -42,17 +42,17 @@ export class CommunityResolverService { return space.levelZeroSpaceID; } - async getCommunityForRoleManager(roleManagerID: string): Promise { + async getCommunityForRoleSet(roleSetID: string): Promise { const community = await this.entityManager.findOne(Community, { where: { - roleManager: { - id: roleManagerID, + roleSet: { + id: roleSetID, }, }, }); if (!community) { throw new EntityNotFoundException( - `Unable to find Community for given RoleManager id: ${roleManagerID}`, + `Unable to find Community for given RoleSet id: ${roleSetID}`, LogContext.COMMUNITY ); } @@ -288,14 +288,12 @@ export class CommunityResolverService { return community; } - public async getSpaceForRoleManagerOrFail( - roleManagerID: string - ): Promise { + public async getSpaceForRoleSetOrFail(roleSetID: string): Promise { const space = await this.entityManager.findOne(Space, { where: { community: { - roleManager: { - id: roleManagerID, + roleSet: { + id: roleSetID, }, }, }, @@ -305,7 +303,7 @@ export class CommunityResolverService { }); if (!space) { throw new EntityNotFoundException( - `Unable to find space for community: ${roleManagerID}`, + `Unable to find space for community: ${roleSetID}`, LogContext.URL_GENERATOR ); } @@ -336,10 +334,10 @@ export class CommunityResolverService { return space; } - public async getDisplayNameForRoleManagerOrFail( - roleManagerID: string + public async getDisplayNameForRoleSetOrFail( + roleSetID: string ): Promise { - const space = await this.getSpaceForRoleManagerOrFail(roleManagerID); + const space = await this.getSpaceForRoleSetOrFail(roleSetID); return space.profile.displayName; } diff --git a/src/services/infrastructure/naming/naming.service.ts b/src/services/infrastructure/naming/naming.service.ts index 497bb5da41..c2e48f7cf6 100644 --- a/src/services/infrastructure/naming/naming.service.ts +++ b/src/services/infrastructure/naming/naming.service.ts @@ -22,7 +22,7 @@ import { IDiscussion } from '@platform/forum-discussion/discussion.interface'; import { SpaceReservedName } from '@common/enums/space.reserved.name'; import { generateNameId } from '@services/infrastructure/naming/generate.name.id'; import { Template } from '@domain/template/template/template.entity'; -import { IRoleManager } from '@domain/access/role-manager'; +import { IRoleSet } from '@domain/access/role-set'; export class NamingService { constructor( @@ -238,10 +238,10 @@ export class NamingService { return result; } - async getRoleManagerAndSettingsForCollaboration( + async getRoleSetAndSettingsForCollaboration( collaborationID: string ): Promise<{ - roleManager: IRoleManager; + roleSet: IRoleSet; spaceSettings: ISpaceSettings; }> { const space = await this.entityManager.findOne(Space, { @@ -252,24 +252,24 @@ export class NamingService { }, relations: { community: { - roleManager: true, + roleSet: true, }, }, }); - if (!space || !space.community || !space.community.roleManager) { + if (!space || !space.community || !space.community.roleSet) { throw new EntityNotInitializedException( `Unable to load all entities for space with collaboration ${collaborationID}`, LogContext.COMMUNITY ); } // Directly parse the settings string to avoid the need to load the settings service - const roleManager = space.community.roleManager; + const roleSet = space.community.roleSet; const spaceSettings: ISpaceSettings = JSON.parse(space.settingsStr); - return { roleManager, spaceSettings }; + return { roleSet, spaceSettings }; } - async getRoleManagerAndSettingsForCallout(calloutID: string): Promise<{ - roleManager: IRoleManager; + async getRoleSetAndSettingsForCallout(calloutID: string): Promise<{ + roleSet: IRoleSet; spaceSettings: ISpaceSettings; }> { const space = await this.entityManager.findOne(Space, { @@ -282,11 +282,11 @@ export class NamingService { }, relations: { community: { - roleManager: true, + roleSet: true, }, }, }); - if (!space || !space.community || !space.community.roleManager) { + if (!space || !space.community || !space.community.roleSet) { throw new EntityNotInitializedException( `Unable to load all entities for space with callout ${calloutID}`, LogContext.COMMUNITY @@ -294,10 +294,10 @@ export class NamingService { } // Directly parse the settings string to avoid the need to load the settings service - const roleManager = space.community.roleManager; + const roleSet = space.community.roleSet; const spaceSettings: ISpaceSettings = JSON.parse(space.settingsStr); - return { roleManager: roleManager, spaceSettings }; + return { roleSet: roleSet, spaceSettings }; } async getPostForRoom(roomID: string): Promise { From 4874102e6e8c03874f3e6f4dfc83c73ec2a1b00c Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Mon, 16 Sep 2024 08:08:54 +0100 Subject: [PATCH 04/78] tidied up module imports RoleSet --- src/app.module.ts | 2 ++ src/domain/access/role-set/role.set.module.ts | 9 --------- src/domain/access/role/role.interface.ts | 2 +- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/app.module.ts b/src/app.module.ts index 0e4c648345..89533daf34 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -82,6 +82,7 @@ import { LookupByNameModule } from '@services/api/lookup-by-name'; import { PlatformHubModule } from '@platform/platfrom.hub/platform.hub.module'; import { AdminContributorsModule } from '@platform/admin/avatars/admin.avatar.module'; import { InputCreatorModule } from '@services/api/input-creator/input.creator.module'; +import { RoleSetModule } from '@domain/access/role-set/role.set.module'; @Module({ imports: [ @@ -240,6 +241,7 @@ import { InputCreatorModule } from '@services/api/input-creator/input.creator.mo SearchModule, ActivityLogModule, RolesModule, + RoleSetModule, KonfigModule, AdminContributorsModule, AdminCommunicationModule, diff --git a/src/domain/access/role-set/role.set.module.ts b/src/domain/access/role-set/role.set.module.ts index da10d249b4..bb2dde637b 100644 --- a/src/domain/access/role-set/role.set.module.ts +++ b/src/domain/access/role-set/role.set.module.ts @@ -1,17 +1,13 @@ import { AuthorizationModule } from '@core/authorization/authorization.module'; -import { AgentModule } from '@domain/agent/agent/agent.module'; import { AuthorizationPolicyModule } from '@domain/common/authorization-policy/authorization.policy.module'; -import { CommunicationModule } from '@domain/communication/communication/communication.module'; import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { TrustRegistryAdapterModule } from '@services/external/trust-registry/trust.registry.adapter/trust.registry.adapter.module'; import { RoleSet } from './role.set.entity'; import { RoleSetResolverFields } from './role.set.resolver.fields'; import { RoleSetResolverMutations } from './role.set.resolver.mutations'; import { RoleSetService } from './role.set.service'; import { RoleSetAuthorizationService } from './role.set.service.authorization'; import { FormModule } from '@domain/common/form/form.module'; -import { StorageAggregatorResolverModule } from '@services/infrastructure/storage-aggregator-resolver/storage.aggregator.resolver.module'; import { LicenseEngineModule } from '@core/license-engine/license.engine.module'; import { PlatformInvitationModule } from '@platform/invitation/platform.invitation.module'; import { InvitationModule } from '@domain/community/invitation/invitation.module'; @@ -23,11 +19,7 @@ import { RoleModule } from '../role/role.module'; imports: [ AuthorizationModule, AuthorizationPolicyModule, - AgentModule, - CommunicationModule, LicenseEngineModule, - AgentModule, - StorageAggregatorResolverModule, FormModule, RoleModule, InvitationModule, @@ -35,7 +27,6 @@ import { RoleModule } from '../role/role.module'; PlatformInvitationModule, VirtualContributorModule, TypeOrmModule.forFeature([RoleSet]), - TrustRegistryAdapterModule, ], providers: [ RoleSetService, diff --git a/src/domain/access/role/role.interface.ts b/src/domain/access/role/role.interface.ts index 9ecbfd0381..5e115df9b7 100644 --- a/src/domain/access/role/role.interface.ts +++ b/src/domain/access/role/role.interface.ts @@ -2,7 +2,7 @@ import { CommunityRoleType } from '@common/enums/community.role'; import { IBaseAlkemio } from '@domain/common/entity/base-entity'; import { Field, ObjectType } from '@nestjs/graphql'; -@ObjectType('Role') +@ObjectType('Role2') export abstract class IRole extends IBaseAlkemio { @Field(() => CommunityRoleType, { nullable: false, From 8551ee60a6632f931cb58ca716bdcb0b0623b1be Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Mon, 16 Sep 2024 08:16:20 +0100 Subject: [PATCH 05/78] remoed circular dependencies --- src/domain/access/role/role.entity.ts | 2 +- src/domain/access/role/role.interface.ts | 2 +- src/domain/access/role/role.module.ts | 2 +- src/domain/community/application/application.entity.ts | 2 +- src/domain/community/application/application.interface.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/domain/access/role/role.entity.ts b/src/domain/access/role/role.entity.ts index 568eeb5e01..89db6a1158 100644 --- a/src/domain/access/role/role.entity.ts +++ b/src/domain/access/role/role.entity.ts @@ -3,7 +3,7 @@ import { Column, Entity, ManyToOne } from 'typeorm'; import { IRole } from './role.interface'; import { CommunityRoleType } from '@common/enums/community.role'; import { ENUM_LENGTH } from '@common/constants/entity.field.length.constants'; -import { RoleSet } from '../role-set'; +import { RoleSet } from '../role-set/role.set.entity'; @Entity() export class Role extends BaseAlkemioEntity implements IRole { diff --git a/src/domain/access/role/role.interface.ts b/src/domain/access/role/role.interface.ts index 5e115df9b7..9ecbfd0381 100644 --- a/src/domain/access/role/role.interface.ts +++ b/src/domain/access/role/role.interface.ts @@ -2,7 +2,7 @@ import { CommunityRoleType } from '@common/enums/community.role'; import { IBaseAlkemio } from '@domain/common/entity/base-entity'; import { Field, ObjectType } from '@nestjs/graphql'; -@ObjectType('Role2') +@ObjectType('Role') export abstract class IRole extends IBaseAlkemio { @Field(() => CommunityRoleType, { nullable: false, diff --git a/src/domain/access/role/role.module.ts b/src/domain/access/role/role.module.ts index 4e78c33c65..8da65602e7 100644 --- a/src/domain/access/role/role.module.ts +++ b/src/domain/access/role/role.module.ts @@ -6,7 +6,7 @@ import { RoleService } from './role.service'; @Module({ imports: [TypeOrmModule.forFeature([Role])], - providers: [RoleService, RoleResolverFields], + providers: [RoleResolverFields, RoleService], exports: [RoleService], }) export class RoleModule {} diff --git a/src/domain/community/application/application.entity.ts b/src/domain/community/application/application.entity.ts index 2c767a3b67..4129b3f740 100644 --- a/src/domain/community/application/application.entity.ts +++ b/src/domain/community/application/application.entity.ts @@ -12,7 +12,7 @@ import { NVP } from '@domain/common/nvp/nvp.entity'; import { User } from '@domain/community/user/user.entity'; import { AuthorizableEntity } from '@domain/common/entity/authorizable-entity'; import { IQuestion } from '@domain/common/question/question.interface'; -import { RoleSet } from '@domain/access/role-set'; +import { RoleSet } from '@domain/access/role-set/role.set.entity'; @Entity() export class Application extends AuthorizableEntity implements IApplication { @OneToOne(() => Lifecycle, { diff --git a/src/domain/community/application/application.interface.ts b/src/domain/community/application/application.interface.ts index 9722b9a06a..82acbbf459 100644 --- a/src/domain/community/application/application.interface.ts +++ b/src/domain/community/application/application.interface.ts @@ -3,7 +3,7 @@ import { IUser } from '@domain/community/user/user.interface'; import { Field, ObjectType } from '@nestjs/graphql'; import { IAuthorizable } from '@domain/common/entity/authorizable-entity'; import { IQuestion } from '@domain/common/question/question.interface'; -import { IRoleSet } from '@domain/access/role-set'; +import { IRoleSet } from '@domain/access/role-set/role.set.interface'; @ObjectType('Application') export abstract class IApplication extends IAuthorizable { From 88a3bbfd3870c9349d259756534c2919951d0cfc Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Mon, 16 Sep 2024 08:23:05 +0100 Subject: [PATCH 06/78] moved application, invitation to be under access namespace --- .../validation/handlers/base/base.handler.ts | 2 +- .../application/application.entity.ts | 0 .../application/application.interface.ts | 0 .../application/application.lifecycle.config.ts | 0 .../application/application.module.ts | 4 ++-- .../application/application.resolver.fields.ts | 4 ++-- .../application.resolver.mutations.ts | 4 ++-- .../application.service.authorization.ts | 0 .../application/application.service.ts | 8 ++++---- .../application/dto/application.dto.create.ts | 0 .../application/dto/application.dto.delete.ts | 0 .../application/dto/application.dto.event.ts | 0 .../{community => access}/application/index.ts | 0 .../invitation/dto/invitation.dto.create.ts | 0 .../invitation/dto/invitation.dto.delete.ts | 0 .../invitation/dto/invitation.dto.event.ts | 0 .../{community => access}/invitation/index.ts | 0 .../invitation/invitation.entity.ts | 0 .../invitation/invitation.interface.ts | 0 .../invitation/invitation.lifecycle.config.ts | 0 .../invitation/invitation.module.ts | 8 ++++---- .../invitation/invitation.resolver.fields.ts | 4 ++-- .../invitation/invitation.resolver.mutations.ts | 0 .../invitation.service.authorization.ts | 4 ++-- .../invitation/invitation.service.ts | 14 +++++++------- src/domain/access/role-set/role.set.entity.ts | 4 ++-- .../access/role-set/role.set.interface.ts | 4 ++-- src/domain/access/role-set/role.set.module.ts | 4 ++-- .../role-set/role.set.service.authorization.ts | 4 ++-- src/domain/access/role-set/role.set.service.ts | 4 ++-- ...le.lifecycle.application.options.provider.ts | 4 ++-- ...ole.lifecycle.invitation.options.provider.ts | 6 +++--- .../community-role/community.role.module.ts | 4 ++-- .../community.role.resolver.fields.ts | 4 ++-- .../community.role.resolver.mutations.ts | 17 +++++++++-------- .../community-role/community.role.service.ts | 10 +++++----- src/domain/community/user/user.entity.ts | 2 +- .../virtual.contributor.service.ts | 3 ++- src/services/api/lookup/lookup.module.ts | 4 ++-- .../api/lookup/lookup.resolver.fields.ts | 8 ++++---- .../api/me/dto/me.application.result.ts | 2 +- src/services/api/me/dto/me.invitation.result.ts | 2 +- src/services/api/me/dto/me.query.results.ts | 4 ++-- src/services/api/me/me.module.ts | 4 ++-- .../api/registration/registration.module.ts | 4 ++-- .../api/registration/registration.service.ts | 10 +++++----- src/services/api/roles/roles.module.ts | 4 ++-- src/services/api/roles/roles.service.spec.ts | 2 +- src/services/api/roles/roles.service.ts | 8 ++++---- test/mocks/application.service.mock.ts | 2 +- 50 files changed, 89 insertions(+), 87 deletions(-) rename src/domain/{community => access}/application/application.entity.ts (100%) rename src/domain/{community => access}/application/application.interface.ts (100%) rename src/domain/{community => access}/application/application.lifecycle.config.ts (100%) rename src/domain/{community => access}/application/application.module.ts (88%) rename src/domain/{community => access}/application/application.resolver.fields.ts (90%) rename src/domain/{community => access}/application/application.resolver.mutations.ts (90%) rename src/domain/{community => access}/application/application.service.authorization.ts (100%) rename src/domain/{community => access}/application/application.service.ts (95%) rename src/domain/{community => access}/application/dto/application.dto.create.ts (100%) rename src/domain/{community => access}/application/dto/application.dto.delete.ts (100%) rename src/domain/{community => access}/application/dto/application.dto.event.ts (100%) rename src/domain/{community => access}/application/index.ts (100%) rename src/domain/{community => access}/invitation/dto/invitation.dto.create.ts (100%) rename src/domain/{community => access}/invitation/dto/invitation.dto.delete.ts (100%) rename src/domain/{community => access}/invitation/dto/invitation.dto.event.ts (100%) rename src/domain/{community => access}/invitation/index.ts (100%) rename src/domain/{community => access}/invitation/invitation.entity.ts (100%) rename src/domain/{community => access}/invitation/invitation.interface.ts (100%) rename src/domain/{community => access}/invitation/invitation.lifecycle.config.ts (100%) rename src/domain/{community => access}/invitation/invitation.module.ts (77%) rename src/domain/{community => access}/invitation/invitation.resolver.fields.ts (90%) rename src/domain/{community => access}/invitation/invitation.resolver.mutations.ts (100%) rename src/domain/{community => access}/invitation/invitation.service.authorization.ts (94%) rename src/domain/{community => access}/invitation/invitation.service.ts (92%) diff --git a/src/core/validation/handlers/base/base.handler.ts b/src/core/validation/handlers/base/base.handler.ts index c2fd745358..47ec309c2d 100644 --- a/src/core/validation/handlers/base/base.handler.ts +++ b/src/core/validation/handlers/base/base.handler.ts @@ -24,7 +24,7 @@ import { CreateTagsetOnProfileInput, UpdateProfileInput, } from '@domain/common/profile/dto'; -import { ApplicationEventInput } from '@domain/community/application/dto/application.dto.event'; +import { ApplicationEventInput } from '@domain/access/application/dto/application.dto.event'; import { OrganizationVerificationEventInput } from '@domain/community/organization-verification/dto/organization.verification.dto.event'; import { RoomSendMessageInput } from '@domain/communication/room/dto/room.dto.send.message'; import { UpdatePostInput } from '@domain/collaboration/post/dto/post.dto.update'; diff --git a/src/domain/community/application/application.entity.ts b/src/domain/access/application/application.entity.ts similarity index 100% rename from src/domain/community/application/application.entity.ts rename to src/domain/access/application/application.entity.ts diff --git a/src/domain/community/application/application.interface.ts b/src/domain/access/application/application.interface.ts similarity index 100% rename from src/domain/community/application/application.interface.ts rename to src/domain/access/application/application.interface.ts diff --git a/src/domain/community/application/application.lifecycle.config.ts b/src/domain/access/application/application.lifecycle.config.ts similarity index 100% rename from src/domain/community/application/application.lifecycle.config.ts rename to src/domain/access/application/application.lifecycle.config.ts diff --git a/src/domain/community/application/application.module.ts b/src/domain/access/application/application.module.ts similarity index 88% rename from src/domain/community/application/application.module.ts rename to src/domain/access/application/application.module.ts index f53b7785f1..76a103f517 100644 --- a/src/domain/community/application/application.module.ts +++ b/src/domain/access/application/application.module.ts @@ -1,7 +1,7 @@ import { LifecycleModule } from '@domain/common/lifecycle/lifecycle.module'; import { NVPModule } from '@domain/common/nvp/nvp.module'; -import { Application } from '@domain/community/application'; -import { ApplicationService } from '@domain/community/application/application.service'; +import { Application } from '@domain/access/application'; +import { ApplicationService } from '@domain/access/application/application.service'; import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { UserModule } from '@domain/community/user/user.module'; diff --git a/src/domain/community/application/application.resolver.fields.ts b/src/domain/access/application/application.resolver.fields.ts similarity index 90% rename from src/domain/community/application/application.resolver.fields.ts rename to src/domain/access/application/application.resolver.fields.ts index 14aa0c373d..490bcfb8ad 100644 --- a/src/domain/community/application/application.resolver.fields.ts +++ b/src/domain/access/application/application.resolver.fields.ts @@ -3,11 +3,11 @@ import { Resolver } from '@nestjs/graphql'; import { Parent, ResolveField } from '@nestjs/graphql'; import { ApplicationService } from './application.service'; import { AuthorizationPrivilege } from '@common/enums'; -import { Application, IApplication } from '@domain/community/application'; +import { Application, IApplication } from '@domain/access/application'; import { GraphqlGuard } from '@core/authorization'; import { AuthorizationAgentPrivilege, Profiling } from '@src/common/decorators'; import { IQuestion } from '@domain/common/question/question.interface'; -import { IContributor } from '../contributor/contributor.interface'; +import { IContributor } from '../../community/contributor/contributor.interface'; @Resolver(() => IApplication) export class ApplicationResolverFields { diff --git a/src/domain/community/application/application.resolver.mutations.ts b/src/domain/access/application/application.resolver.mutations.ts similarity index 90% rename from src/domain/community/application/application.resolver.mutations.ts rename to src/domain/access/application/application.resolver.mutations.ts index ee9a358500..2135f8167e 100644 --- a/src/domain/community/application/application.resolver.mutations.ts +++ b/src/domain/access/application/application.resolver.mutations.ts @@ -1,8 +1,8 @@ import { UseGuards } from '@nestjs/common'; import { Args, Mutation, Resolver } from '@nestjs/graphql'; import { CurrentUser } from '@src/common/decorators'; -import { IApplication } from '@domain/community/application'; -import { ApplicationService } from '@domain/community/application/application.service'; +import { IApplication } from '@domain/access/application'; +import { ApplicationService } from '@domain/access/application/application.service'; import { GraphqlGuard } from '@core/authorization'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { AuthorizationPrivilege } from '@common/enums'; diff --git a/src/domain/community/application/application.service.authorization.ts b/src/domain/access/application/application.service.authorization.ts similarity index 100% rename from src/domain/community/application/application.service.authorization.ts rename to src/domain/access/application/application.service.authorization.ts diff --git a/src/domain/community/application/application.service.ts b/src/domain/access/application/application.service.ts similarity index 95% rename from src/domain/community/application/application.service.ts rename to src/domain/access/application/application.service.ts index ee7ca49cd2..64ff24095f 100644 --- a/src/domain/community/application/application.service.ts +++ b/src/domain/access/application/application.service.ts @@ -1,9 +1,9 @@ -import { CreateApplicationInput } from '@domain/community/application'; +import { CreateApplicationInput } from '@domain/access/application'; import { Application, IApplication, DeleteApplicationInput, -} from '@domain/community/application'; +} from '@domain/access/application'; import { Inject, Injectable, LoggerService } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; @@ -17,12 +17,12 @@ import { FindManyOptions, FindOneOptions, Repository } from 'typeorm'; import { NVPService } from '@domain/common/nvp/nvp.service'; import { UserService } from '@domain/community/user/user.service'; import { LifecycleService } from '@domain/common/lifecycle/lifecycle.service'; -import { applicationLifecycleConfig } from '@domain/community/application/application.lifecycle.config'; +import { applicationLifecycleConfig } from '@domain/access/application/application.lifecycle.config'; import { AuthorizationPolicy } from '@domain/common/authorization-policy'; import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service'; import { IQuestion } from '@domain/common/question/question.interface'; import { asyncFilter } from '@common/utils'; -import { IContributor } from '../contributor/contributor.interface'; +import { IContributor } from '../../community/contributor/contributor.interface'; import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type'; @Injectable() diff --git a/src/domain/community/application/dto/application.dto.create.ts b/src/domain/access/application/dto/application.dto.create.ts similarity index 100% rename from src/domain/community/application/dto/application.dto.create.ts rename to src/domain/access/application/dto/application.dto.create.ts diff --git a/src/domain/community/application/dto/application.dto.delete.ts b/src/domain/access/application/dto/application.dto.delete.ts similarity index 100% rename from src/domain/community/application/dto/application.dto.delete.ts rename to src/domain/access/application/dto/application.dto.delete.ts diff --git a/src/domain/community/application/dto/application.dto.event.ts b/src/domain/access/application/dto/application.dto.event.ts similarity index 100% rename from src/domain/community/application/dto/application.dto.event.ts rename to src/domain/access/application/dto/application.dto.event.ts diff --git a/src/domain/community/application/index.ts b/src/domain/access/application/index.ts similarity index 100% rename from src/domain/community/application/index.ts rename to src/domain/access/application/index.ts diff --git a/src/domain/community/invitation/dto/invitation.dto.create.ts b/src/domain/access/invitation/dto/invitation.dto.create.ts similarity index 100% rename from src/domain/community/invitation/dto/invitation.dto.create.ts rename to src/domain/access/invitation/dto/invitation.dto.create.ts diff --git a/src/domain/community/invitation/dto/invitation.dto.delete.ts b/src/domain/access/invitation/dto/invitation.dto.delete.ts similarity index 100% rename from src/domain/community/invitation/dto/invitation.dto.delete.ts rename to src/domain/access/invitation/dto/invitation.dto.delete.ts diff --git a/src/domain/community/invitation/dto/invitation.dto.event.ts b/src/domain/access/invitation/dto/invitation.dto.event.ts similarity index 100% rename from src/domain/community/invitation/dto/invitation.dto.event.ts rename to src/domain/access/invitation/dto/invitation.dto.event.ts diff --git a/src/domain/community/invitation/index.ts b/src/domain/access/invitation/index.ts similarity index 100% rename from src/domain/community/invitation/index.ts rename to src/domain/access/invitation/index.ts diff --git a/src/domain/community/invitation/invitation.entity.ts b/src/domain/access/invitation/invitation.entity.ts similarity index 100% rename from src/domain/community/invitation/invitation.entity.ts rename to src/domain/access/invitation/invitation.entity.ts diff --git a/src/domain/community/invitation/invitation.interface.ts b/src/domain/access/invitation/invitation.interface.ts similarity index 100% rename from src/domain/community/invitation/invitation.interface.ts rename to src/domain/access/invitation/invitation.interface.ts diff --git a/src/domain/community/invitation/invitation.lifecycle.config.ts b/src/domain/access/invitation/invitation.lifecycle.config.ts similarity index 100% rename from src/domain/community/invitation/invitation.lifecycle.config.ts rename to src/domain/access/invitation/invitation.lifecycle.config.ts diff --git a/src/domain/community/invitation/invitation.module.ts b/src/domain/access/invitation/invitation.module.ts similarity index 77% rename from src/domain/community/invitation/invitation.module.ts rename to src/domain/access/invitation/invitation.module.ts index 45dabc51f4..8b46b9fc7b 100644 --- a/src/domain/community/invitation/invitation.module.ts +++ b/src/domain/access/invitation/invitation.module.ts @@ -1,6 +1,6 @@ import { LifecycleModule } from '@domain/common/lifecycle/lifecycle.module'; -import { Invitation } from '@domain/community/invitation'; -import { InvitationService } from '@domain/community/invitation/invitation.service'; +import { Invitation } from '@domain/access/invitation'; +import { InvitationService } from '@domain/access/invitation/invitation.service'; import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { UserModule } from '@domain/community/user/user.module'; @@ -9,8 +9,8 @@ import { InvitationResolverFields } from './invitation.resolver.fields'; import { AuthorizationModule } from '@core/authorization/authorization.module'; import { InvitationAuthorizationService } from './invitation.service.authorization'; import { InvitationResolverMutations } from './invitation.resolver.mutations'; -import { ContributorModule } from '../contributor/contributor.module'; -import { VirtualContributorModule } from '../virtual-contributor/virtual.contributor.module'; +import { ContributorModule } from '@domain/community/contributor/contributor.module'; +import { VirtualContributorModule } from '@domain/community/virtual-contributor/virtual.contributor.module'; @Module({ imports: [ diff --git a/src/domain/community/invitation/invitation.resolver.fields.ts b/src/domain/access/invitation/invitation.resolver.fields.ts similarity index 90% rename from src/domain/community/invitation/invitation.resolver.fields.ts rename to src/domain/access/invitation/invitation.resolver.fields.ts index 0d2ead4f33..16b6267ce2 100644 --- a/src/domain/community/invitation/invitation.resolver.fields.ts +++ b/src/domain/access/invitation/invitation.resolver.fields.ts @@ -3,11 +3,11 @@ import { Resolver } from '@nestjs/graphql'; import { Parent, ResolveField } from '@nestjs/graphql'; import { InvitationService } from './invitation.service'; import { AuthorizationPrivilege } from '@common/enums'; -import { IInvitation } from '@domain/community/invitation'; +import { IInvitation } from '@domain/access/invitation'; import { GraphqlGuard } from '@core/authorization'; import { IUser } from '@domain/community/user/user.interface'; import { AuthorizationAgentPrivilege, Profiling } from '@src/common/decorators'; -import { IContributor } from '../contributor/contributor.interface'; +import { IContributor } from '@domain/community/contributor/contributor.interface'; @Resolver(() => IInvitation) export class InvitationResolverFields { diff --git a/src/domain/community/invitation/invitation.resolver.mutations.ts b/src/domain/access/invitation/invitation.resolver.mutations.ts similarity index 100% rename from src/domain/community/invitation/invitation.resolver.mutations.ts rename to src/domain/access/invitation/invitation.resolver.mutations.ts diff --git a/src/domain/community/invitation/invitation.service.authorization.ts b/src/domain/access/invitation/invitation.service.authorization.ts similarity index 94% rename from src/domain/community/invitation/invitation.service.authorization.ts rename to src/domain/access/invitation/invitation.service.authorization.ts index e673e5c792..287e7b9452 100644 --- a/src/domain/community/invitation/invitation.service.authorization.ts +++ b/src/domain/access/invitation/invitation.service.authorization.ts @@ -6,10 +6,10 @@ import { IAuthorizationPolicy } from '@domain/common/authorization-policy/author import { IInvitation } from './invitation.interface'; import { IAuthorizationPolicyRuleCredential } from '@core/authorization/authorization.policy.rule.credential.interface'; import { CREDENTIAL_RULE_COMMUNITY_USER_INVITATION as CREDENTIAL_RULE_COMMUNITY_CONTRIBUTOR_INVITATION } from '@common/constants/authorization/credential.rule.constants'; -import { ContributorService } from '../contributor/contributor.service'; import { CommunityContributorType } from '@common/enums/community.contributor.type'; -import { VirtualContributorService } from '../virtual-contributor/virtual.contributor.service'; import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; +import { VirtualContributorService } from '@domain/community/virtual-contributor/virtual.contributor.service'; +import { ContributorService } from '@domain/community/contributor/contributor.service'; @Injectable() export class InvitationAuthorizationService { diff --git a/src/domain/community/invitation/invitation.service.ts b/src/domain/access/invitation/invitation.service.ts similarity index 92% rename from src/domain/community/invitation/invitation.service.ts rename to src/domain/access/invitation/invitation.service.ts index 58d6e0f3ee..fbf1b35599 100644 --- a/src/domain/community/invitation/invitation.service.ts +++ b/src/domain/access/invitation/invitation.service.ts @@ -1,9 +1,9 @@ -import { CreateInvitationInput } from '@domain/community/invitation'; +import { CreateInvitationInput } from '@domain/access/invitation'; import { Invitation, IInvitation, DeleteInvitationInput, -} from '@domain/community/invitation'; +} from '@domain/access/invitation'; import { Inject, Injectable, LoggerService } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; @@ -14,16 +14,16 @@ import { import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; import { FindManyOptions, FindOneOptions, Repository } from 'typeorm'; import { LifecycleService } from '@domain/common/lifecycle/lifecycle.service'; -import { invitationLifecycleConfig } from '@domain/community/invitation/invitation.lifecycle.config'; +import { invitationLifecycleConfig } from '@domain/access/invitation/invitation.lifecycle.config'; import { AuthorizationPolicy } from '@domain/common/authorization-policy'; import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service'; import { asyncFilter } from '@common/utils'; -import { IUser } from '../user/user.interface'; -import { UserService } from '../user/user.service'; import { LogContext } from '@common/enums/logging.context'; -import { ContributorService } from '../contributor/contributor.service'; -import { IContributor } from '../contributor/contributor.interface'; import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type'; +import { ContributorService } from '@domain/community/contributor/contributor.service'; +import { UserService } from '@domain/community/user/user.service'; +import { IContributor } from '@domain/community/contributor/contributor.interface'; +import { IUser } from '@domain/community/user/user.interface'; @Injectable() export class InvitationService { diff --git a/src/domain/access/role-set/role.set.entity.ts b/src/domain/access/role-set/role.set.entity.ts index b5ff9d4b6e..b73ae1a198 100644 --- a/src/domain/access/role-set/role.set.entity.ts +++ b/src/domain/access/role-set/role.set.entity.ts @@ -5,8 +5,8 @@ import { Role } from '../role/role.entity'; import { Form } from '@domain/common/form/form.entity'; import { PlatformInvitation } from '@platform/invitation/platform.invitation.entity'; import { IRoleSet } from './role.set.interface'; -import { Application } from '@domain/community/application/application.entity'; -import { Invitation } from '@domain/community/invitation/invitation.entity'; +import { Application } from '@domain/access/application/application.entity'; +import { Invitation } from '@domain/access/invitation/invitation.entity'; @Entity() export class RoleSet diff --git a/src/domain/access/role-set/role.set.interface.ts b/src/domain/access/role-set/role.set.interface.ts index 3c4f8463ab..20057a5621 100644 --- a/src/domain/access/role-set/role.set.interface.ts +++ b/src/domain/access/role-set/role.set.interface.ts @@ -2,8 +2,8 @@ import { ObjectType } from '@nestjs/graphql'; import { IAuthorizable } from '@domain/common/entity/authorizable-entity'; import { IForm } from '@domain/common/form/form.interface'; import { IPlatformInvitation } from '@platform/invitation'; -import { IApplication } from '@domain/community/application/application.interface'; -import { IInvitation } from '@domain/community/invitation/invitation.interface'; +import { IApplication } from '@domain/access/application/application.interface'; +import { IInvitation } from '@domain/access/invitation/invitation.interface'; import { IRole } from '../role/role.interface'; @ObjectType('RoleSet') diff --git a/src/domain/access/role-set/role.set.module.ts b/src/domain/access/role-set/role.set.module.ts index bb2dde637b..ac8bae6d3d 100644 --- a/src/domain/access/role-set/role.set.module.ts +++ b/src/domain/access/role-set/role.set.module.ts @@ -10,8 +10,8 @@ import { RoleSetAuthorizationService } from './role.set.service.authorization'; import { FormModule } from '@domain/common/form/form.module'; import { LicenseEngineModule } from '@core/license-engine/license.engine.module'; import { PlatformInvitationModule } from '@platform/invitation/platform.invitation.module'; -import { InvitationModule } from '@domain/community/invitation/invitation.module'; -import { ApplicationModule } from '@domain/community/application/application.module'; +import { InvitationModule } from '@domain/access/invitation/invitation.module'; +import { ApplicationModule } from '@domain/access/application/application.module'; import { VirtualContributorModule } from '@domain/community/virtual-contributor/virtual.contributor.module'; import { RoleModule } from '../role/role.module'; diff --git a/src/domain/access/role-set/role.set.service.authorization.ts b/src/domain/access/role-set/role.set.service.authorization.ts index 044b900afa..c5797874f6 100644 --- a/src/domain/access/role-set/role.set.service.authorization.ts +++ b/src/domain/access/role-set/role.set.service.authorization.ts @@ -34,8 +34,8 @@ import { IAgent } from '@domain/agent'; import { PlatformInvitationAuthorizationService } from '@platform/invitation/platform.invitation.service.authorization'; import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; import { EntityNotInitializedException } from '@common/exceptions/entity.not.initialized.exception'; -import { ApplicationAuthorizationService } from '@domain/community/application/application.service.authorization'; -import { InvitationAuthorizationService } from '@domain/community/invitation/invitation.service.authorization'; +import { ApplicationAuthorizationService } from '@domain/access/application/application.service.authorization'; +import { InvitationAuthorizationService } from '@domain/access/invitation/invitation.service.authorization'; import { VirtualContributorService } from '@domain/community/virtual-contributor/virtual.contributor.service'; import { CommunityMembershipPolicy } from '@common/enums/community.membership.policy'; import { CommunityRoleType } from '@common/enums/community.role'; diff --git a/src/domain/access/role-set/role.set.service.ts b/src/domain/access/role-set/role.set.service.ts index 06646ff647..24644d067d 100644 --- a/src/domain/access/role-set/role.set.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -15,8 +15,8 @@ import { UpdateFormInput } from '@domain/common/form/dto/form.dto.update'; import { CreateRoleSetInput } from './dto/role.set.dto.create'; import { PlatformInvitationService } from '@platform/invitation/platform.invitation.service'; import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type'; -import { ApplicationService } from '@domain/community/application/application.service'; -import { InvitationService } from '@domain/community/invitation/invitation.service'; +import { ApplicationService } from '@domain/access/application/application.service'; +import { InvitationService } from '@domain/access/invitation/invitation.service'; import { RoleSet } from './role.set.entity'; import { IRoleSet } from './role.set.interface'; import { RoleService } from '../role/role.service'; diff --git a/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts b/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts index a3e4e53479..b8fffe80d4 100644 --- a/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts +++ b/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts @@ -1,13 +1,13 @@ import { IApplication, ApplicationEventInput, -} from '@domain/community/application'; +} from '@domain/access/application'; import { Inject, Injectable, LoggerService } from '@nestjs/common'; import { AuthorizationPrivilege, LogContext } from '@common/enums'; import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; import { MachineOptions } from 'xstate'; import { LifecycleService } from '@domain/common/lifecycle/lifecycle.service'; -import { ApplicationService } from '@domain/community/application/application.service'; +import { ApplicationService } from '@domain/access/application/application.service'; import { EntityNotInitializedException } from '@common/exceptions'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { AuthorizationService } from '@core/authorization/authorization.service'; diff --git a/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts b/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts index d1b82bfcc0..8607bb6f96 100644 --- a/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts +++ b/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts @@ -8,10 +8,10 @@ import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { AuthorizationService } from '@core/authorization/authorization.service'; import { AuthorizationPolicy } from '@domain/common/authorization-policy'; import { CommunityRoleType } from '@common/enums/community.role'; -import { InvitationService } from '../invitation/invitation.service'; -import { InvitationEventInput } from '../invitation/dto/invitation.dto.event'; -import { IInvitation } from '../invitation'; import { CommunityRoleService } from './community.role.service'; +import { InvitationEventInput } from '@domain/access/invitation/dto/invitation.dto.event'; +import { IInvitation } from '@domain/access/invitation/invitation.interface'; +import { InvitationService } from '@domain/access/invitation/invitation.service'; @Injectable() export class CommunityRoleInvitationLifecycleOptionsProvider { diff --git a/src/domain/community/community-role/community.role.module.ts b/src/domain/community/community-role/community.role.module.ts index c13aab9c4f..a95a96bd5e 100644 --- a/src/domain/community/community-role/community.role.module.ts +++ b/src/domain/community/community-role/community.role.module.ts @@ -13,8 +13,7 @@ import { UserModule } from '../user/user.module'; import { ContributorModule } from '../contributor/contributor.module'; import { OrganizationModule } from '../organization/organization.module'; import { VirtualContributorModule } from '../virtual-contributor/virtual.contributor.module'; -import { ApplicationModule } from '../application/application.module'; -import { InvitationModule } from '../invitation/invitation.module'; +import { ApplicationModule } from '../../access/application/application.module'; import { AiServerAdapterModule } from '@services/adapters/ai-server-adapter/ai.server.adapter.module'; import { PlatformInvitationModule } from '@platform/invitation/platform.invitation.module'; import { EntityResolverModule } from '@services/infrastructure/entity-resolver/entity.resolver.module'; @@ -24,6 +23,7 @@ import { ActivityAdapterModule } from '@services/adapters/activity-adapter/activ import { LifecycleModule } from '@domain/common/lifecycle/lifecycle.module'; import { RoleSetModule } from '@domain/access/role-set/role.set.module'; import { RoleModule } from '@domain/access/role/role.module'; +import { InvitationModule } from '@domain/access/invitation/invitation.module'; @Module({ imports: [ diff --git a/src/domain/community/community-role/community.role.resolver.fields.ts b/src/domain/community/community-role/community.role.resolver.fields.ts index 88e13a6966..961ce421c5 100644 --- a/src/domain/community/community-role/community.role.resolver.fields.ts +++ b/src/domain/community/community-role/community.role.resolver.fields.ts @@ -8,7 +8,7 @@ import { } from '@src/common/decorators'; import { Community, ICommunity } from '@domain/community/community'; import { IUser } from '@domain/community/user/user.interface'; -import { IApplication } from '@domain/community/application'; +import { IApplication } from '@domain/access/application'; import { AuthorizationPrivilege } from '@common/enums'; import { IOrganization } from '../organization'; import { CommunityRoleType } from '@common/enums/community.role'; @@ -18,12 +18,12 @@ import { UserService } from '../user/user.service'; import { UserFilterInput } from '@core/filtering'; import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; -import { IInvitation } from '../invitation'; import { IVirtualContributor } from '../virtual-contributor/virtual.contributor.interface'; import { CommunityRoleImplicit } from '@common/enums/community.role.implicit'; import { IPlatformInvitation } from '@platform/invitation'; import { CommunityRoleService } from './community.role.service'; import { CommunityService } from '../community/community.service'; +import { IInvitation } from '@domain/access/invitation/invitation.interface'; @Resolver(() => ICommunity) export class CommunityResolverFields { diff --git a/src/domain/community/community-role/community.role.resolver.mutations.ts b/src/domain/community/community-role/community.role.resolver.mutations.ts index 3f11099dd4..f71213b382 100644 --- a/src/domain/community/community-role/community.role.resolver.mutations.ts +++ b/src/domain/community/community-role/community.role.resolver.mutations.ts @@ -1,8 +1,8 @@ import { UseGuards } from '@nestjs/common'; import { Args, Mutation, Resolver } from '@nestjs/graphql'; import { CurrentUser, Profiling } from '@src/common/decorators'; -import { IApplication } from '@domain/community/application'; -import { ApplicationService } from '@domain/community/application/application.service'; +import { IApplication } from '@domain/access/application'; +import { ApplicationService } from '@domain/access/application/application.service'; import { ICommunity } from '@domain/community/community/community.interface'; import { GraphqlGuard } from '@core/authorization'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; @@ -11,8 +11,8 @@ import { AuthorizationService } from '@core/authorization/authorization.service' import { UserService } from '@domain/community/user/user.service'; import { UserAuthorizationService } from '../user/user.service.authorization'; import { RemoveCommunityRoleFromUserInput } from './dto/community.role.dto.role.remove.user'; -import { ApplicationEventInput } from '../application/dto/application.dto.event'; -import { ApplicationAuthorizationService } from '../application/application.service.authorization'; +import { ApplicationEventInput } from '../../access/application/dto/application.dto.event'; +import { ApplicationAuthorizationService } from '../../access/application/application.service.authorization'; import { AgentService } from '@domain/agent/agent/agent.service'; import { CommunityJoinInput } from './dto/community.role.dto.join'; import { CommunityRoleApplyInput } from './dto/community.role.dto.apply'; @@ -25,11 +25,7 @@ import { CommunityRoleType } from '@common/enums/community.role'; import { AssignCommunityRoleToUserInput } from './dto/community.role.dto.role.assign.user'; import { NotificationAdapter } from '@services/adapters/notification-adapter/notification.adapter'; import { NotificationInputCommunityApplication } from '@services/adapters/notification-adapter/dto/notification.dto.input.community.application'; -import { InvitationAuthorizationService } from '../invitation/invitation.service.authorization'; -import { InvitationService } from '../invitation/invitation.service'; import { NotificationInputCommunityInvitation } from '@services/adapters/notification-adapter/dto/notification.dto.input.community.invitation'; -import { InvitationEventInput } from '../invitation/dto/invitation.dto.event'; -import { CreateInvitationInput, IInvitation } from '../invitation'; import { IOrganization } from '../organization'; import { IUser } from '../user/user.interface'; import { CreatePlatformInvitationOnCommunityInput } from './dto/community.role.dto.platform.invitation.community'; @@ -58,6 +54,11 @@ import { IVirtualContributor } from '../virtual-contributor/virtual.contributor. import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service'; import { RoleSetService } from '@domain/access/role-set/role.set.service'; import { CommunityResolverService } from '@services/infrastructure/entity-resolver/community.resolver.service'; +import { InvitationAuthorizationService } from '@domain/access/invitation/invitation.service.authorization'; +import { IInvitation } from '@domain/access/invitation/invitation.interface'; +import { CreateInvitationInput } from '@domain/access/invitation/dto/invitation.dto.create'; +import { InvitationEventInput } from '@domain/access/invitation/dto/invitation.dto.event'; +import { InvitationService } from '@domain/access/invitation/invitation.service'; @Resolver() export class CommunityRoleResolverMutations { diff --git a/src/domain/community/community-role/community.role.service.ts b/src/domain/community/community-role/community.role.service.ts index 8471b8c21c..757001abb6 100644 --- a/src/domain/community/community-role/community.role.service.ts +++ b/src/domain/community/community-role/community.role.service.ts @@ -1,7 +1,7 @@ import { CreateApplicationInput, IApplication, -} from '@domain/community/application'; +} from '@domain/access/application'; import { UserService } from '@domain/community/user/user.service'; import { Inject, Injectable, LoggerService } from '@nestjs/common'; import { @@ -12,7 +12,7 @@ import { import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; import { IUser } from '@domain/community/user/user.interface'; import { ICommunity } from '@domain/community/community'; -import { ApplicationService } from '@domain/community/application/application.service'; +import { ApplicationService } from '@domain/access/application/application.service'; import { AgentService } from '@domain/agent/agent/agent.service'; import { LogContext } from '@common/enums/logging.context'; import { OrganizationService } from '../organization/organization.service'; @@ -24,11 +24,8 @@ import { CommunityContributorsUpdateType } from '@common/enums/community.contrib import { CommunityContributorType } from '@common/enums/community.contributor.type'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; -import { InvitationService } from '../invitation/invitation.service'; -import { IInvitation } from '../invitation/invitation.interface'; import { CreatePlatformInvitationOnCommunityInput } from './dto/community.role.dto.platform.invitation.community'; import { CommunityResolverService } from '@services/infrastructure/entity-resolver/community.resolver.service'; -import { CreateInvitationInput } from '../invitation/dto/invitation.dto.create'; import { CommunityMembershipException } from '@common/exceptions/community.membership.exception'; import { CommunityRoleEventsService } from './community.role.service.events'; import { IVirtualContributor } from '../virtual-contributor/virtual.contributor.interface'; @@ -45,6 +42,9 @@ import { CommunityService } from '../community/community.service'; import { IRoleSet } from '@domain/access/role-set'; import { RoleSetService } from '@domain/access/role-set/role.set.service'; import { RoleService } from '@domain/access/role/role.service'; +import { CreateInvitationInput } from '@domain/access/invitation/dto/invitation.dto.create'; +import { IInvitation } from '@domain/access/invitation/invitation.interface'; +import { InvitationService } from '@domain/access/invitation/invitation.service'; @Injectable() export class CommunityRoleService { diff --git a/src/domain/community/user/user.entity.ts b/src/domain/community/user/user.entity.ts index 1a54b2ffe7..35e136fa8a 100644 --- a/src/domain/community/user/user.entity.ts +++ b/src/domain/community/user/user.entity.ts @@ -8,7 +8,7 @@ import { Generated, } from 'typeorm'; import { IUser } from '@domain/community/user/user.interface'; -import { Application } from '@domain/community/application/application.entity'; +import { Application } from '@domain/access/application/application.entity'; import { PreferenceSet } from '@domain/common/preference-set/preference.set.entity'; import { ContributorBase } from '../contributor/contributor.base.entity'; import { StorageAggregator } from '@domain/storage/storage-aggregator/storage.aggregator.entity'; diff --git a/src/domain/community/virtual-contributor/virtual.contributor.service.ts b/src/domain/community/virtual-contributor/virtual.contributor.service.ts index c28dd4c62f..379394a6b0 100644 --- a/src/domain/community/virtual-contributor/virtual.contributor.service.ts +++ b/src/domain/community/virtual-contributor/virtual.contributor.service.ts @@ -37,10 +37,11 @@ import { IAiPersona } from '../ai-persona'; import { IContributor } from '../contributor/contributor.interface'; import { AccountHostService } from '@domain/space/account.host/account.host.service'; import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; -import { Invitation } from '../invitation'; + import { AgentType } from '@common/enums/agent.type'; import { ContributorService } from '../contributor/contributor.service'; import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type'; +import { Invitation } from '@domain/access/invitation/invitation.entity'; @Injectable() export class VirtualContributorService { diff --git a/src/services/api/lookup/lookup.module.ts b/src/services/api/lookup/lookup.module.ts index 226a176abc..d46d18322e 100644 --- a/src/services/api/lookup/lookup.module.ts +++ b/src/services/api/lookup/lookup.module.ts @@ -13,8 +13,8 @@ import { ProfileModule } from '@domain/common/profile/profile.module'; import { ContextModule } from '@domain/context/context/context.module'; import { CalendarEventModule } from '@domain/timeline/event/event.module'; import { CalendarModule } from '@domain/timeline/calendar/calendar.module'; -import { ApplicationModule } from '@domain/community/application/application.module'; -import { InvitationModule } from '@domain/community/invitation/invitation.module'; +import { ApplicationModule } from '@domain/access/application/application.module'; +import { InvitationModule } from '@domain/access/invitation/invitation.module'; import { WhiteboardModule } from '@domain/common/whiteboard'; import { DocumentModule } from '@domain/storage/document/document.module'; import { StorageAggregatorModule } from '@domain/storage/storage-aggregator/storage.aggregator.module'; diff --git a/src/services/api/lookup/lookup.resolver.fields.ts b/src/services/api/lookup/lookup.resolver.fields.ts index 375f96e967..2b8e7c859b 100644 --- a/src/services/api/lookup/lookup.resolver.fields.ts +++ b/src/services/api/lookup/lookup.resolver.fields.ts @@ -28,10 +28,10 @@ import { CalendarEventService } from '@domain/timeline/event/event.service'; import { ICalendarEvent } from '@domain/timeline/event'; import { ICalendar } from '@domain/timeline/calendar/calendar.interface'; import { CalendarService } from '@domain/timeline/calendar/calendar.service'; -import { ApplicationService } from '@domain/community/application/application.service'; -import { InvitationService } from '@domain/community/invitation/invitation.service'; -import { IApplication } from '@domain/community/application'; -import { IInvitation } from '@domain/community/invitation'; +import { ApplicationService } from '@domain/access/application/application.service'; +import { InvitationService } from '@domain/access/invitation/invitation.service'; +import { IApplication } from '@domain/access/application'; +import { IInvitation } from '@domain/access/invitation'; import { WhiteboardService } from '@domain/common/whiteboard'; import { IWhiteboard } from '@domain/common/whiteboard/types'; import { DocumentService } from '@domain/storage/document/document.service'; diff --git a/src/services/api/me/dto/me.application.result.ts b/src/services/api/me/dto/me.application.result.ts index feb5277a5d..6dedfa8971 100644 --- a/src/services/api/me/dto/me.application.result.ts +++ b/src/services/api/me/dto/me.application.result.ts @@ -1,5 +1,5 @@ import { Field, ObjectType } from '@nestjs/graphql'; -import { IApplication } from '@domain/community/application'; +import { IApplication } from '@domain/access/application'; import { CommunityPendingMembershipResult } from './me.pending.membership.result'; @ObjectType() diff --git a/src/services/api/me/dto/me.invitation.result.ts b/src/services/api/me/dto/me.invitation.result.ts index 26e1acc94c..a4d26bdf81 100644 --- a/src/services/api/me/dto/me.invitation.result.ts +++ b/src/services/api/me/dto/me.invitation.result.ts @@ -1,5 +1,5 @@ import { Field, ObjectType } from '@nestjs/graphql'; -import { IInvitation } from '@domain/community/invitation/invitation.interface'; +import { IInvitation } from '@domain/access/invitation/invitation.interface'; import { CommunityPendingMembershipResult } from './me.pending.membership.result'; @ObjectType() diff --git a/src/services/api/me/dto/me.query.results.ts b/src/services/api/me/dto/me.query.results.ts index 6f16d1ff8f..92c453ccd4 100644 --- a/src/services/api/me/dto/me.query.results.ts +++ b/src/services/api/me/dto/me.query.results.ts @@ -1,7 +1,7 @@ import { ObjectType } from '@nestjs/graphql'; import { IUser } from '@domain/community/user/user.interface'; -import { IInvitation } from '@domain/community/invitation'; -import { IApplication } from '@domain/community/application'; +import { IInvitation } from '@domain/access/invitation'; +import { IApplication } from '@domain/access/application'; import { CommunityMembershipResult } from './me.membership.result'; @ObjectType() diff --git a/src/services/api/me/me.module.ts b/src/services/api/me/me.module.ts index 746da042e8..d3c67e735a 100644 --- a/src/services/api/me/me.module.ts +++ b/src/services/api/me/me.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { AuthorizationModule } from '@core/authorization/authorization.module'; -import { ApplicationModule } from '@domain/community/application/application.module'; -import { InvitationModule } from '@domain/community/invitation/invitation.module'; +import { ApplicationModule } from '@domain/access/application/application.module'; +import { InvitationModule } from '@domain/access/invitation/invitation.module'; import { UserModule } from '@domain/community/user/user.module'; import { MeService } from './me.service'; import { MeResolverQueries } from './me.resolver.queries'; diff --git a/src/services/api/registration/registration.module.ts b/src/services/api/registration/registration.module.ts index 29aebf215f..3f5bfb273e 100644 --- a/src/services/api/registration/registration.module.ts +++ b/src/services/api/registration/registration.module.ts @@ -5,8 +5,8 @@ import { OrganizationModule } from '@domain/community/organization/organization. import { RegistrationResolverMutations } from './registration.resolver.mutations'; import { AuthorizationModule } from '@core/authorization/authorization.module'; import { NotificationAdapterModule } from '@services/adapters/notification-adapter/notification.adapter.module'; -import { InvitationModule } from '@domain/community/invitation/invitation.module'; -import { ApplicationModule } from '@domain/community/application/application.module'; +import { InvitationModule } from '@domain/access/invitation/invitation.module'; +import { ApplicationModule } from '@domain/access/application/application.module'; import { PreferenceSetModule } from '@domain/common/preference-set/preference.set.module'; import { PlatformInvitationModule } from '@platform/invitation/platform.invitation.module'; import { PlatformRoleModule } from '@platform/platfrom.role/platform.role.module'; diff --git a/src/services/api/registration/registration.service.ts b/src/services/api/registration/registration.service.ts index 33014bae7d..1b61895fc0 100644 --- a/src/services/api/registration/registration.service.ts +++ b/src/services/api/registration/registration.service.ts @@ -10,12 +10,12 @@ import { getEmailDomain } from '@common/utils'; import { OrganizationVerificationEnum } from '@common/enums/organization.verification'; import { OrganizationPreferenceType } from '@common/enums/organization.preference.type'; import { PreferenceSetService } from '@domain/common/preference-set/preference.set.service'; -import { IInvitation } from '@domain/community/invitation/invitation.interface'; -import { InvitationAuthorizationService } from '@domain/community/invitation/invitation.service.authorization'; -import { CreateInvitationInput } from '@domain/community/invitation/dto/invitation.dto.create'; +import { IInvitation } from '@domain/access/invitation/invitation.interface'; +import { InvitationAuthorizationService } from '@domain/access/invitation/invitation.service.authorization'; +import { CreateInvitationInput } from '@domain/access/invitation/dto/invitation.dto.create'; import { DeleteUserInput } from '@domain/community/user/dto/user.dto.delete'; -import { InvitationService } from '@domain/community/invitation/invitation.service'; -import { ApplicationService } from '@domain/community/application/application.service'; +import { InvitationService } from '@domain/access/invitation/invitation.service'; +import { ApplicationService } from '@domain/access/application/application.service'; import { OrganizationRole } from '@common/enums/organization.role'; import { PlatformInvitationService } from '@platform/invitation/platform.invitation.service'; import { PlatformRoleService } from '@platform/platfrom.role/platform.role.service'; diff --git a/src/services/api/roles/roles.module.ts b/src/services/api/roles/roles.module.ts index 1ee2bdf3fd..6ea78a2dd1 100644 --- a/src/services/api/roles/roles.module.ts +++ b/src/services/api/roles/roles.module.ts @@ -7,11 +7,11 @@ import { RolesService } from './roles.service'; import { RolesResolverQueries } from './roles.resolver.queries'; import { CommunityModule } from '@domain/community/community/community.module'; import { AuthorizationModule } from '@core/authorization/authorization.module'; -import { ApplicationModule } from '@domain/community/application/application.module'; +import { ApplicationModule } from '@domain/access/application/application.module'; import { AuthorizationPolicyModule } from '@domain/common/authorization-policy/authorization.policy.module'; import { PlatformAuthorizationPolicyModule } from '@src/platform/authorization/platform.authorization.policy.module'; import { SpaceFilterModule } from '@services/infrastructure/space-filter/space.filter.module'; -import { InvitationModule } from '@domain/community/invitation/invitation.module'; +import { InvitationModule } from '@domain/access/invitation/invitation.module'; import { EntityResolverModule } from '@services/infrastructure/entity-resolver/entity.resolver.module'; import { RolesResolverFields } from './roles.resolver.fields'; import { VirtualContributorModule } from '@domain/community/virtual-contributor/virtual.contributor.module'; diff --git a/src/services/api/roles/roles.service.spec.ts b/src/services/api/roles/roles.service.spec.ts index 6fb179bd97..2ef69a6a7e 100644 --- a/src/services/api/roles/roles.service.spec.ts +++ b/src/services/api/roles/roles.service.spec.ts @@ -12,7 +12,7 @@ import { import { Test } from '@nestjs/testing'; import { RolesService } from './roles.service'; import { UserService } from '@domain/community/user/user.service'; -import { ApplicationService } from '@domain/community/application/application.service'; +import { ApplicationService } from '@domain/access/application/application.service'; import { OrganizationService } from '@domain/community/organization/organization.service'; import { CommunityService } from '@domain/community/community/community.service'; import { SpaceFilterService } from '@services/infrastructure/space-filter/space.filter.service'; diff --git a/src/services/api/roles/roles.service.ts b/src/services/api/roles/roles.service.ts index 245b8c2d25..e1d64c146f 100644 --- a/src/services/api/roles/roles.service.ts +++ b/src/services/api/roles/roles.service.ts @@ -4,8 +4,8 @@ import { InjectEntityManager } from '@nestjs/typeorm'; import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; import { OrganizationService } from '@domain/community/organization/organization.service'; import { UserService } from '@domain/community/user/user.service'; -import { ApplicationService } from '@domain/community/application/application.service'; -import { IApplication } from '@domain/community/application'; +import { ApplicationService } from '@domain/access/application/application.service'; +import { IApplication } from '@domain/access/application'; import { SpaceFilterService } from '@services/infrastructure/space-filter/space.filter.service'; import { RolesUserInput } from './dto/roles.dto.input.user'; import { ContributorRoles } from './dto/roles.dto.result.contributor'; @@ -13,8 +13,8 @@ import { CommunityApplicationForRoleResult } from './dto/roles.dto.result.commun import { RolesOrganizationInput } from './dto/roles.dto.input.organization'; import { mapSpaceCredentialsToRoles } from './util/map.space.credentials.to.roles'; import { CommunityInvitationForRoleResult } from './dto/roles.dto.result.community.invitation'; -import { InvitationService } from '@domain/community/invitation/invitation.service'; -import { IInvitation } from '@domain/community/invitation'; +import { InvitationService } from '@domain/access/invitation/invitation.service'; +import { IInvitation } from '@domain/access/invitation'; import { CommunityResolverService } from '@services/infrastructure/entity-resolver/community.resolver.service'; import { RolesResultOrganization } from './dto/roles.dto.result.organization'; import { mapOrganizationCredentialsToRoles } from './util/map.organization.credentials.to.roles'; diff --git a/test/mocks/application.service.mock.ts b/test/mocks/application.service.mock.ts index fc9ed10d6b..54a2c8af25 100644 --- a/test/mocks/application.service.mock.ts +++ b/test/mocks/application.service.mock.ts @@ -1,4 +1,4 @@ -import { ApplicationService } from '@domain/community/application/application.service'; +import { ApplicationService } from '@domain/access/application/application.service'; import { ValueProvider } from '@nestjs/common'; import { PublicPart } from '../utils/public-part'; From f9388963593d65b9cc8089f5b346fd39ee753da8 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Mon, 16 Sep 2024 09:35:21 +0100 Subject: [PATCH 07/78] removed community role module + put parts in other locations --- src/common/enums/alkemio.error.status.ts | 6 +- src/common/exceptions/index.ts | 2 +- ...on.ts => role.set.invitation.exception.ts} | 4 +- ...on.ts => role.set.membership.exception.ts} | 4 +- ... role.set.policy.role.limits.exception.ts} | 4 +- .../validation/handlers/base/base.handler.ts | 6 +- .../role-set/dto/role.set.dto.apply.ts} | 0 .../dto/role.set.dto.invite.contributor.ts} | 0 .../role-set/dto/role.set.dto.join.ts} | 0 ....set.dto.platform.invitation.community.ts} | 0 .../role.set.dto.role.assign.organization.ts} | 0 .../dto/role.set.dto.role.assign.user.ts} | 0 .../dto/role.set.dto.role.assign.virtual.ts} | 0 .../role.set.dto.role.remove.organization.ts} | 0 .../dto/role.set.dto.role.remove.user.ts} | 0 .../dto/role.set.dto.role.remove.virtual.ts} | 0 ...lifecycle.application.options.provider.ts} | 8 +- ....lifecycle.invitation.options.provider.ts} | 10 +- .../role-set/role.set.resolver.fields.ts | 214 +++- .../role-set/role.set.resolver.mutations.ts | 714 ++++++++++- .../role-set/role.set.service.events.ts} | 21 +- .../access/role-set/role.set.service.ts | 1018 +++++++++++++++- .../community-role/community.role.module.ts | 60 - .../community.role.resolver.fields.ts | 273 ----- .../community.role.resolver.mutations.ts | 767 ------------ .../community.role.service.spec.ts | 30 - .../community-role/community.role.service.ts | 1084 ----------------- src/domain/community/community-role/index.ts | 2 - .../community/community.resolver.fields.ts | 54 +- .../community.service.authorization.ts | 69 -- src/domain/space/space/space.module.ts | 2 - src/domain/space/space/space.service.ts | 21 +- .../admin.communication.module.ts | 4 +- .../admin.communication.service.ts | 8 +- .../api/conversion/conversion.module.ts | 4 +- .../api/conversion/conversion.service.ts | 42 +- src/services/api/me/me.module.ts | 2 - .../api/registration/registration.module.ts | 4 +- .../api/registration/registration.service.ts | 6 +- 39 files changed, 2070 insertions(+), 2373 deletions(-) rename src/common/exceptions/{community.invitation.exception.ts => role.set.invitation.exception.ts} (58%) rename src/common/exceptions/{community.membership.exception.ts => role.set.membership.exception.ts} (58%) rename src/common/exceptions/{community.policy.role.limits.exception.ts => role.set.policy.role.limits.exception.ts} (63%) rename src/domain/{community/community-role/dto/community.role.dto.apply.ts => access/role-set/dto/role.set.dto.apply.ts} (100%) rename src/domain/{community/community-role/dto/community.role.dto.invite.contributor.ts => access/role-set/dto/role.set.dto.invite.contributor.ts} (100%) rename src/domain/{community/community-role/dto/community.role.dto.join.ts => access/role-set/dto/role.set.dto.join.ts} (100%) rename src/domain/{community/community-role/dto/community.role.dto.platform.invitation.community.ts => access/role-set/dto/role.set.dto.platform.invitation.community.ts} (100%) rename src/domain/{community/community-role/dto/community.role.dto.role.assign.organization.ts => access/role-set/dto/role.set.dto.role.assign.organization.ts} (100%) rename src/domain/{community/community-role/dto/community.role.dto.role.assign.user.ts => access/role-set/dto/role.set.dto.role.assign.user.ts} (100%) rename src/domain/{community/community-role/dto/community.role.dto.role.assign.virtual.ts => access/role-set/dto/role.set.dto.role.assign.virtual.ts} (100%) rename src/domain/{community/community-role/dto/community.role.dto.role.remove.organization.ts => access/role-set/dto/role.set.dto.role.remove.organization.ts} (100%) rename src/domain/{community/community-role/dto/community.role.dto.role.remove.user.ts => access/role-set/dto/role.set.dto.role.remove.user.ts} (100%) rename src/domain/{community/community-role/dto/community.role.dto.role.remove.virtual.ts => access/role-set/dto/role.set.dto.role.remove.virtual.ts} (100%) rename src/domain/{community/community-role/community.role.lifecycle.application.options.provider.ts => access/role-set/role.set.lifecycle.application.options.provider.ts} (93%) rename src/domain/{community/community-role/community.role.lifecycle.invitation.options.provider.ts => access/role-set/role.set.lifecycle.invitation.options.provider.ts} (94%) rename src/domain/{community/community-role/community.role.service.events.ts => access/role-set/role.set.service.events.ts} (85%) delete mode 100644 src/domain/community/community-role/community.role.module.ts delete mode 100644 src/domain/community/community-role/community.role.resolver.fields.ts delete mode 100644 src/domain/community/community-role/community.role.resolver.mutations.ts delete mode 100644 src/domain/community/community-role/community.role.service.spec.ts delete mode 100644 src/domain/community/community-role/community.role.service.ts delete mode 100644 src/domain/community/community-role/index.ts diff --git a/src/common/enums/alkemio.error.status.ts b/src/common/enums/alkemio.error.status.ts index ba5d8e1aec..f4b3267dda 100644 --- a/src/common/enums/alkemio.error.status.ts +++ b/src/common/enums/alkemio.error.status.ts @@ -44,9 +44,9 @@ export enum AlkemioErrorStatus { PAGINATION_INPUT_OUT_OF_BOUND = 'PAGINATION_INPUT_OUT_OF_BOUND', PAGINATION_NOT_FOUND = 'PAGINATION_NOT_FOUND', PAGINATION_PARAM_NOT_FOUND = 'PAGINATION_PARAM_NOT_FOUND', - COMMUNITY_POLICY_ROLE_LIMITS_VIOLATED = 'COMMUNITY_POLICY_ROLE_LIMITS_VIOLATED', - COMMUNITY_MEMBERSHIP = 'COMMUNITY_MEMBERSHIP', - COMMUNITY_INVITATION = 'COMMUNITY_INVITATION', + ROLE_SET_POLICY_ROLE_LIMITS_VIOLATED = 'COMMUNITY_POLICY_ROLE_LIMITS_VIOLATED', + ROLE_SET_MEMBERSHIP = 'COMMUNITY_MEMBERSHIP', + ROLE_SET_INVITATION = 'COMMUNITY_INVITATION', LOGIN_FLOW_INIT = 'LOGIN_FLOW_INIT', LOGIN_FLOW = 'LOGIN_FLOW', SESSION_EXTEND = 'SESSION_EXTEND', diff --git a/src/common/exceptions/index.ts b/src/common/exceptions/index.ts index 13045405e3..110d3e64d8 100644 --- a/src/common/exceptions/index.ts +++ b/src/common/exceptions/index.ts @@ -10,7 +10,7 @@ export * from './relationship.not.found.exception'; export * from './validation.exception'; export * from './registration.exception'; export * from './matrix.entity.not.found.exception'; -export * from './community.policy.role.limits.exception'; +export * from './role.set.policy.role.limits.exception'; export * from './subscription'; export * from './pagination'; diff --git a/src/common/exceptions/community.invitation.exception.ts b/src/common/exceptions/role.set.invitation.exception.ts similarity index 58% rename from src/common/exceptions/community.invitation.exception.ts rename to src/common/exceptions/role.set.invitation.exception.ts index 2199a5e046..ad028f1b3a 100644 --- a/src/common/exceptions/community.invitation.exception.ts +++ b/src/common/exceptions/role.set.invitation.exception.ts @@ -1,8 +1,8 @@ import { LogContext, AlkemioErrorStatus } from '@common/enums'; import { BaseException } from './base.exception'; -export class CommunityInvitationException extends BaseException { +export class RoleSetInvitationException extends BaseException { constructor(error: string, context: LogContext, code?: AlkemioErrorStatus) { - super(error, context, code ?? AlkemioErrorStatus.COMMUNITY_INVITATION); + super(error, context, code ?? AlkemioErrorStatus.ROLE_SET_INVITATION); } } diff --git a/src/common/exceptions/community.membership.exception.ts b/src/common/exceptions/role.set.membership.exception.ts similarity index 58% rename from src/common/exceptions/community.membership.exception.ts rename to src/common/exceptions/role.set.membership.exception.ts index 6bfdd8ed3c..4585b79720 100644 --- a/src/common/exceptions/community.membership.exception.ts +++ b/src/common/exceptions/role.set.membership.exception.ts @@ -1,8 +1,8 @@ import { LogContext, AlkemioErrorStatus } from '@common/enums'; import { BaseException } from './base.exception'; -export class CommunityMembershipException extends BaseException { +export class RoleSetMembershipException extends BaseException { constructor(error: string, context: LogContext, code?: AlkemioErrorStatus) { - super(error, context, code ?? AlkemioErrorStatus.COMMUNITY_MEMBERSHIP); + super(error, context, code ?? AlkemioErrorStatus.ROLE_SET_MEMBERSHIP); } } diff --git a/src/common/exceptions/community.policy.role.limits.exception.ts b/src/common/exceptions/role.set.policy.role.limits.exception.ts similarity index 63% rename from src/common/exceptions/community.policy.role.limits.exception.ts rename to src/common/exceptions/role.set.policy.role.limits.exception.ts index 28c424d029..f115302fab 100644 --- a/src/common/exceptions/community.policy.role.limits.exception.ts +++ b/src/common/exceptions/role.set.policy.role.limits.exception.ts @@ -1,12 +1,12 @@ import { LogContext, AlkemioErrorStatus } from '@common/enums'; import { BaseException } from './base.exception'; -export class CommunityPolicyRoleLimitsException extends BaseException { +export class RoleSetPolicyRoleLimitsException extends BaseException { constructor(error: string, context: LogContext, code?: AlkemioErrorStatus) { super( error, context, - code ?? AlkemioErrorStatus.COMMUNITY_POLICY_ROLE_LIMITS_VIOLATED + code ?? AlkemioErrorStatus.ROLE_SET_POLICY_ROLE_LIMITS_VIOLATED ); } } diff --git a/src/core/validation/handlers/base/base.handler.ts b/src/core/validation/handlers/base/base.handler.ts index 47ec309c2d..2020e055d0 100644 --- a/src/core/validation/handlers/base/base.handler.ts +++ b/src/core/validation/handlers/base/base.handler.ts @@ -70,11 +70,11 @@ import { UpdateSpaceSettingsInput } from '@domain/space/space/dto/space.dto.upda import { CreateAccountInput } from '@domain/space/account/dto'; import { UpdateCommunityGuidelinesInput } from '@domain/community/community-guidelines/dto/community.guidelines.dto.update'; import { ForumCreateDiscussionInput } from '@platform/forum/dto/forum.dto.create.discussion'; -import { CommunityRoleApplyInput } from '@domain/community/community-role/dto/community.role.dto.apply'; -import { CreateInvitationForContributorsOnCommunityInput } from '@domain/community/community-role/dto/community.role.dto.invite.contributor'; -import { CreatePlatformInvitationOnCommunityInput } from '@domain/community/community-role/dto/community.role.dto.platform.invitation.community'; import { CreateCollaborationOnSpaceInput } from '@domain/space/space/dto/space.dto.create.collaboration'; import { UpdateInnovationFlowEntityInput } from '@domain/collaboration/innovation-flow/dto/innovation.flow.dto.update.entity'; +import { CommunityRoleApplyInput } from '@domain/access/role-set/dto/role.set.dto.apply'; +import { CreateInvitationForContributorsOnCommunityInput } from '@domain/access/role-set/dto/role.set.dto.invite.contributor'; +import { CreatePlatformInvitationOnCommunityInput } from '@domain/access/role-set/dto/role.set.dto.platform.invitation.community'; export class BaseHandler extends AbstractHandler { public async handle( diff --git a/src/domain/community/community-role/dto/community.role.dto.apply.ts b/src/domain/access/role-set/dto/role.set.dto.apply.ts similarity index 100% rename from src/domain/community/community-role/dto/community.role.dto.apply.ts rename to src/domain/access/role-set/dto/role.set.dto.apply.ts diff --git a/src/domain/community/community-role/dto/community.role.dto.invite.contributor.ts b/src/domain/access/role-set/dto/role.set.dto.invite.contributor.ts similarity index 100% rename from src/domain/community/community-role/dto/community.role.dto.invite.contributor.ts rename to src/domain/access/role-set/dto/role.set.dto.invite.contributor.ts diff --git a/src/domain/community/community-role/dto/community.role.dto.join.ts b/src/domain/access/role-set/dto/role.set.dto.join.ts similarity index 100% rename from src/domain/community/community-role/dto/community.role.dto.join.ts rename to src/domain/access/role-set/dto/role.set.dto.join.ts diff --git a/src/domain/community/community-role/dto/community.role.dto.platform.invitation.community.ts b/src/domain/access/role-set/dto/role.set.dto.platform.invitation.community.ts similarity index 100% rename from src/domain/community/community-role/dto/community.role.dto.platform.invitation.community.ts rename to src/domain/access/role-set/dto/role.set.dto.platform.invitation.community.ts diff --git a/src/domain/community/community-role/dto/community.role.dto.role.assign.organization.ts b/src/domain/access/role-set/dto/role.set.dto.role.assign.organization.ts similarity index 100% rename from src/domain/community/community-role/dto/community.role.dto.role.assign.organization.ts rename to src/domain/access/role-set/dto/role.set.dto.role.assign.organization.ts diff --git a/src/domain/community/community-role/dto/community.role.dto.role.assign.user.ts b/src/domain/access/role-set/dto/role.set.dto.role.assign.user.ts similarity index 100% rename from src/domain/community/community-role/dto/community.role.dto.role.assign.user.ts rename to src/domain/access/role-set/dto/role.set.dto.role.assign.user.ts diff --git a/src/domain/community/community-role/dto/community.role.dto.role.assign.virtual.ts b/src/domain/access/role-set/dto/role.set.dto.role.assign.virtual.ts similarity index 100% rename from src/domain/community/community-role/dto/community.role.dto.role.assign.virtual.ts rename to src/domain/access/role-set/dto/role.set.dto.role.assign.virtual.ts diff --git a/src/domain/community/community-role/dto/community.role.dto.role.remove.organization.ts b/src/domain/access/role-set/dto/role.set.dto.role.remove.organization.ts similarity index 100% rename from src/domain/community/community-role/dto/community.role.dto.role.remove.organization.ts rename to src/domain/access/role-set/dto/role.set.dto.role.remove.organization.ts diff --git a/src/domain/community/community-role/dto/community.role.dto.role.remove.user.ts b/src/domain/access/role-set/dto/role.set.dto.role.remove.user.ts similarity index 100% rename from src/domain/community/community-role/dto/community.role.dto.role.remove.user.ts rename to src/domain/access/role-set/dto/role.set.dto.role.remove.user.ts diff --git a/src/domain/community/community-role/dto/community.role.dto.role.remove.virtual.ts b/src/domain/access/role-set/dto/role.set.dto.role.remove.virtual.ts similarity index 100% rename from src/domain/community/community-role/dto/community.role.dto.role.remove.virtual.ts rename to src/domain/access/role-set/dto/role.set.dto.role.remove.virtual.ts diff --git a/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts b/src/domain/access/role-set/role.set.lifecycle.application.options.provider.ts similarity index 93% rename from src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts rename to src/domain/access/role-set/role.set.lifecycle.application.options.provider.ts index b8fffe80d4..f5b30dd3fb 100644 --- a/src/domain/community/community-role/community.role.lifecycle.application.options.provider.ts +++ b/src/domain/access/role-set/role.set.lifecycle.application.options.provider.ts @@ -13,15 +13,15 @@ import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { AuthorizationService } from '@core/authorization/authorization.service'; import { AuthorizationPolicy } from '@domain/common/authorization-policy'; import { CommunityRoleType } from '@common/enums/community.role'; -import { CommunityRoleService } from './community.role.service'; +import { RoleSetService } from './role.set.service'; @Injectable() -export class CommunityRoleApplicationLifecycleOptionsProvider { +export class RoleSetApplicationLifecycleOptionsProvider { constructor( private lifecycleService: LifecycleService, - private communityRoleService: CommunityRoleService, private authorizationService: AuthorizationService, private applicationService: ApplicationService, + private roleSetService: RoleSetService, @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService ) {} @@ -76,7 +76,7 @@ export class CommunityRoleApplicationLifecycleOptionsProvider { LogContext.COMMUNITY ); - await this.communityRoleService.assignUserToRole( + await this.roleSetService.assignUserToRole( roleSet, CommunityRoleType.MEMBER, userID, diff --git a/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts b/src/domain/access/role-set/role.set.lifecycle.invitation.options.provider.ts similarity index 94% rename from src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts rename to src/domain/access/role-set/role.set.lifecycle.invitation.options.provider.ts index 8607bb6f96..ead2b31f44 100644 --- a/src/domain/community/community-role/community.role.lifecycle.invitation.options.provider.ts +++ b/src/domain/access/role-set/role.set.lifecycle.invitation.options.provider.ts @@ -8,18 +8,18 @@ import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { AuthorizationService } from '@core/authorization/authorization.service'; import { AuthorizationPolicy } from '@domain/common/authorization-policy'; import { CommunityRoleType } from '@common/enums/community.role'; -import { CommunityRoleService } from './community.role.service'; import { InvitationEventInput } from '@domain/access/invitation/dto/invitation.dto.event'; import { IInvitation } from '@domain/access/invitation/invitation.interface'; import { InvitationService } from '@domain/access/invitation/invitation.service'; +import { RoleSetService } from './role.set.service'; @Injectable() -export class CommunityRoleInvitationLifecycleOptionsProvider { +export class RoleSetInvitationLifecycleOptionsProvider { constructor( private lifecycleService: LifecycleService, - private communityRoleService: CommunityRoleService, private authorizationService: AuthorizationService, private invitationService: InvitationService, + private roleSetService: RoleSetService, @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService ) {} @@ -110,7 +110,7 @@ export class CommunityRoleInvitationLifecycleOptionsProvider { LogContext.COMMUNITY ); } - await this.communityRoleService.assignContributorToRole( + await this.roleSetService.assignContributorToRole( roleSet.parentRoleSet, CommunityRoleType.MEMBER, contributorID, @@ -119,7 +119,7 @@ export class CommunityRoleInvitationLifecycleOptionsProvider { true ); } - await this.communityRoleService.assignContributorToRole( + await this.roleSetService.assignContributorToRole( roleSet, CommunityRoleType.MEMBER, contributorID, diff --git a/src/domain/access/role-set/role.set.resolver.fields.ts b/src/domain/access/role-set/role.set.resolver.fields.ts index efd24181f2..c27375cf8a 100644 --- a/src/domain/access/role-set/role.set.resolver.fields.ts +++ b/src/domain/access/role-set/role.set.resolver.fields.ts @@ -1,15 +1,223 @@ import { GraphqlGuard } from '@core/authorization'; import { UseGuards } from '@nestjs/common'; -import { Parent, ResolveField, Resolver } from '@nestjs/graphql'; -import { Profiling } from '@src/common/decorators'; +import { Args, Float, Parent, ResolveField, Resolver } from '@nestjs/graphql'; +import { AuthorizationAgentPrivilege, Profiling } from '@src/common/decorators'; import { RoleSetService } from './role.set.service'; import { IForm } from '@domain/common/form/form.interface'; import { IRoleSet } from './role.set.interface'; import { RoleSet } from './role.set.entity'; +import { IApplication } from '../application/application.interface'; +import { AuthorizationPrivilege } from '@common/enums/authorization.privilege'; +import { UserFilterInput } from '@core/filtering/input-types/user.filter.input'; +import { PaginationArgs } from '@core/pagination/pagination.args'; +import { PaginatedUsers } from '@core/pagination/paginated.user'; +import { PaginationInputOutOfBoundException } from '@common/exceptions/pagination/pagination.input.out.of.bounds.exception'; +import { IUser } from '@domain/community/user/user.interface'; +import { CommunityRoleType } from '@common/enums/community.role'; +import { UserService } from '@domain/community/user/user.service'; +import { IOrganization } from '@domain/community/organization/organization.interface'; +import { IVirtualContributor } from '@domain/community/virtual-contributor/virtual.contributor.interface'; +import { IInvitation } from '../invitation/invitation.interface'; +import { IPlatformInvitation } from '@platform/invitation/platform.invitation.interface'; @Resolver(() => IRoleSet) export class RoleSetResolverFields { - constructor(private roleSetService: RoleSetService) {} + constructor( + private roleSetService: RoleSetService, + private userService: UserService + ) {} + + @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) + @UseGuards(GraphqlGuard) + @ResolveField('memberUsers', () => [IUser], { + nullable: false, + description: 'All users that are contributing to this Community.', + }) + @Profiling.api + async memberUsers( + @Parent() roleSet: IRoleSet, + @Args({ + name: 'limit', + type: () => Float, + description: + 'The positive number of member users to return; if omitted returns all member users.', + nullable: true, + }) + limit?: number + ) { + if (limit && limit < 0) { + throw new PaginationInputOutOfBoundException( + `Limit expects a positive amount: ${limit} provided instead` + ); + } + + return await this.roleSetService.getUsersWithRole( + roleSet, + CommunityRoleType.MEMBER, + limit + ); + } + + @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) + @UseGuards(GraphqlGuard) + @ResolveField('availableMemberUsers', () => PaginatedUsers, { + nullable: false, + description: 'All available users that are potential Community members.', + }) + @Profiling.api + async availableMemberUsers( + @Parent() roleSet: IRoleSet, + @Args({ nullable: true }) pagination: PaginationArgs, + @Args('filter', { nullable: true }) filter?: UserFilterInput + ) { + const memberRoleCredentials = + this.roleSetService.getCredentialDefinitionForRole( + roleSet, + CommunityRoleType.MEMBER + ); + + const parentCommunity = await this.roleSetService.getParentRoleSet(roleSet); + + const parentCommunityMemberCredentials = parentCommunity + ? this.roleSetService.getCredentialDefinitionForRole( + parentCommunity, + CommunityRoleType.MEMBER + ) + : undefined; + + const roleSetMemberCredentials = { + member: memberRoleCredentials, + parentCommunityMember: parentCommunityMemberCredentials, + }; + + return this.userService.getPaginatedAvailableMemberUsers( + roleSetMemberCredentials, + pagination, + filter + ); + } + + @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) + @UseGuards(GraphqlGuard) + @ResolveField('usersInRole', () => [IUser], { + nullable: false, + description: 'All users that have the specified Role in this Community.', + }) + @Profiling.api + async usersInRole( + @Parent() roleSet: IRoleSet, + @Args('role', { type: () => CommunityRoleType, nullable: false }) + role: CommunityRoleType + ) { + return await this.roleSetService.getUsersWithRole(roleSet, role); + } + + @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) + @UseGuards(GraphqlGuard) + @ResolveField('organizationsInRole', () => [IOrganization], { + nullable: false, + description: + 'All Organizations that have the specified Role in this Community.', + }) + @Profiling.api + async organizationsInRole( + @Parent() roleSet: IRoleSet, + @Args('role', { type: () => CommunityRoleType, nullable: false }) + role: CommunityRoleType + ): Promise { + return await this.roleSetService.getOrganizationsWithRole(roleSet, role); + } + + @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) + @UseGuards(GraphqlGuard) + @ResolveField('virtualContributorsInRole', () => [IVirtualContributor], { + nullable: false, + description: 'All virtuals that have the specified Role in this Community.', + }) + @Profiling.api + async virtualsInRole( + @Parent() roleSet: IRoleSet, + @Args('role', { type: () => CommunityRoleType, nullable: false }) + role: CommunityRoleType + ) { + return await this.roleSetService.getVirtualContributorsWithRole( + roleSet, + role + ); + } + + @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) + @UseGuards(GraphqlGuard) + @ResolveField('availableLeadUsers', () => PaginatedUsers, { + nullable: false, + description: + 'All member users excluding the current lead users in this Community.', + }) + @Profiling.api + async availableLeadUsers( + @Parent() roleSet: IRoleSet, + @Args({ nullable: true }) pagination: PaginationArgs, + @Args('filter', { nullable: true }) filter?: UserFilterInput + ) { + const memberRoleCredentials = + this.roleSetService.getCredentialDefinitionForRole( + roleSet, + CommunityRoleType.MEMBER + ); + + const leadRoleCredential = + this.roleSetService.getCredentialDefinitionForRole( + roleSet, + CommunityRoleType.LEAD + ); + + const credentialCriteria = { + member: memberRoleCredentials, + lead: leadRoleCredential, + }; + + return this.userService.getPaginatedAvailableLeadUsers( + credentialCriteria, + pagination, + filter + ); + } + + @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) + @UseGuards(GraphqlGuard) + @ResolveField('invitations', () => [IInvitation], { + nullable: false, + description: 'Invitations for this roleSet.', + }) + @Profiling.api + async inivitations(@Parent() roleSet: IRoleSet): Promise { + return await this.roleSetService.getInvitations(roleSet); + } + + @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) + @UseGuards(GraphqlGuard) + @ResolveField('platformInvitations', () => [IPlatformInvitation], { + nullable: false, + description: + 'Invitations to join this Community for users not yet on the Alkemio platform.', + }) + @Profiling.api + async platformInvitations( + @Parent() roleSet: IRoleSet + ): Promise { + return await this.roleSetService.getPlatformInvitations(roleSet); + } + + @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) + @UseGuards(GraphqlGuard) + @ResolveField('applications', () => [IApplication], { + nullable: false, + description: 'Applications available for this roleSet.', + }) + async applications(@Parent() roleSet: IRoleSet) { + const apps = await this.roleSetService.getApplications(roleSet); + return apps || []; + } @UseGuards(GraphqlGuard) @ResolveField('applicationForm', () => IForm, { diff --git a/src/domain/access/role-set/role.set.resolver.mutations.ts b/src/domain/access/role-set/role.set.resolver.mutations.ts index 19b5595e6c..54c6775886 100644 --- a/src/domain/access/role-set/role.set.resolver.mutations.ts +++ b/src/domain/access/role-set/role.set.resolver.mutations.ts @@ -4,18 +4,728 @@ import { RoleSetService } from './role.set.service'; import { CurrentUser, Profiling } from '@src/common/decorators'; import { GraphqlGuard } from '@core/authorization'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; -import { AuthorizationPrivilege } from '@common/enums'; +import { AuthorizationPrivilege, LogContext } from '@common/enums'; import { AuthorizationService } from '@core/authorization/authorization.service'; import { UpdateRoleSetApplicationFormInput } from './dto/role.set.dto.update.application.form'; import { IRoleSet } from './role.set.interface'; +import { IInvitation } from '../invitation/invitation.interface'; +import { InvitationEventInput } from '../invitation/dto/invitation.dto.event'; +import { ApplicationEventInput } from '../application/dto/application.dto.event'; +import { IApplication } from '../application/application.interface'; +import { CommunityRoleType } from '@common/enums/community.role'; +import { RoleSetMembershipException } from '@common/exceptions/role.set.membership.exception'; +import { CommunityJoinInput } from './dto/role.set.dto.join'; +import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; +import { NotificationInputPlatformInvitation } from '@services/adapters/notification-adapter/dto/notification.dto.input.platform.invitation'; +import { ApplicationService } from '../application/application.service'; +import { ApplicationAuthorizationService } from '../application/application.service.authorization'; +import { InvitationService } from '../invitation/invitation.service'; +import { InvitationAuthorizationService } from '../invitation/invitation.service.authorization'; +import { ContributorService } from '@domain/community/contributor/contributor.service'; +import { PlatformInvitationAuthorizationService } from '@platform/invitation/platform.invitation.service.authorization'; +import { VirtualContributorService } from '@domain/community/virtual-contributor/virtual.contributor.service'; +import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service'; +import { NotificationAdapter } from '@services/adapters/notification-adapter/notification.adapter'; +import { UserService } from '@domain/community/user/user.service'; +import { UserAuthorizationService } from '@domain/community/user/user.service.authorization'; +import { CommunityResolverService } from '@services/infrastructure/entity-resolver/community.resolver.service'; +import { RoleSetApplicationLifecycleOptionsProvider } from './role.set.lifecycle.application.options.provider'; +import { RoleSetInvitationLifecycleOptionsProvider } from './role.set.lifecycle.invitation.options.provider'; +import { PlatformInvitationService } from '@platform/invitation/platform.invitation.service'; +import { AssignCommunityRoleToUserInput } from './dto/role.set.dto.role.assign.user'; +import { IUser } from '@domain/community/user/user.interface'; +import { IOrganization } from '@domain/community/organization/organization.interface'; +import { AssignCommunityRoleToOrganizationInput } from './dto/role.set.dto.role.assign.organization'; +import { IVirtualContributor } from '@domain/community/virtual-contributor/virtual.contributor.interface'; +import { AssignCommunityRoleToVirtualInput } from './dto/role.set.dto.role.assign.virtual'; +import { RemoveCommunityRoleFromUserInput } from './dto/role.set.dto.role.remove.user'; +import { RemoveCommunityRoleFromOrganizationInput } from './dto/role.set.dto.role.remove.organization'; +import { RemoveCommunityRoleFromVirtualInput } from './dto/role.set.dto.role.remove.virtual'; +import { CommunityRoleApplyInput } from './dto/role.set.dto.apply'; +import { NotificationInputCommunityApplication } from '@services/adapters/notification-adapter/dto/notification.dto.input.community.application'; +import { CreateInvitationForContributorsOnCommunityInput } from './dto/role.set.dto.invite.contributor'; +import { RoleSetInvitationException } from '@common/exceptions/role.set.invitation.exception'; +import { IContributor } from '@domain/community/contributor/contributor.interface'; +import { EntityNotInitializedException } from '@common/exceptions/entity.not.initialized.exception'; +import { CreateInvitationInput } from '../invitation/dto/invitation.dto.create'; +import { VirtualContributor } from '@domain/community/virtual-contributor/virtual.contributor.entity'; +import { NotificationInputCommunityInvitationVirtualContributor } from '@services/adapters/notification-adapter/dto/notification.dto.input.community.invitation.vc'; +import { IPlatformInvitation } from '@platform/invitation/platform.invitation.interface'; +import { CreatePlatformInvitationOnCommunityInput } from './dto/role.set.dto.platform.invitation.community'; +import { NotificationInputCommunityInvitation } from '@services/adapters/notification-adapter/dto/notification.dto.input.community.invitation'; +import { RoleSetAuthorizationService } from './role.set.service.authorization'; @Resolver() export class RoleSetResolverMutations { constructor( private authorizationService: AuthorizationService, - private roleSetService: RoleSetService + private roleSetService: RoleSetService, + private roleSetAuthorizationService: RoleSetAuthorizationService, + private authorizationPolicyService: AuthorizationPolicyService, + private notificationAdapter: NotificationAdapter, + private userService: UserService, + private userAuthorizationService: UserAuthorizationService, + private virtualContributorService: VirtualContributorService, + private communityResolverService: CommunityResolverService, + private roleSetLifecycleApplicationOptionsProvider: RoleSetApplicationLifecycleOptionsProvider, + private roleSetLifecycleInvitationOptionsProvider: RoleSetInvitationLifecycleOptionsProvider, + private applicationService: ApplicationService, + private applicationAuthorizationService: ApplicationAuthorizationService, + private invitationService: InvitationService, + private invitationAuthorizationService: InvitationAuthorizationService, + private contributorService: ContributorService, + private platformInvitationAuthorizationService: PlatformInvitationAuthorizationService, + private platformInvitationService: PlatformInvitationService ) {} + @UseGuards(GraphqlGuard) + @Mutation(() => IUser, { + description: 'Assigns a User to a role in the specified Community.', + }) + @Profiling.api + async assignCommunityRoleToUser( + @CurrentUser() agentInfo: AgentInfo, + @Args('roleData') roleData: AssignCommunityRoleToUserInput + ): Promise { + const roleSet = await this.roleSetService.getRoleSetOrFail( + roleData.roleSetID + ); + + let requiredPrivilege = AuthorizationPrivilege.GRANT; + if (roleData.role === CommunityRoleType.MEMBER) { + requiredPrivilege = AuthorizationPrivilege.COMMUNITY_ADD_MEMBER; + } + + this.authorizationService.grantAccessOrFail( + agentInfo, + roleSet.authorization, + requiredPrivilege, + `assign user community role: ${roleSet.id}` + ); + + await this.roleSetService.assignUserToRole( + roleSet, + roleData.role, + roleData.userID, + agentInfo, + true + ); + + // reset the user authorization policy so that their profile is visible to other community members + const user = await this.userService.getUserOrFail(roleData.userID); + const authorizations = + await this.userAuthorizationService.applyAuthorizationPolicy(user); + await this.authorizationPolicyService.saveAll(authorizations); + return await this.userService.getUserOrFail(roleData.userID); + } + + @UseGuards(GraphqlGuard) + @Mutation(() => IOrganization, { + description: 'Assigns an Organization a Role in the specified Community.', + }) + @Profiling.api + async assignCommunityRoleToOrganization( + @CurrentUser() agentInfo: AgentInfo, + @Args('roleData') + roleData: AssignCommunityRoleToOrganizationInput + ): Promise { + const roleSet = await this.roleSetService.getRoleSetOrFail( + roleData.roleSetID + ); + + this.authorizationService.grantAccessOrFail( + agentInfo, + roleSet.authorization, + AuthorizationPrivilege.GRANT, + `assign organization community role: ${roleSet.id}` + ); + return await this.roleSetService.assignOrganizationToRole( + roleSet, + roleData.role, + roleData.organizationID + ); + } + + @UseGuards(GraphqlGuard) + @Mutation(() => IVirtualContributor, { + description: + 'Assigns a Virtual Contributor to a role in the specified Community.', + }) + @Profiling.api + async assignCommunityRoleToVirtual( + @CurrentUser() agentInfo: AgentInfo, + @Args('roleData') roleData: AssignCommunityRoleToVirtualInput + ): Promise { + const roleSet = await this.roleSetService.getRoleSetOrFail( + roleData.roleSetID + ); + + let requiredPrivilege = AuthorizationPrivilege.GRANT; + if (roleData.role === CommunityRoleType.MEMBER) { + const sameAccount = + await this.roleSetService.isCommunityAccountMatchingVcAccount( + roleSet.id, + roleData.virtualContributorID + ); + if (sameAccount) { + requiredPrivilege = + AuthorizationPrivilege.COMMUNITY_ADD_MEMBER_VC_FROM_ACCOUNT; + } else { + requiredPrivilege = AuthorizationPrivilege.COMMUNITY_ADD_MEMBER; + } + } + + this.authorizationService.grantAccessOrFail( + agentInfo, + roleSet.authorization, + requiredPrivilege, + `assign virtual community role: ${roleSet.id}` + ); + + // Also require ACCESS_VIRTUAL_CONTRIBUTORS to assign a virtual contributor + this.authorizationService.grantAccessOrFail( + agentInfo, + roleSet.authorization, + AuthorizationPrivilege.ACCESS_VIRTUAL_CONTRIBUTOR, + `assign virtual community role VC privilege: ${roleSet.id}` + ); + + await this.roleSetService.assignVirtualToRole( + roleSet, + roleData.role, + roleData.virtualContributorID, + agentInfo, + true + ); + + return await this.virtualContributorService.getVirtualContributorOrFail( + roleData.virtualContributorID + ); + } + + @UseGuards(GraphqlGuard) + @Mutation(() => IUser, { + description: 'Removes a User from a Role in the specified Community.', + }) + @Profiling.api + async removeCommunityRoleFromUser( + @CurrentUser() agentInfo: AgentInfo, + @Args('roleData') roleData: RemoveCommunityRoleFromUserInput + ): Promise { + const roleSet = await this.roleSetService.getRoleSetOrFail( + roleData.roleSetID + ); + + // Extend the authorization policy with a credential rule to assign the GRANT privilege + // to the user specified in the incoming mutation. Then if it is the same user as is logged + // in then the user will have the GRANT privilege + so can carry out the mutation + const community = + await this.communityResolverService.getCommunityForRoleSet(roleSet.id); + + const extendedAuthorization = + this.roleSetAuthorizationService.extendAuthorizationPolicyForSelfRemoval( + community, + roleData.userID + ); + + await this.authorizationService.grantAccessOrFail( + agentInfo, + extendedAuthorization, + AuthorizationPrivilege.GRANT, + `remove user from community role: ${roleSet.id}` + ); + + await this.roleSetService.removeUserFromRole( + roleSet, + roleData.role, + roleData.userID + ); + // reset the user authorization policy so that their profile is not visible + // to other community members + const user = await this.userService.getUserOrFail(roleData.userID); + const authorizations = + await this.userAuthorizationService.applyAuthorizationPolicy(user); + await this.authorizationPolicyService.saveAll(authorizations); + return await this.userService.getUserOrFail(roleData.userID); + } + + @UseGuards(GraphqlGuard) + @Mutation(() => IOrganization, { + description: + 'Removes an Organization from a Role in the specified Community.', + }) + @Profiling.api + async removeCommunityRoleFromOrganization( + @CurrentUser() agentInfo: AgentInfo, + @Args('roleData') roleData: RemoveCommunityRoleFromOrganizationInput + ): Promise { + const roleSet = await this.roleSetService.getRoleSetOrFail( + roleData.roleSetID + ); + await this.authorizationService.grantAccessOrFail( + agentInfo, + roleSet.authorization, + AuthorizationPrivilege.GRANT, + `remove community role organization: ${roleSet.id}` + ); + + return await this.roleSetService.removeOrganizationFromRole( + roleSet, + roleData.role, + roleData.organizationID + ); + } + + @UseGuards(GraphqlGuard) + @Mutation(() => IVirtualContributor, { + description: 'Removes a Virtual from a Role in the specified Community.', + }) + @Profiling.api + async removeCommunityRoleFromVirtual( + @CurrentUser() agentInfo: AgentInfo, + @Args('roleData') roleData: RemoveCommunityRoleFromVirtualInput + ): Promise { + const roleSet = await this.roleSetService.getRoleSetOrFail( + roleData.roleSetID + ); + + // Extend the authorization policy with a credential rule to assign the GRANT privilege + // to the user with rights around the incoming virtual being removed. + //. Then if it is the user that is logged in then the user will have the GRANT privilege + so can carry out the mutation + const extendedAuthorization = + await this.roleSetAuthorizationService.extendAuthorizationPolicyForVirtualContributorRemoval( + roleSet, + roleData.virtualContributorID + ); + + await this.authorizationService.grantAccessOrFail( + agentInfo, + extendedAuthorization, + AuthorizationPrivilege.GRANT, + `remove virtual from community role: ${roleSet.id}` + ); + + await this.roleSetService.removeVirtualFromRole( + roleSet, + roleData.role, + roleData.virtualContributorID + ); + + return await this.virtualContributorService.getVirtualContributorOrFail( + roleData.virtualContributorID + ); + } + + @UseGuards(GraphqlGuard) + @Mutation(() => IApplication, { + description: 'Apply to join the specified Community as a member.', + }) + @Profiling.api + async applyForCommunityMembership( + @CurrentUser() agentInfo: AgentInfo, + @Args('applicationData') applicationData: CommunityRoleApplyInput + ): Promise { + const roleSet = await this.roleSetService.getRoleSetOrFail( + applicationData.roleSetID, + { + relations: { + parentRoleSet: true, + }, + } + ); + + await this.authorizationService.grantAccessOrFail( + agentInfo, + roleSet.authorization, + AuthorizationPrivilege.COMMUNITY_APPLY, + `create application community: ${roleSet.id}` + ); + + if (roleSet.parentRoleSet) { + const { agent } = await this.userService.getUserAndAgent( + agentInfo.userID + ); + const userIsMemberInParent = await this.roleSetService.isInRole( + agent, + roleSet.parentRoleSet, + CommunityRoleType.MEMBER + ); + if (!userIsMemberInParent) { + throw new RoleSetMembershipException( + `Unable to apply for Community (${roleSet.id}): user is not a member of the parent Community`, + LogContext.COMMUNITY + ); + } + } + + let application = await this.roleSetService.createApplication({ + roleSetID: roleSet.id, + questions: applicationData.questions, + userID: agentInfo.userID, + }); + + application = await this.applicationService.save(application); + + const community = + await this.communityResolverService.getCommunityForRoleSet(roleSet.id); + + const authorization = + await this.applicationAuthorizationService.applyAuthorizationPolicy( + application, + roleSet.authorization + ); + await this.authorizationPolicyService.save(authorization); + + // Send the notification + const notificationInput: NotificationInputCommunityApplication = { + triggeredBy: agentInfo.userID, + community, + }; + await this.notificationAdapter.applicationCreated(notificationInput); + + return application; + } + + @UseGuards(GraphqlGuard) + @Mutation(() => [IInvitation], { + description: + 'Invite an existing Contriburor to join the specified Community as a member.', + }) + @Profiling.api + async inviteContributorsForCommunityMembership( + @CurrentUser() agentInfo: AgentInfo, + @Args('invitationData') + invitationData: CreateInvitationForContributorsOnCommunityInput + ): Promise { + const roleSet = await this.roleSetService.getRoleSetOrFail( + invitationData.roleSetID, + { + relations: { + parentRoleSet: { + authorization: true, + }, + }, + } + ); + if (invitationData.invitedContributors.length === 0) { + throw new RoleSetInvitationException( + `No contributors were provided to invite: ${roleSet.id}`, + LogContext.COMMUNITY + ); + } + + await this.authorizationService.grantAccessOrFail( + agentInfo, + roleSet.authorization, + AuthorizationPrivilege.COMMUNITY_INVITE, + `create invitation community: ${roleSet.id}` + ); + + const contributors: IContributor[] = []; + for (const contributorID of invitationData.invitedContributors) { + const contributor = + await this.contributorService.getContributorByUuidOrFail( + contributorID, + { + relations: { + agent: true, + }, + } + ); + contributors.push(contributor); + } + + // Logic is that the ability to invite to a subspace requires the ability to invite to the + // parent community if the user is not a member there + if (roleSet.parentRoleSet) { + const parentCommunityAuthorization = roleSet.parentRoleSet.authorization; + const canInviteToParent = this.authorizationService.isAccessGranted( + agentInfo, + parentCommunityAuthorization, + AuthorizationPrivilege.COMMUNITY_INVITE + ); + + // Need to see if also can invite to the parent community if any of the users are not members there + for (const contributor of contributors) { + if (!contributor.agent) { + throw new EntityNotInitializedException( + `Unable to load agent on contributor: ${contributor.id}`, + LogContext.COMMUNITY + ); + } + const isMember = await this.roleSetService.isMember( + contributor.agent, + roleSet.parentRoleSet + ); + if (!isMember && !canInviteToParent) { + throw new RoleSetInvitationException( + `Contributor is not a member of the parent community (${roleSet.parentRoleSet.id}) and the current user does not have the privilege to invite to the parent community`, + LogContext.COMMUNITY + ); + } else { + invitationData.invitedToParent = true; + } + } + } else { + invitationData.invitedToParent = false; + } + + return Promise.all( + contributors.map(async invitedContributor => { + return await this.inviteSingleExistingContributor( + roleSet, + invitedContributor, + agentInfo, + invitationData.invitedToParent, + invitationData.welcomeMessage + ); + }) + ); + } + + private async inviteSingleExistingContributor( + roleSet: IRoleSet, + invitedContributor: IContributor, + agentInfo: AgentInfo, + invitedToParent: boolean, + welcomeMessage?: string + ): Promise { + const input: CreateInvitationInput = { + roleSetID: roleSet.id, + invitedContributor: invitedContributor.id, + createdBy: agentInfo.userID, + invitedToParent, + welcomeMessage, + }; + + let invitation = + await this.roleSetService.createInvitationExistingContributor(input); + + invitation = await this.invitationService.save(invitation); + + const community = + await this.communityResolverService.getCommunityForRoleSet(roleSet.id); + + const authorization = + await this.invitationAuthorizationService.applyAuthorizationPolicy( + invitation, + roleSet.authorization + ); + await this.authorizationPolicyService.save(authorization); + + if (invitedContributor instanceof VirtualContributor) { + const accountProvider = + await this.virtualContributorService.getProvider(invitedContributor); + const notificationInput: NotificationInputCommunityInvitationVirtualContributor = + { + triggeredBy: agentInfo.userID, + community, + invitedContributorID: invitedContributor.id, + accountHost: accountProvider, + welcomeMessage, + }; + + await this.notificationAdapter.invitationVirtualContributorCreated( + notificationInput + ); + } else { + // Send the notification + const notificationInput: NotificationInputCommunityInvitation = { + triggeredBy: agentInfo.userID, + community, + invitedContributorID: invitedContributor.id, + welcomeMessage, + }; + + await this.notificationAdapter.invitationCreated(notificationInput); + } + + return invitation; + } + + @UseGuards(GraphqlGuard) + @Mutation(() => IPlatformInvitation, { + description: + 'Invite a User to join the platform and the specified Community as a member.', + }) + @Profiling.api + async inviteUserToPlatformAndCommunity( + @CurrentUser() agentInfo: AgentInfo, + @Args('invitationData') + invitationData: CreatePlatformInvitationOnCommunityInput + ): Promise { + const roleSet = await this.roleSetService.getRoleSetOrFail( + invitationData.roleSetID, + { + relations: { + parentRoleSet: { + authorization: true, + }, + }, + } + ); + + this.authorizationService.grantAccessOrFail( + agentInfo, + roleSet.authorization, + AuthorizationPrivilege.COMMUNITY_INVITE, + `create invitation external community: ${roleSet.id}` + ); + + const existingUser = await this.userService.getUserByEmail( + invitationData.email, + { + relations: { + agent: true, + }, + } + ); + + if (existingUser) { + throw new RoleSetInvitationException( + `User already has a profile (${existingUser.email})`, + LogContext.COMMUNITY + ); + } + + // Logic is that the ability to invite to a subspace requires the ability to invite to the + // parent community if the user is not a member there + if (roleSet.parentRoleSet) { + const parentCommunityAuthorization = roleSet.parentRoleSet.authorization; + const canInviteToParent = this.authorizationService.isAccessGranted( + agentInfo, + parentCommunityAuthorization, + AuthorizationPrivilege.COMMUNITY_INVITE + ); + + // Not an existing user + if (!canInviteToParent) { + throw new RoleSetInvitationException( + `New external user (${invitationData.email}) and the current user (${agentInfo.email}) does not have the privilege to invite to the parent community: ${roleSet.parentRoleSet.id}`, + LogContext.COMMUNITY + ); + } else { + invitationData.communityInvitedToParent = true; + } + } + + const community = + await this.communityResolverService.getCommunityForRoleSet(roleSet.id); + + let platformInvitation = await this.roleSetService.createPlatformInvitation( + invitationData, + agentInfo + ); + + platformInvitation = + await this.platformInvitationService.save(platformInvitation); + const authorizations = + await this.platformInvitationAuthorizationService.applyAuthorizationPolicy( + platformInvitation, + roleSet.authorization + ); + await this.authorizationPolicyService.save(authorizations); + + const notificationInput: NotificationInputPlatformInvitation = { + triggeredBy: agentInfo.userID, + community, + invitedUser: invitationData.email, + welcomeMessage: invitationData.welcomeMessage, + }; + await this.notificationAdapter.platformInvitationCreated(notificationInput); + return platformInvitation; + } + + @UseGuards(GraphqlGuard) + @Mutation(() => IRoleSet, { + description: + 'Join the specified Community as a member, without going through an approval process.', + }) + @Profiling.api + async joinRoleSet( + @CurrentUser() agentInfo: AgentInfo, + @Args('joinCommunityData') joiningData: CommunityJoinInput + ): Promise { + const roleSet = await this.roleSetService.getRoleSetOrFail( + joiningData.roleSetID + ); + const membershipStatus = await this.roleSetService.getMembershipStatus( + agentInfo, + roleSet + ); + if (membershipStatus === CommunityMembershipStatus.INVITATION_PENDING) { + throw new RoleSetMembershipException( + `Unable to join Community (${roleSet.id}): invitation to join is pending.`, + LogContext.COMMUNITY + ); + } + + await this.authorizationService.grantAccessOrFail( + agentInfo, + roleSet.authorization, + AuthorizationPrivilege.COMMUNITY_JOIN, + `join community: ${roleSet.id}` + ); + + await this.roleSetService.assignUserToRole( + roleSet, + CommunityRoleType.MEMBER, + agentInfo.userID, + agentInfo, + true + ); + + return roleSet; + } + + @UseGuards(GraphqlGuard) + @Mutation(() => IApplication, { + description: 'Trigger an event on the Application.', + }) + async eventOnApplication( + @Args('applicationEventData') + applicationEventData: ApplicationEventInput, + @CurrentUser() agentInfo: AgentInfo + ): Promise { + const application = await this.applicationService.getApplicationOrFail( + applicationEventData.applicationID + ); + await this.authorizationService.grantAccessOrFail( + agentInfo, + application.authorization, + AuthorizationPrivilege.UPDATE, + `event on application: ${application.id}` + ); + return await this.roleSetLifecycleApplicationOptionsProvider.eventOnApplication( + applicationEventData, + agentInfo + ); + } + + @UseGuards(GraphqlGuard) + @Mutation(() => IInvitation, { + description: 'Trigger an event on the Invitation.', + }) + async eventOnCommunityInvitation( + @Args('invitationEventData') + invitationEventData: InvitationEventInput, + @CurrentUser() agentInfo: AgentInfo + ): Promise { + const invitation = await this.invitationService.getInvitationOrFail( + invitationEventData.invitationID + ); + await this.authorizationService.grantAccessOrFail( + agentInfo, + invitation.authorization, + AuthorizationPrivilege.UPDATE, + `event on invitation: ${invitation.id}` + ); + return await this.roleSetLifecycleInvitationOptionsProvider.eventOnInvitation( + invitationEventData, + agentInfo + ); + } + @UseGuards(GraphqlGuard) @Mutation(() => IRoleSet, { description: 'Update the Application Form used by this RoleSet.', diff --git a/src/domain/community/community-role/community.role.service.events.ts b/src/domain/access/role-set/role.set.service.events.ts similarity index 85% rename from src/domain/community/community-role/community.role.service.events.ts rename to src/domain/access/role-set/role.set.service.events.ts index 352734ba06..d499291518 100644 --- a/src/domain/community/community-role/community.role.service.events.ts +++ b/src/domain/access/role-set/role.set.service.events.ts @@ -6,12 +6,12 @@ import { NotificationInputCommunityNewMember } from '@services/adapters/notifica import { ActivityInputMemberJoined } from '@services/adapters/activity-adapter/dto/activity.dto.input.member.joined'; import { ActivityAdapter } from '@services/adapters/activity-adapter/activity.adapter'; import { SpaceType } from '@common/enums/space.type'; -import { IContributor } from '../contributor/contributor.interface'; import { CommunityResolverService } from '@services/infrastructure/entity-resolver/community.resolver.service'; -import { ICommunity } from '../community/community.interface'; +import { IRoleSet } from './role.set.interface'; +import { IContributor } from '@domain/community/contributor/contributor.interface'; @Injectable() -export class CommunityRoleEventsService { +export class RoleSetEventsService { constructor( private contributionReporter: ContributionReporterService, private notificationAdapter: NotificationAdapter, @@ -20,37 +20,42 @@ export class CommunityRoleEventsService { ) {} public async registerCommunityNewMemberActivity( - community: ICommunity, + roleSet: IRoleSet, newContributor: IContributor, agentInfo: AgentInfo ) { + const community = + await this.communityResolverService.getCommunityForRoleSet(roleSet.id); + const activityLogInput: ActivityInputMemberJoined = { triggeredBy: agentInfo.userID, - community: community, + community, contributor: newContributor, }; await this.activityAdapter.memberJoined(activityLogInput); } public async processCommunityNewMemberEvents( - community: ICommunity, + roleSet: IRoleSet, levelZeroSpaceID: string, displayName: string, agentInfo: AgentInfo, newContributor: IContributor ) { + const community = + await this.communityResolverService.getCommunityForRoleSet(roleSet.id); // TODO: community just needs to know the level, not the type // Send the notification const notificationInput: NotificationInputCommunityNewMember = { contributorID: newContributor.id, triggeredBy: agentInfo.userID, - community: community, + community, }; await this.notificationAdapter.communityNewMember(notificationInput); // Record the contribution events const space = await this.communityResolverService.getSpaceForRoleSetOrFail( - community.id + roleSet.id ); switch (space.type) { case SpaceType.SPACE: diff --git a/src/domain/access/role-set/role.set.service.ts b/src/domain/access/role-set/role.set.service.ts index 24644d067d..d45005949a 100644 --- a/src/domain/access/role-set/role.set.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -1,8 +1,11 @@ import { Inject, Injectable, LoggerService } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { + RoleSetPolicyRoleLimitsException, EntityNotFoundException, + EntityNotInitializedException, RelationshipNotFoundException, + ValidationException, } from '@common/exceptions'; import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; import { FindOneOptions, Repository } from 'typeorm'; @@ -20,11 +23,37 @@ import { InvitationService } from '@domain/access/invitation/invitation.service' import { RoleSet } from './role.set.entity'; import { IRoleSet } from './role.set.interface'; import { RoleService } from '../role/role.service'; -import { CommunityRoleType } from '@common/enums/community.role'; import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; import { IRole } from '../role/role.interface'; import { AuthorizationCredential } from '@common/enums/authorization.credential'; import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; +import { AgentService } from '@domain/agent/agent/agent.service'; +import { AgentInfo } from '@core/authentication.agent.info/agent.info'; +import { UserService } from '@domain/community/user/user.service'; +import { IInvitation } from '../invitation/invitation.interface'; +import { IUser } from '@domain/community/user/user.interface'; +import { IVirtualContributor } from '@domain/community/virtual-contributor/virtual.contributor.interface'; +import { OrganizationService } from '@domain/community/organization/organization.service'; +import { VirtualContributorService } from '@domain/community/virtual-contributor/virtual.contributor.service'; +import { CommunityContributorType } from '@common/enums/community.contributor.type'; +import { IContributor } from '@domain/community/contributor/contributor.interface'; +import { CommunityRoleImplicit } from '@common/enums/community.role.implicit'; +import { CommunityResolverService } from '@services/infrastructure/entity-resolver/community.resolver.service'; +import { CommunityRoleType } from '@common/enums/community.role'; +import { IApplication } from '../application/application.interface'; +import { IOrganization } from '@domain/community/organization/organization.interface'; +import { IAgent } from '@domain/agent/agent/agent.interface'; +import { CommunityContributorsUpdateType } from '@common/enums/community.contributors.update.type'; +import { RoleSetMembershipException } from '@common/exceptions/role.set.membership.exception'; +import { CreateApplicationInput } from '../application/dto/application.dto.create'; +import { CreateInvitationInput } from '../invitation/dto/invitation.dto.create'; +import { CreatePlatformInvitationOnCommunityInput } from './dto/role.set.dto.platform.invitation.community'; +import { IPlatformInvitation } from '@platform/invitation/platform.invitation.interface'; +import { CreatePlatformInvitationInput } from '@platform/invitation/dto/platform.invitation.dto.create'; +import { ContributorService } from '@domain/community/contributor/contributor.service'; +import { RoleSetEventsService } from './role.set.service.events'; +import { AiServerAdapter } from '@services/adapters/ai-server-adapter/ai.server.adapter'; +import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; @Injectable() export class RoleSetService { @@ -35,6 +64,14 @@ export class RoleSetService { private platformInvitationService: PlatformInvitationService, private formService: FormService, private roleService: RoleService, + private agentService: AgentService, + private contributorService: ContributorService, + private userService: UserService, + private organizationService: OrganizationService, + private virtualContributorService: VirtualContributorService, + private communityResolverService: CommunityResolverService, + private roleSetEventsService: RoleSetEventsService, + private aiServerAdapter: AiServerAdapter, @InjectRepository(RoleSet) private roleSetRepository: Repository, @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService @@ -190,6 +227,985 @@ export class RoleSetService { return roleSet; } + public async removeAllCommunityRoles(roleSet: IRoleSet) { + // Remove all issued role credentials for contributors + for (const roleType of Object.values(CommunityRoleType)) { + const users = await this.getUsersWithRole(roleSet, roleType); + for (const user of users) { + await this.removeUserFromRole(roleSet, roleType, user.id, false); + } + + const organizations = await this.getOrganizationsWithRole( + roleSet, + roleType + ); + for (const organization of organizations) { + await this.removeOrganizationFromRole( + roleSet, + roleType, + organization.id, + false + ); + } + } + } + + async getCommunityRoles( + agentInfo: AgentInfo, + roleSet: IRoleSet + ): Promise { + const result: CommunityRoleType[] = []; + const agent = await this.agentService.getAgentOrFail(agentInfo.agentID); + const roles: CommunityRoleType[] = Object.values( + CommunityRoleType + ) as CommunityRoleType[]; + for (const role of roles) { + const hasAgentRole = await this.isInRole(agent, roleSet, role); + if (hasAgentRole) { + result.push(role); + } + } + + return result; + } + + public async findOpenApplication( + userID: string, + roleSetID: string + ): Promise { + const applications = await this.applicationService.findExistingApplications( + userID, + roleSetID + ); + for (const application of applications) { + // skip any finalized applications; only want to return pending applications + const isFinalized = await this.applicationService.isFinalizedApplication( + application.id + ); + if (isFinalized) continue; + return application; + } + return undefined; + } + + async getMembershipStatus( + agentInfo: AgentInfo, + roleSet: IRoleSet + ): Promise { + if (!agentInfo.agentID) { + return CommunityMembershipStatus.NOT_MEMBER; + } + const agent = await this.agentService.getAgentOrFail(agentInfo.agentID); + const isMember = await this.isMember(agent, roleSet); + if (isMember) return CommunityMembershipStatus.MEMBER; + + const openApplication = await this.findOpenApplication( + agentInfo.userID, + roleSet.id + ); + if (openApplication) return CommunityMembershipStatus.APPLICATION_PENDING; + + const openInvitation = await this.findOpenInvitation( + agentInfo.userID, + roleSet.id + ); + + if ( + openInvitation && + (await this.invitationService.canInvitationBeAccepted(openInvitation.id)) + ) { + return CommunityMembershipStatus.INVITATION_PENDING; + } + + return CommunityMembershipStatus.NOT_MEMBER; + } + + public async findOpenInvitation( + contributorID: string, + roleSetID: string + ): Promise { + const invitations = await this.invitationService.findExistingInvitations( + contributorID, + roleSetID + ); + for (const invitation of invitations) { + // skip any finalized applications; only want to return pending applications + const isFinalized = await this.invitationService.isFinalizedInvitation( + invitation.id + ); + if (isFinalized) continue; + return invitation; + } + return undefined; + } + + async getUsersWithRole( + roleSet: IRoleSet, + roleType: CommunityRoleType, + limit?: number + ): Promise { + const membershipCredential = this.getCredentialDefinitionForRole( + roleSet, + roleType + ); + return await this.userService.usersWithCredentials( + { + type: membershipCredential.type, + resourceID: membershipCredential.resourceID, + }, + limit + ); + } + + async getVirtualContributorsWithRole( + roleSet: IRoleSet, + roleType: CommunityRoleType + ): Promise { + const membershipCredential = this.getCredentialDefinitionForRole( + roleSet, + roleType + ); + return await this.virtualContributorService.virtualContributorsWithCredentials( + { + type: membershipCredential.type, + resourceID: membershipCredential.resourceID, + } + ); + } + + async getOrganizationsWithRole( + roleSet: IRoleSet, + roleType: CommunityRoleType + ): Promise { + const membershipCredential = this.getCredentialDefinitionForRole( + roleSet, + roleType + ); + return await this.organizationService.organizationsWithCredentials({ + type: membershipCredential.type, + resourceID: membershipCredential.resourceID, + }); + } + + async countContributorsPerRole( + roleSet: IRoleSet, + roleType: CommunityRoleType, + contributorType: CommunityContributorType + ): Promise { + const membershipCredential = this.getCredentialDefinitionForRole( + roleSet, + roleType + ); + + if (contributorType === CommunityContributorType.ORGANIZATION) { + return await this.organizationService.countOrganizationsWithCredentials({ + type: membershipCredential.type, + resourceID: membershipCredential.resourceID, + }); + } + + if (contributorType === CommunityContributorType.USER) { + return await this.userService.countUsersWithCredentials({ + type: membershipCredential.type, + resourceID: membershipCredential.resourceID, + }); + } + + return 0; + } + + getCredentialDefinitionForRole( + roleSet: IRoleSet, + role: CommunityRoleType + ): ICredentialDefinition { + const credential = this.getCredentialForRole(roleSet, role); + return credential; + } + + async assignContributorToRole( + roleSet: IRoleSet, + roleType: CommunityRoleType, + contributorID: string, + contributorType: CommunityContributorType, + agentInfo?: AgentInfo, + triggerNewMemberEvents = false + ): Promise { + switch (contributorType) { + case CommunityContributorType.USER: + return await this.assignUserToRole( + roleSet, + roleType, + contributorID, + agentInfo, + triggerNewMemberEvents + ); + case CommunityContributorType.ORGANIZATION: + return await this.assignOrganizationToRole( + roleSet, + roleType, + contributorID + ); + case CommunityContributorType.VIRTUAL: + return await this.assignVirtualToRole( + roleSet, + roleType, + contributorID, + agentInfo, + triggerNewMemberEvents + ); + default: + throw new EntityNotInitializedException( + `Invalid roleSet contributor type: ${contributorType}`, + LogContext.ROLES + ); + } + } + + async assignUserToRole( + roleSet: IRoleSet, + roleType: CommunityRoleType, + userID: string, + agentInfo?: AgentInfo, + triggerNewMemberEvents = false + ): Promise { + const { user, agent } = await this.userService.getUserAndAgent(userID); + const { isMember: hasMemberRoleInParent, parentRoleSet } = + await this.isMemberInParentRoleSet(agent, roleSet.id); + if (!hasMemberRoleInParent) { + throw new ValidationException( + `Unable to assign Agent (${agent.id}) to roleSet (${roleSet.id}): agent is not a member of parent roleSet ${parentRoleSet?.id}`, + LogContext.SPACES + ); + } + + const userAlreadyHasRole = await this.isInRole(agent, roleSet, roleType); + if (userAlreadyHasRole) { + return user; + } + + user.agent = await this.assignContributorAgentToRole( + roleSet, + roleType, + agent, + CommunityContributorType.USER + ); + if (roleType === CommunityRoleType.ADMIN && parentRoleSet) { + // also assign as subspace admin in parent roleSet if there is a parent roleSet + const credential = this.getCredentialForImplicitRole( + parentRoleSet, + CommunityRoleImplicit.SUBSPACE_ADMIN + ); + const alreadyHasSubspaceAdmin = + await this.agentService.hasValidCredential(agent.id, credential); + if (!alreadyHasSubspaceAdmin) { + await this.assignContributorToImplicitRole( + parentRoleSet, + agent, + CommunityRoleImplicit.SUBSPACE_ADMIN + ); + } + } + + await this.contributorAddedToRole( + user, + roleSet, + roleType, + agentInfo, + triggerNewMemberEvents + ); + + return user; + } + + private async contributorAddedToRole( + contributor: IContributor, + roleSet: IRoleSet, + role: CommunityRoleType, + agentInfo?: AgentInfo, + triggerNewMemberEvents = false + ) { + this.logger.verbose?.( + `Trigger new member events: ${triggerNewMemberEvents}`, + LogContext.ROLES + ); + if (role === CommunityRoleType.MEMBER) { + // TODO: FIX ME - need callback depending on Type of RoleSet + + // const community = + // await this.communityResolverService.getCommunityForRoleSet(roleSet.id); + // this.addMemberToCommunication(contributor, roleSet); + + if (agentInfo) { + await this.roleSetEventsService.registerCommunityNewMemberActivity( + roleSet, + contributor, + agentInfo + ); + + // if (triggerNewMemberEvents) { + // const levelZeroSpaceID = + // await this.getLevelZeroSpaceIdForCommunity(roleSet); + // const displayName = await this.getDisplayName(roleSet); + // await this.roleSetEventsService.processCommunityNewMemberEvents( + // roleSet, + // levelZeroSpaceID, + // displayName, + // agentInfo, + // contributor + // ); + // } + } + } + } + + async assignVirtualToRole( + roleSet: IRoleSet, + roleType: CommunityRoleType, + virtualContributorID: string, + agentInfo?: AgentInfo, + triggerNewMemberEvents = false + ): Promise { + const { virtualContributor, agent } = + await this.virtualContributorService.getVirtualContributorAndAgent( + virtualContributorID + ); + const { isMember: hasMemberRoleInParent, parentRoleSet } = + await this.isMemberInParentRoleSet(agent, roleSet.id); + if (!hasMemberRoleInParent) { + if (!parentRoleSet) { + throw new ValidationException( + `Unable to find parent roleSet for roleSet ${roleSet.id}`, + LogContext.SPACES + ); + } + throw new ValidationException( + `Unable to assign Agent (${agent.id}) to roleSet (${roleSet.id}): agent is not a member of parent roleSet ${parentRoleSet.id}`, + LogContext.SPACES + ); + } + + const virtualAlreadyHasRole = await this.isInRole(agent, roleSet, roleType); + if (virtualAlreadyHasRole) { + return virtualContributor; + } + + virtualContributor.agent = await this.assignContributorAgentToRole( + roleSet, + roleType, + agent, + CommunityContributorType.VIRTUAL + ); + + await this.contributorAddedToRole( + virtualContributor, + roleSet, + roleType, + agentInfo, + triggerNewMemberEvents + ); + // TO: THIS BREAKS THE DECOUPLING + const space = await this.communityResolverService.getSpaceForRoleSetOrFail( + roleSet.id + ); + this.aiServerAdapter.ensureContextIsLoaded(space.id); + return virtualContributor; + } + + private async isMemberInParentRoleSet( + agent: IAgent, + roleSetID: string + ): Promise<{ parentRoleSet: IRoleSet | undefined; isMember: boolean }> { + const roleSet = await this.getRoleSetOrFail(roleSetID, { + relations: { parentRoleSet: true }, + }); + + // If the parent roleSet is set, then check if the user is also a member there + if (roleSet.parentRoleSet) { + const isParentMember = await this.isMember(agent, roleSet.parentRoleSet); + return { + parentRoleSet: roleSet?.parentRoleSet, + isMember: isParentMember, + }; + } + return { + parentRoleSet: undefined, + isMember: true, + }; + } + + async assignOrganizationToRole( + roleSet: IRoleSet, + roleType: CommunityRoleType, + organizationID: string + ): Promise { + const { organization, agent } = + await this.organizationService.getOrganizationAndAgent(organizationID); + + organization.agent = await this.assignContributorAgentToRole( + roleSet, + roleType, + agent, + CommunityContributorType.ORGANIZATION + ); + + return organization; + } + + async removeUserFromRole( + roleSet: IRoleSet, + roleType: CommunityRoleType, + userID: string, + validatePolicyLimits = true + ): Promise { + const { user, agent } = await this.userService.getUserAndAgent(userID); + + user.agent = await this.removeContributorFromRole( + roleSet, + roleType, + agent, + CommunityContributorType.USER, + validatePolicyLimits + ); + + const parentRoleSet = await this.getParentRoleSet(roleSet); + if (roleType === CommunityRoleType.ADMIN && parentRoleSet) { + // Check if an admin anywhere else in the roleSet + const peerRoleSets = await this.getPeerRoleSets(parentRoleSet, roleSet); + const hasAnotherAdminRole = peerRoleSets.some(pc => + this.isInRole(agent, pc, CommunityRoleType.ADMIN) + ); + + if (!hasAnotherAdminRole) { + await this.removeContributorToImplicitRole( + parentRoleSet, + agent, + CommunityRoleImplicit.SUBSPACE_ADMIN + ); + } + } + + // TODO: FIX ME + // if (roleType === CommunityRoleType.MEMBER) { + // const community = + // await this.communityResolverService.getCommunityForRoleSet(roleSet.id); + + // await this.removeMemberFromCommunication(community, user); + // } + + return user; + } + + async removeOrganizationFromRole( + roleSet: IRoleSet, + roleType: CommunityRoleType, + organizationID: string, + validatePolicyLimits = true + ): Promise { + const { organization, agent } = + await this.organizationService.getOrganizationAndAgent(organizationID); + + organization.agent = await this.removeContributorFromRole( + roleSet, + roleType, + agent, + CommunityContributorType.ORGANIZATION, + validatePolicyLimits + ); + + return organization; + } + + async removeVirtualFromRole( + roleSet: IRoleSet, + roleType: CommunityRoleType, + virtualContributorID: string, + validatePolicyLimits = true + ): Promise { + const { virtualContributor, agent } = + await this.virtualContributorService.getVirtualContributorAndAgent( + virtualContributorID + ); + + virtualContributor.agent = await this.removeContributorFromRole( + roleSet, + roleType, + agent, + CommunityContributorType.VIRTUAL, + validatePolicyLimits + ); + + return virtualContributor; + } + + public async isCommunityAccountMatchingVcAccount( + roleSetID: string, + virtualContributorID: string + ): Promise { + return await this.isCommunityAccountMatchingVcAccount( + roleSetID, + virtualContributorID + ); + } + + private async validateUserCommunityPolicy( + roleSet: IRoleSet, + roleType: CommunityRoleType, + action: CommunityContributorsUpdateType + ) { + const userMembersCount = await this.countContributorsPerRole( + roleSet, + roleType, + CommunityContributorType.USER + ); + + const roleDefinition = this.getRoleDefinition(roleSet, roleType); + + const userPolicy = this.roleService.getUserPolicy(roleDefinition); + + if (action === CommunityContributorsUpdateType.ASSIGN) { + if (userMembersCount === userPolicy.maximum) { + throw new RoleSetPolicyRoleLimitsException( + `Max limit of users reached for role '${roleType}': ${userPolicy.maximum}, cannot assign new user.`, + LogContext.COMMUNITY + ); + } + } + + if (action === CommunityContributorsUpdateType.REMOVE) { + if (userMembersCount === userPolicy.minimum) { + throw new RoleSetPolicyRoleLimitsException( + `Min limit of users reached for role '${roleType}': ${userPolicy.minimum}, cannot remove user.`, + LogContext.COMMUNITY + ); + } + } + } + + private async validateOrganizationCommunityPolicy( + roleSet: IRoleSet, + roleType: CommunityRoleType, + action: CommunityContributorsUpdateType + ) { + const orgMemberCount = await this.countContributorsPerRole( + roleSet, + roleType, + CommunityContributorType.ORGANIZATION + ); + + const roleDefinition = this.getRoleDefinition(roleSet, roleType); + + const organizationPolicy = + this.roleService.getOrganizationPolicy(roleDefinition); + + if (action === CommunityContributorsUpdateType.ASSIGN) { + if (orgMemberCount === organizationPolicy.maximum) { + throw new RoleSetPolicyRoleLimitsException( + `Max limit of organizations reached for role '${roleType}': ${organizationPolicy.maximum}, cannot assign new organization.`, + LogContext.COMMUNITY + ); + } + } + + if (action === CommunityContributorsUpdateType.REMOVE) { + if (orgMemberCount === organizationPolicy.minimum) { + throw new RoleSetPolicyRoleLimitsException( + `Min limit of organizations reached for role '${roleType}': ${organizationPolicy.minimum}, cannot remove organization.`, + LogContext.COMMUNITY + ); + } + } + } + + private async validateCommunityPolicyLimits( + roleSet: IRoleSet, + roleType: CommunityRoleType, + action: CommunityContributorsUpdateType, + contributorType: CommunityContributorType + ) { + if (contributorType === CommunityContributorType.USER) + await this.validateUserCommunityPolicy(roleSet, roleType, action); + + if (contributorType === CommunityContributorType.ORGANIZATION) + await this.validateOrganizationCommunityPolicy(roleSet, roleType, action); + } + + public async assignContributorAgentToRole( + roleSet: IRoleSet, + roleType: CommunityRoleType, + agent: IAgent, + contributorType: CommunityContributorType + ): Promise { + await this.validateCommunityPolicyLimits( + roleSet, + roleType, + CommunityContributorsUpdateType.ASSIGN, + contributorType + ); + + const roleCredential = this.getCredentialForRole(roleSet, roleType); + + return await this.agentService.grantCredential({ + agentID: agent.id, + type: roleCredential.type, + resourceID: roleCredential.resourceID, + }); + } + + private async assignContributorToImplicitRole( + roleSet: IRoleSet, + agent: IAgent, + role: CommunityRoleImplicit + ): Promise { + const credential = this.getCredentialForImplicitRole(roleSet, role); + + return await this.agentService.grantCredential({ + agentID: agent.id, + type: credential.type, + resourceID: credential.resourceID, + }); + } + + private async removeContributorToImplicitRole( + roleSet: IRoleSet, + agent: IAgent, + role: CommunityRoleImplicit + ): Promise { + const credential = this.getCredentialForImplicitRole(roleSet, role); + + return await this.agentService.revokeCredential({ + agentID: agent.id, + type: credential.type, + resourceID: credential.resourceID, + }); + } + + private getCredentialForImplicitRole( + roleSet: IRoleSet, + role: CommunityRoleImplicit + ): ICredentialDefinition { + // Use the admin credential to get the resourceID + const adminCredential = this.getCredentialDefinitionForRole( + roleSet, + CommunityRoleType.ADMIN + ); + const resourceID = adminCredential.resourceID; + switch (role) { + case CommunityRoleImplicit.SUBSPACE_ADMIN: + return { + type: AuthorizationCredential.SPACE_SUBSPACE_ADMIN, + resourceID, + }; + default: { + throw new RoleSetMembershipException( + `Invalid implicit role: ${role}`, + LogContext.COMMUNITY + ); + } + } + } + + private async removeContributorFromRole( + roleSet: IRoleSet, + roleType: CommunityRoleType, + agent: IAgent, + contributorType: CommunityContributorType, + validatePolicyLimits: boolean + ): Promise { + if (validatePolicyLimits) { + await this.validateCommunityPolicyLimits( + roleSet, + roleType, + CommunityContributorsUpdateType.REMOVE, + contributorType + ); + } + + const roleCredential = this.getCredentialForRole(roleSet, roleType); + + return await this.agentService.revokeCredential({ + agentID: agent.id, + type: roleCredential.type, + resourceID: roleCredential.resourceID, + }); + } + + async isMember(agent: IAgent, roleSet: IRoleSet): Promise { + const membershipCredential = this.getCredentialDefinitionForRole( + roleSet, + CommunityRoleType.MEMBER + ); + + const validCredential = await this.agentService.hasValidCredential( + agent.id, + { + type: membershipCredential.type, + resourceID: membershipCredential.resourceID, + } + ); + return validCredential; + } + + async isInRole( + agent: IAgent, + roleSet: IRoleSet, + role: CommunityRoleType + ): Promise { + const membershipCredential = this.getCredentialDefinitionForRole( + roleSet, + role + ); + + const validCredential = await this.agentService.hasValidCredential( + agent.id, + { + type: membershipCredential.type, + resourceID: membershipCredential.resourceID, + } + ); + return validCredential; + } + + async isInRoleImplicit( + agent: IAgent, + roleSet: IRoleSet, + role: CommunityRoleImplicit + ): Promise { + const membershipCredential = this.getCredentialForImplicitRole( + roleSet, + role + ); + + const validCredential = await this.agentService.hasValidCredential( + agent.id, + { + type: membershipCredential.type, + resourceID: membershipCredential.resourceID, + } + ); + return validCredential; + } + + async createApplication( + applicationData: CreateApplicationInput + ): Promise { + const { user, agent } = await this.userService.getUserAndAgent( + applicationData.userID + ); + const roleSet = await this.getRoleSetOrFail(applicationData.roleSetID, { + relations: { + parentRoleSet: true, + }, + }); + + await this.validateApplicationFromUser(user, agent, roleSet); + + const application = + await this.applicationService.createApplication(applicationData); + application.roleSet = roleSet; + return await this.applicationService.save(application); + } + + async createInvitationExistingContributor( + invitationData: CreateInvitationInput + ): Promise { + const { contributor: contributor, agent } = + await this.contributorService.getContributorAndAgent( + invitationData.invitedContributor + ); + const roleSet = await this.getRoleSetOrFail(invitationData.roleSetID); + + await this.validateInvitationToExistingContributor( + contributor, + agent, + roleSet + ); + + const invitation = await this.invitationService.createInvitation( + invitationData, + contributor + ); + invitation.roleSet = roleSet; + + return await this.invitationService.save(invitation); + } + + async createPlatformInvitation( + invitationData: CreatePlatformInvitationOnCommunityInput, + agentInfo: AgentInfo + ): Promise { + await this.validateInvitationToExternalUser( + invitationData.email, + invitationData.roleSetID + ); + const roleSet = await this.getRoleSetOrFail(invitationData.roleSetID, { + relations: {}, + }); + + const externalInvitationInput: CreatePlatformInvitationInput = { + ...invitationData, + createdBy: agentInfo.userID, + }; + const externalInvitation = + await this.platformInvitationService.createPlatformInvitation( + externalInvitationInput + ); + externalInvitation.roleSet = roleSet; + return await this.platformInvitationService.save(externalInvitation); + } + + private async validateApplicationFromUser( + user: IUser, + agent: IAgent, + roleSet: IRoleSet + ) { + const openApplication = await this.findOpenApplication(user.id, roleSet.id); + if (openApplication) { + throw new RoleSetMembershipException( + `Application not possible: An open application (ID: ${openApplication.id}) already exists for contributor ${openApplication.user?.id} on Community: ${roleSet.id}.`, + LogContext.COMMUNITY + ); + } + + const openInvitation = await this.findOpenInvitation(user.id, roleSet.id); + if (openInvitation) { + throw new RoleSetMembershipException( + `Application not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on Community: ${roleSet.id}.`, + LogContext.COMMUNITY + ); + } + + // Check if the user is already a member; if so do not allow an application + const isExistingMember = await this.isMember(agent, roleSet); + if (isExistingMember) + throw new RoleSetMembershipException( + `Application not possible: Contributor ${user.id} is already a member of the Community: ${roleSet.id}.`, + LogContext.COMMUNITY + ); + } + + private async validateInvitationToExistingContributor( + contributor: IContributor, + agent: IAgent, + roleSet: IRoleSet + ) { + const openInvitation = await this.findOpenInvitation( + contributor.id, + roleSet.id + ); + if (openInvitation) { + throw new RoleSetMembershipException( + `Invitation not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on Community: ${roleSet.id}.`, + LogContext.COMMUNITY + ); + } + + const openApplication = await this.findOpenApplication( + contributor.id, + roleSet.id + ); + if (openApplication) { + throw new RoleSetMembershipException( + `Invitation not possible: An open application (ID: ${openApplication.id}) already exists for contributor ${openApplication.user?.id} on Community: ${roleSet.id}.`, + LogContext.COMMUNITY + ); + } + + // Check if the user is already a member; if so do not allow an application + const isExistingMember = await this.isMember(agent, roleSet); + if (isExistingMember) + throw new RoleSetMembershipException( + `Invitation not possible: Contributor ${contributor.id} is already a member of the Community: ${roleSet.id}.`, + LogContext.COMMUNITY + ); + } + + private async validateInvitationToExternalUser( + email: string, + roleSetID: string + ) { + // Check if a user with the provided email address already exists or not + const isExistingUser = await this.userService.isRegisteredUser(email); + if (isExistingUser) { + throw new RoleSetMembershipException( + `User with the provided email address already exists: ${email}`, + LogContext.COMMUNITY + ); + } + + const platformInvitations = + await this.platformInvitationService.findPlatformInvitationsForUser( + email + ); + for (const platformInvitation of platformInvitations) { + if ( + platformInvitation.roleSet && + platformInvitation.roleSet.id === roleSetID + ) { + throw new RoleSetMembershipException( + `An invitation with the provided email address (${email}) already exists for the specified roleSet: ${roleSetID}`, + LogContext.COMMUNITY + ); + } + } + } + + async getApplications(roleSet: IRoleSet): Promise { + const roleSetApplications = await this.getRoleSetOrFail(roleSet.id, { + relations: { applications: true }, + }); + return roleSetApplications?.applications || []; + } + + async getInvitations(roleSet: IRoleSet): Promise { + const roleSetInvitations = await this.getRoleSetOrFail(roleSet.id, { + relations: { invitations: true }, + }); + return roleSetInvitations?.invitations || []; + } + + async getPlatformInvitations( + roleSet: IRoleSet + ): Promise { + const roleSetInvs = await this.getRoleSetOrFail(roleSet.id, { + relations: { platformInvitations: true }, + }); + return roleSetInvs?.platformInvitations || []; + } + + async getMembersCount(roleSet: IRoleSet): Promise { + const membershipCredential = this.getCredentialDefinitionForRole( + roleSet, + CommunityRoleType.MEMBER + ); + + const credentialMatches = + await this.agentService.countAgentsWithMatchingCredentials({ + type: membershipCredential.type, + resourceID: membershipCredential.resourceID, + }); + + return credentialMatches; + } + + async getCommunityImplicitRoles( + agentInfo: AgentInfo, + roleSet: IRoleSet + ): Promise { + const result: CommunityRoleImplicit[] = []; + const agent = await this.agentService.getAgentOrFail(agentInfo.agentID); + + const rolesImplicit: CommunityRoleImplicit[] = Object.values( + CommunityRoleImplicit + ) as CommunityRoleImplicit[]; + for (const role of rolesImplicit) { + const hasAgentRole = await this.isInRoleImplicit(agent, roleSet, role); + if (hasAgentRole) { + result.push(role); + } + } + return result; + } + public async getPeerRoleSets( parentRoleSet: IRoleSet, childRoleSet: IRoleSet diff --git a/src/domain/community/community-role/community.role.module.ts b/src/domain/community/community-role/community.role.module.ts deleted file mode 100644 index a95a96bd5e..0000000000 --- a/src/domain/community/community-role/community.role.module.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Module } from '@nestjs/common'; -import { CommunityRoleApplicationLifecycleOptionsProvider } from './community.role.lifecycle.application.options.provider'; -import { CommunityResolverFields } from './community.role.resolver.fields'; -import { CommunityRoleResolverMutations } from './community.role.resolver.mutations'; -import { CommunityRoleEventsService } from './community.role.service.events'; -import { CommunityModule } from '../community/community.module'; -import { CommunityRoleService } from './community.role.service'; -import { CommunityRoleInvitationLifecycleOptionsProvider } from './community.role.lifecycle.invitation.options.provider'; -import { AuthorizationModule } from '@core/authorization/authorization.module'; -import { AuthorizationPolicyModule } from '@domain/common/authorization-policy/authorization.policy.module'; -import { AgentModule } from '@domain/agent/agent/agent.module'; -import { UserModule } from '../user/user.module'; -import { ContributorModule } from '../contributor/contributor.module'; -import { OrganizationModule } from '../organization/organization.module'; -import { VirtualContributorModule } from '../virtual-contributor/virtual.contributor.module'; -import { ApplicationModule } from '../../access/application/application.module'; -import { AiServerAdapterModule } from '@services/adapters/ai-server-adapter/ai.server.adapter.module'; -import { PlatformInvitationModule } from '@platform/invitation/platform.invitation.module'; -import { EntityResolverModule } from '@services/infrastructure/entity-resolver/entity.resolver.module'; -import { ContributionReporterModule } from '@services/external/elasticsearch/contribution-reporter'; -import { NotificationAdapterModule } from '@services/adapters/notification-adapter/notification.adapter.module'; -import { ActivityAdapterModule } from '@services/adapters/activity-adapter/activity.adapter.module'; -import { LifecycleModule } from '@domain/common/lifecycle/lifecycle.module'; -import { RoleSetModule } from '@domain/access/role-set/role.set.module'; -import { RoleModule } from '@domain/access/role/role.module'; -import { InvitationModule } from '@domain/access/invitation/invitation.module'; - -@Module({ - imports: [ - AuthorizationModule, - AuthorizationPolicyModule, - EntityResolverModule, - AgentModule, - CommunityModule, - RoleSetModule, - RoleModule, - UserModule, - ContributorModule, - OrganizationModule, - VirtualContributorModule, - LifecycleModule, - ApplicationModule, - InvitationModule, - PlatformInvitationModule, - AiServerAdapterModule, - ContributionReporterModule, - NotificationAdapterModule, - ActivityAdapterModule, - ], - providers: [ - CommunityRoleService, - CommunityRoleEventsService, - CommunityRoleResolverMutations, - CommunityResolverFields, - CommunityRoleApplicationLifecycleOptionsProvider, - CommunityRoleInvitationLifecycleOptionsProvider, - ], - exports: [CommunityRoleService], -}) -export class CommunityRoleModule {} diff --git a/src/domain/community/community-role/community.role.resolver.fields.ts b/src/domain/community/community-role/community.role.resolver.fields.ts deleted file mode 100644 index 961ce421c5..0000000000 --- a/src/domain/community/community-role/community.role.resolver.fields.ts +++ /dev/null @@ -1,273 +0,0 @@ -import { GraphqlGuard } from '@core/authorization'; -import { UseGuards } from '@nestjs/common'; -import { Parent, ResolveField, Resolver, Args, Float } from '@nestjs/graphql'; -import { - AuthorizationAgentPrivilege, - CurrentUser, - Profiling, -} from '@src/common/decorators'; -import { Community, ICommunity } from '@domain/community/community'; -import { IUser } from '@domain/community/user/user.interface'; -import { IApplication } from '@domain/access/application'; -import { AuthorizationPrivilege } from '@common/enums'; -import { IOrganization } from '../organization'; -import { CommunityRoleType } from '@common/enums/community.role'; -import { PaginationArgs, PaginatedUsers } from '@core/pagination'; -import { PaginationInputOutOfBoundException } from '@common/exceptions'; -import { UserService } from '../user/user.service'; -import { UserFilterInput } from '@core/filtering'; -import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; -import { AgentInfo } from '@core/authentication.agent.info/agent.info'; -import { IVirtualContributor } from '../virtual-contributor/virtual.contributor.interface'; -import { CommunityRoleImplicit } from '@common/enums/community.role.implicit'; -import { IPlatformInvitation } from '@platform/invitation'; -import { CommunityRoleService } from './community.role.service'; -import { CommunityService } from '../community/community.service'; -import { IInvitation } from '@domain/access/invitation/invitation.interface'; - -@Resolver(() => ICommunity) -export class CommunityResolverFields { - constructor( - private communityRoleService: CommunityRoleService, - private communityService: CommunityService, - private userService: UserService - ) {} - - @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) - @UseGuards(GraphqlGuard) - @ResolveField('memberUsers', () => [IUser], { - nullable: false, - description: 'All users that are contributing to this Community.', - }) - @Profiling.api - async memberUsers( - @Parent() community: Community, - @Args({ - name: 'limit', - type: () => Float, - description: - 'The positive number of member users to return; if omitted returns all member users.', - nullable: true, - }) - limit?: number - ) { - if (limit && limit < 0) { - throw new PaginationInputOutOfBoundException( - `Limit expects a positive amount: ${limit} provided instead` - ); - } - - return await this.communityRoleService.getUsersWithRole( - community, - CommunityRoleType.MEMBER, - limit - ); - } - - @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) - @UseGuards(GraphqlGuard) - @ResolveField('availableMemberUsers', () => PaginatedUsers, { - nullable: false, - description: 'All available users that are potential Community members.', - }) - @Profiling.api - async availableMemberUsers( - @Parent() community: Community, - @Args({ nullable: true }) pagination: PaginationArgs, - @Args('filter', { nullable: true }) filter?: UserFilterInput - ) { - const memberRoleCredentials = - this.communityRoleService.getCredentialDefinitionForRole( - community, - CommunityRoleType.MEMBER - ); - - const parentCommunity = - await this.communityService.getParentCommunity(community); - - const parentCommunityMemberCredentials = parentCommunity - ? this.communityRoleService.getCredentialDefinitionForRole( - parentCommunity, - CommunityRoleType.MEMBER - ) - : undefined; - - const communityMemberCredentials = { - member: memberRoleCredentials, - parentCommunityMember: parentCommunityMemberCredentials, - }; - - return this.userService.getPaginatedAvailableMemberUsers( - communityMemberCredentials, - pagination, - filter - ); - } - - @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) - @UseGuards(GraphqlGuard) - @ResolveField('usersInRole', () => [IUser], { - nullable: false, - description: 'All users that have the specified Role in this Community.', - }) - @Profiling.api - async usersInRole( - @Parent() community: Community, - @Args('role', { type: () => CommunityRoleType, nullable: false }) - role: CommunityRoleType - ) { - return await this.communityRoleService.getUsersWithRole(community, role); - } - - @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) - @UseGuards(GraphqlGuard) - @ResolveField('organizationsInRole', () => [IOrganization], { - nullable: false, - description: - 'All Organizations that have the specified Role in this Community.', - }) - @Profiling.api - async organizationsInRole( - @Parent() community: Community, - @Args('role', { type: () => CommunityRoleType, nullable: false }) - role: CommunityRoleType - ): Promise { - return await this.communityRoleService.getOrganizationsWithRole( - community, - role - ); - } - - @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) - @UseGuards(GraphqlGuard) - @ResolveField('virtualContributorsInRole', () => [IVirtualContributor], { - nullable: false, - description: 'All virtuals that have the specified Role in this Community.', - }) - @Profiling.api - async virtualsInRole( - @Parent() community: Community, - @Args('role', { type: () => CommunityRoleType, nullable: false }) - role: CommunityRoleType - ) { - return await this.communityRoleService.getVirtualContributorsWithRole( - community, - role - ); - } - - @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) - @UseGuards(GraphqlGuard) - @ResolveField('availableLeadUsers', () => PaginatedUsers, { - nullable: false, - description: - 'All member users excluding the current lead users in this Community.', - }) - @Profiling.api - async availableLeadUsers( - @Parent() community: Community, - @Args({ nullable: true }) pagination: PaginationArgs, - @Args('filter', { nullable: true }) filter?: UserFilterInput - ) { - const memberRoleCredentials = - this.communityRoleService.getCredentialDefinitionForRole( - community, - CommunityRoleType.MEMBER - ); - - const leadRoleCredential = - this.communityRoleService.getCredentialDefinitionForRole( - community, - CommunityRoleType.LEAD - ); - - const credentialCriteria = { - member: memberRoleCredentials, - lead: leadRoleCredential, - }; - - return this.userService.getPaginatedAvailableLeadUsers( - credentialCriteria, - pagination, - filter - ); - } - - @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) - @UseGuards(GraphqlGuard) - @ResolveField('invitations', () => [IInvitation], { - nullable: false, - description: 'Invitations for this community.', - }) - @Profiling.api - async inivitations(@Parent() community: Community): Promise { - return await this.communityRoleService.getInvitations(community); - } - - @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) - @UseGuards(GraphqlGuard) - @ResolveField('platformInvitations', () => [IPlatformInvitation], { - nullable: false, - description: - 'Invitations to join this Community for users not yet on the Alkemio platform.', - }) - @Profiling.api - async platformInvitations( - @Parent() community: Community - ): Promise { - return await this.communityRoleService.getPlatformInvitations(community); - } - - @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) - @UseGuards(GraphqlGuard) - @ResolveField('applications', () => [IApplication], { - nullable: false, - description: 'Applications available for this community.', - }) - @Profiling.api - async applications(@Parent() community: Community) { - const apps = await this.communityRoleService.getApplications(community); - return apps || []; - } - - @UseGuards(GraphqlGuard) - @ResolveField('myMembershipStatus', () => CommunityMembershipStatus, { - nullable: true, - description: 'The membership status of the currently logged in user.', - }) - async myMembershipStatus( - @CurrentUser() agentInfo: AgentInfo, - @Parent() community: ICommunity - ): Promise { - return this.communityRoleService.getMembershipStatus(agentInfo, community); - } - - @UseGuards(GraphqlGuard) - @ResolveField('myRoles', () => [CommunityRoleType], { - nullable: false, - description: - 'The roles on this community for the currently logged in user.', - }) - async myRoles( - @CurrentUser() agentInfo: AgentInfo, - @Parent() community: ICommunity - ): Promise { - return this.communityRoleService.getCommunityRoles(agentInfo, community); - } - - @UseGuards(GraphqlGuard) - @ResolveField('myRolesImplicit', () => [CommunityRoleImplicit], { - nullable: false, - description: - 'The implicit roles on this community for the currently logged in user.', - }) - async myRolesImplicit( - @CurrentUser() agentInfo: AgentInfo, - @Parent() community: ICommunity - ): Promise { - return this.communityRoleService.getCommunityImplicitRoles( - agentInfo, - community - ); - } -} diff --git a/src/domain/community/community-role/community.role.resolver.mutations.ts b/src/domain/community/community-role/community.role.resolver.mutations.ts deleted file mode 100644 index f71213b382..0000000000 --- a/src/domain/community/community-role/community.role.resolver.mutations.ts +++ /dev/null @@ -1,767 +0,0 @@ -import { UseGuards } from '@nestjs/common'; -import { Args, Mutation, Resolver } from '@nestjs/graphql'; -import { CurrentUser, Profiling } from '@src/common/decorators'; -import { IApplication } from '@domain/access/application'; -import { ApplicationService } from '@domain/access/application/application.service'; -import { ICommunity } from '@domain/community/community/community.interface'; -import { GraphqlGuard } from '@core/authorization'; -import { AgentInfo } from '@core/authentication.agent.info/agent.info'; -import { AuthorizationPrivilege, LogContext } from '@common/enums'; -import { AuthorizationService } from '@core/authorization/authorization.service'; -import { UserService } from '@domain/community/user/user.service'; -import { UserAuthorizationService } from '../user/user.service.authorization'; -import { RemoveCommunityRoleFromUserInput } from './dto/community.role.dto.role.remove.user'; -import { ApplicationEventInput } from '../../access/application/dto/application.dto.event'; -import { ApplicationAuthorizationService } from '../../access/application/application.service.authorization'; -import { AgentService } from '@domain/agent/agent/agent.service'; -import { CommunityJoinInput } from './dto/community.role.dto.join'; -import { CommunityRoleApplyInput } from './dto/community.role.dto.apply'; -import { CommunityMemberClaim } from '@services/external/trust-registry/trust.registry.claim/claim.community.member'; -import { AgentBeginVerifiedCredentialOfferOutput } from '@domain/agent/agent/dto/agent.dto.verified.credential.offer.begin.output'; -import { AlkemioUserClaim } from '@services/external/trust-registry/trust.registry.claim/claim.alkemio.user'; -import { RemoveCommunityRoleFromOrganizationInput } from './dto/community.role.dto.role.remove.organization'; -import { AssignCommunityRoleToOrganizationInput } from './dto/community.role.dto.role.assign.organization'; -import { CommunityRoleType } from '@common/enums/community.role'; -import { AssignCommunityRoleToUserInput } from './dto/community.role.dto.role.assign.user'; -import { NotificationAdapter } from '@services/adapters/notification-adapter/notification.adapter'; -import { NotificationInputCommunityApplication } from '@services/adapters/notification-adapter/dto/notification.dto.input.community.application'; -import { NotificationInputCommunityInvitation } from '@services/adapters/notification-adapter/dto/notification.dto.input.community.invitation'; -import { IOrganization } from '../organization'; -import { IUser } from '../user/user.interface'; -import { CreatePlatformInvitationOnCommunityInput } from './dto/community.role.dto.platform.invitation.community'; -import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; -import { CommunityMembershipException } from '@common/exceptions/community.membership.exception'; -import { AssignCommunityRoleToVirtualInput } from './dto/community.role.dto.role.assign.virtual'; -import { RemoveCommunityRoleFromVirtualInput } from './dto/community.role.dto.role.remove.virtual'; -import { VirtualContributorService } from '../virtual-contributor/virtual.contributor.service'; -import { VirtualContributor } from '../virtual-contributor/virtual.contributor.entity'; -import { EntityNotInitializedException } from '@common/exceptions'; -import { CommunityInvitationException } from '@common/exceptions/community.invitation.exception'; -import { CreateInvitationForContributorsOnCommunityInput } from './dto/community.role.dto.invite.contributor'; -import { IContributor } from '../contributor/contributor.interface'; -import { ContributorService } from '../contributor/contributor.service'; -import { PlatformInvitationAuthorizationService } from '@platform/invitation/platform.invitation.service.authorization'; -import { PlatformInvitationService } from '@platform/invitation/platform.invitation.service'; -import { IPlatformInvitation } from '@platform/invitation'; -import { NotificationInputPlatformInvitation } from '@services/adapters/notification-adapter/dto/notification.dto.input.platform.invitation'; -import { NotificationInputCommunityInvitationVirtualContributor } from '@services/adapters/notification-adapter/dto/notification.dto.input.community.invitation.vc'; -import { CommunityRoleService } from './community.role.service'; -import { CommunityService } from '../community/community.service'; -import { CommunityAuthorizationService } from '../community/community.service.authorization'; -import { CommunityRoleInvitationLifecycleOptionsProvider } from './community.role.lifecycle.invitation.options.provider'; -import { CommunityRoleApplicationLifecycleOptionsProvider } from './community.role.lifecycle.application.options.provider'; -import { IVirtualContributor } from '../virtual-contributor/virtual.contributor.interface'; -import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service'; -import { RoleSetService } from '@domain/access/role-set/role.set.service'; -import { CommunityResolverService } from '@services/infrastructure/entity-resolver/community.resolver.service'; -import { InvitationAuthorizationService } from '@domain/access/invitation/invitation.service.authorization'; -import { IInvitation } from '@domain/access/invitation/invitation.interface'; -import { CreateInvitationInput } from '@domain/access/invitation/dto/invitation.dto.create'; -import { InvitationEventInput } from '@domain/access/invitation/dto/invitation.dto.event'; -import { InvitationService } from '@domain/access/invitation/invitation.service'; - -@Resolver() -export class CommunityRoleResolverMutations { - constructor( - private authorizationService: AuthorizationService, - private authorizationPolicyService: AuthorizationPolicyService, - private notificationAdapter: NotificationAdapter, - private userService: UserService, - private userAuthorizationService: UserAuthorizationService, - private virtualContributorService: VirtualContributorService, - private communityRoleService: CommunityRoleService, - private communityService: CommunityService, - private roleSetService: RoleSetService, - private communityResolverService: CommunityResolverService, - private communityAuthorizationService: CommunityAuthorizationService, - private communityLifecycleApplicationOptionsProvider: CommunityRoleApplicationLifecycleOptionsProvider, - private communityLifecycleInvitationOptionsProvider: CommunityRoleInvitationLifecycleOptionsProvider, - private applicationService: ApplicationService, - private agentService: AgentService, - private applicationAuthorizationService: ApplicationAuthorizationService, - private invitationService: InvitationService, - private invitationAuthorizationService: InvitationAuthorizationService, - private contributorService: ContributorService, - private platformInvitationAuthorizationService: PlatformInvitationAuthorizationService, - private platformInvitationService: PlatformInvitationService - ) {} - - @UseGuards(GraphqlGuard) - @Mutation(() => IUser, { - description: 'Assigns a User to a role in the specified Community.', - }) - @Profiling.api - async assignCommunityRoleToUser( - @CurrentUser() agentInfo: AgentInfo, - @Args('roleData') roleData: AssignCommunityRoleToUserInput - ): Promise { - const roleSet = await this.roleSetService.getRoleSetOrFail( - roleData.roleSetID - ); - - let requiredPrivilege = AuthorizationPrivilege.GRANT; - if (roleData.role === CommunityRoleType.MEMBER) { - requiredPrivilege = AuthorizationPrivilege.COMMUNITY_ADD_MEMBER; - } - - this.authorizationService.grantAccessOrFail( - agentInfo, - roleSet.authorization, - requiredPrivilege, - `assign user community role: ${roleSet.id}` - ); - - await this.communityRoleService.assignUserToRole( - roleSet, - roleData.role, - roleData.userID, - agentInfo, - true - ); - - // reset the user authorization policy so that their profile is visible to other community members - const user = await this.userService.getUserOrFail(roleData.userID); - const authorizations = - await this.userAuthorizationService.applyAuthorizationPolicy(user); - await this.authorizationPolicyService.saveAll(authorizations); - return await this.userService.getUserOrFail(roleData.userID); - } - - @UseGuards(GraphqlGuard) - @Mutation(() => IOrganization, { - description: 'Assigns an Organization a Role in the specified Community.', - }) - @Profiling.api - async assignCommunityRoleToOrganization( - @CurrentUser() agentInfo: AgentInfo, - @Args('roleData') - roleData: AssignCommunityRoleToOrganizationInput - ): Promise { - const roleSet = await this.roleSetService.getRoleSetOrFail( - roleData.roleSetID - ); - - this.authorizationService.grantAccessOrFail( - agentInfo, - roleSet.authorization, - AuthorizationPrivilege.GRANT, - `assign organization community role: ${roleSet.id}` - ); - return await this.communityRoleService.assignOrganizationToRole( - roleSet, - roleData.role, - roleData.organizationID - ); - } - - @UseGuards(GraphqlGuard) - @Mutation(() => IVirtualContributor, { - description: - 'Assigns a Virtual Contributor to a role in the specified Community.', - }) - @Profiling.api - async assignCommunityRoleToVirtual( - @CurrentUser() agentInfo: AgentInfo, - @Args('roleData') roleData: AssignCommunityRoleToVirtualInput - ): Promise { - const roleSet = await this.roleSetService.getRoleSetOrFail( - roleData.roleSetID - ); - - let requiredPrivilege = AuthorizationPrivilege.GRANT; - if (roleData.role === CommunityRoleType.MEMBER) { - const sameAccount = - await this.communityRoleService.isCommunityAccountMatchingVcAccount( - roleSet.id, - roleData.virtualContributorID - ); - if (sameAccount) { - requiredPrivilege = - AuthorizationPrivilege.COMMUNITY_ADD_MEMBER_VC_FROM_ACCOUNT; - } else { - requiredPrivilege = AuthorizationPrivilege.COMMUNITY_ADD_MEMBER; - } - } - - this.authorizationService.grantAccessOrFail( - agentInfo, - roleSet.authorization, - requiredPrivilege, - `assign virtual community role: ${roleSet.id}` - ); - - // Also require ACCESS_VIRTUAL_CONTRIBUTORS to assign a virtual contributor - this.authorizationService.grantAccessOrFail( - agentInfo, - roleSet.authorization, - AuthorizationPrivilege.ACCESS_VIRTUAL_CONTRIBUTOR, - `assign virtual community role VC privilege: ${roleSet.id}` - ); - - await this.communityRoleService.assignVirtualToRole( - roleSet, - roleData.role, - roleData.virtualContributorID, - agentInfo, - true - ); - - return await this.virtualContributorService.getVirtualContributorOrFail( - roleData.virtualContributorID - ); - } - - @UseGuards(GraphqlGuard) - @Mutation(() => IUser, { - description: 'Removes a User from a Role in the specified Community.', - }) - @Profiling.api - async removeCommunityRoleFromUser( - @CurrentUser() agentInfo: AgentInfo, - @Args('roleData') roleData: RemoveCommunityRoleFromUserInput - ): Promise { - const roleSet = await this.roleSetService.getRoleSetOrFail( - roleData.roleSetID - ); - - // Extend the authorization policy with a credential rule to assign the GRANT privilege - // to the user specified in the incoming mutation. Then if it is the same user as is logged - // in then the user will have the GRANT privilege + so can carry out the mutation - const community = - await this.communityResolverService.getCommunityForRoleSet(roleSet.id); - - const extendedAuthorization = - this.communityAuthorizationService.extendAuthorizationPolicyForSelfRemoval( - community, - roleData.userID - ); - - await this.authorizationService.grantAccessOrFail( - agentInfo, - extendedAuthorization, - AuthorizationPrivilege.GRANT, - `remove user from community role: ${roleSet.id}` - ); - - await this.communityRoleService.removeUserFromRole( - roleSet, - roleData.role, - roleData.userID - ); - // reset the user authorization policy so that their profile is not visible - // to other community members - const user = await this.userService.getUserOrFail(roleData.userID); - const authorizations = - await this.userAuthorizationService.applyAuthorizationPolicy(user); - await this.authorizationPolicyService.saveAll(authorizations); - return await this.userService.getUserOrFail(roleData.userID); - } - - @UseGuards(GraphqlGuard) - @Mutation(() => IOrganization, { - description: - 'Removes an Organization from a Role in the specified Community.', - }) - @Profiling.api - async removeCommunityRoleFromOrganization( - @CurrentUser() agentInfo: AgentInfo, - @Args('roleData') roleData: RemoveCommunityRoleFromOrganizationInput - ): Promise { - const roleSet = await this.roleSetService.getRoleSetOrFail( - roleData.roleSetID - ); - await this.authorizationService.grantAccessOrFail( - agentInfo, - roleSet.authorization, - AuthorizationPrivilege.GRANT, - `remove community role organization: ${roleSet.id}` - ); - - return await this.communityRoleService.removeOrganizationFromRole( - roleSet, - roleData.role, - roleData.organizationID - ); - } - - @UseGuards(GraphqlGuard) - @Mutation(() => IVirtualContributor, { - description: 'Removes a Virtual from a Role in the specified Community.', - }) - @Profiling.api - async removeCommunityRoleFromVirtual( - @CurrentUser() agentInfo: AgentInfo, - @Args('roleData') roleData: RemoveCommunityRoleFromVirtualInput - ): Promise { - const community = await this.communityService.getCommunityOrFail( - roleData.roleSetID - ); - - // Extend the authorization policy with a credential rule to assign the GRANT privilege - // to the user with rights around the incoming virtual being removed. - //. Then if it is the user that is logged in then the user will have the GRANT privilege + so can carry out the mutation - const extendedAuthorization = - await this.communityAuthorizationService.extendAuthorizationPolicyForVirtualContributorRemoval( - community, - roleData.virtualContributorID - ); - - await this.authorizationService.grantAccessOrFail( - agentInfo, - extendedAuthorization, - AuthorizationPrivilege.GRANT, - `remove virtual from community role: ${community.id}` - ); - - await this.communityRoleService.removeVirtualFromRole( - community, - roleData.role, - roleData.virtualContributorID - ); - - return await this.virtualContributorService.getVirtualContributorOrFail( - roleData.virtualContributorID - ); - } - - @UseGuards(GraphqlGuard) - @Mutation(() => IApplication, { - description: 'Apply to join the specified Community as a member.', - }) - @Profiling.api - async applyForCommunityMembership( - @CurrentUser() agentInfo: AgentInfo, - @Args('applicationData') applicationData: CommunityRoleApplyInput - ): Promise { - const community = await this.communityService.getCommunityOrFail( - applicationData.roleSetID, - { - relations: { - parentCommunity: true, - }, - } - ); - - await this.authorizationService.grantAccessOrFail( - agentInfo, - community.authorization, - AuthorizationPrivilege.COMMUNITY_APPLY, - `create application community: ${community.id}` - ); - - if (community.parentCommunity) { - const { agent } = await this.userService.getUserAndAgent( - agentInfo.userID - ); - const userIsMemberInParent = await this.communityRoleService.isInRole( - agent, - community.parentCommunity, - CommunityRoleType.MEMBER - ); - if (!userIsMemberInParent) { - throw new CommunityMembershipException( - `Unable to apply for Community (${community.id}): user is not a member of the parent Community`, - LogContext.COMMUNITY - ); - } - } - - let application = await this.communityRoleService.createApplication({ - roleSetID: community.id, - questions: applicationData.questions, - userID: agentInfo.userID, - }); - - application = await this.applicationService.save(application); - - const authorization = - await this.applicationAuthorizationService.applyAuthorizationPolicy( - application, - community.authorization - ); - await this.authorizationPolicyService.save(authorization); - - // Send the notification - const notificationInput: NotificationInputCommunityApplication = { - triggeredBy: agentInfo.userID, - community: community, - }; - await this.notificationAdapter.applicationCreated(notificationInput); - - return application; - } - - @UseGuards(GraphqlGuard) - @Mutation(() => [IInvitation], { - description: - 'Invite an existing Contriburor to join the specified Community as a member.', - }) - @Profiling.api - async inviteContributorsForCommunityMembership( - @CurrentUser() agentInfo: AgentInfo, - @Args('invitationData') - invitationData: CreateInvitationForContributorsOnCommunityInput - ): Promise { - const community = await this.communityService.getCommunityOrFail( - invitationData.roleSetID, - { - relations: { - parentCommunity: { - authorization: true, - }, - }, - } - ); - if (invitationData.invitedContributors.length === 0) { - throw new CommunityInvitationException( - `No contributors were provided to invite: ${community.id}`, - LogContext.COMMUNITY - ); - } - - await this.authorizationService.grantAccessOrFail( - agentInfo, - community.authorization, - AuthorizationPrivilege.COMMUNITY_INVITE, - `create invitation community: ${community.id}` - ); - - const contributors: IContributor[] = []; - for (const contributorID of invitationData.invitedContributors) { - const contributor = - await this.contributorService.getContributorByUuidOrFail( - contributorID, - { - relations: { - agent: true, - }, - } - ); - contributors.push(contributor); - } - - // Logic is that the ability to invite to a subspace requires the ability to invite to the - // parent community if the user is not a member there - if (community.parentCommunity) { - const parentCommunityAuthorization = - community.parentCommunity.authorization; - const canInviteToParent = this.authorizationService.isAccessGranted( - agentInfo, - parentCommunityAuthorization, - AuthorizationPrivilege.COMMUNITY_INVITE - ); - - // Need to see if also can invite to the parent community if any of the users are not members there - for (const contributor of contributors) { - if (!contributor.agent) { - throw new EntityNotInitializedException( - `Unable to load agent on contributor: ${contributor.id}`, - LogContext.COMMUNITY - ); - } - const isMember = await this.communityRoleService.isMember( - contributor.agent, - community.parentCommunity - ); - if (!isMember && !canInviteToParent) { - throw new CommunityInvitationException( - `Contributor is not a member of the parent community (${community.parentCommunity.id}) and the current user does not have the privilege to invite to the parent community`, - LogContext.COMMUNITY - ); - } else { - invitationData.invitedToParent = true; - } - } - } else { - invitationData.invitedToParent = false; - } - - return Promise.all( - contributors.map(async invitedContributor => { - return await this.inviteSingleExistingContributor( - community, - invitedContributor, - agentInfo, - invitationData.invitedToParent, - invitationData.welcomeMessage - ); - }) - ); - } - - private async inviteSingleExistingContributor( - community: ICommunity, - invitedContributor: IContributor, - agentInfo: AgentInfo, - invitedToParent: boolean, - welcomeMessage?: string - ): Promise { - const input: CreateInvitationInput = { - roleSetID: community.id, - invitedContributor: invitedContributor.id, - createdBy: agentInfo.userID, - invitedToParent, - welcomeMessage, - }; - - let invitation = - await this.communityRoleService.createInvitationExistingContributor( - input - ); - - invitation = await this.invitationService.save(invitation); - - const authorization = - await this.invitationAuthorizationService.applyAuthorizationPolicy( - invitation, - community.authorization - ); - await this.authorizationPolicyService.save(authorization); - - if (invitedContributor instanceof VirtualContributor) { - const accountProvider = - await this.virtualContributorService.getProvider(invitedContributor); - const notificationInput: NotificationInputCommunityInvitationVirtualContributor = - { - triggeredBy: agentInfo.userID, - community: community, - invitedContributorID: invitedContributor.id, - accountHost: accountProvider, - welcomeMessage, - }; - - await this.notificationAdapter.invitationVirtualContributorCreated( - notificationInput - ); - } else { - // Send the notification - const notificationInput: NotificationInputCommunityInvitation = { - triggeredBy: agentInfo.userID, - community: community, - invitedContributorID: invitedContributor.id, - welcomeMessage, - }; - - await this.notificationAdapter.invitationCreated(notificationInput); - } - - return invitation; - } - - @UseGuards(GraphqlGuard) - @Mutation(() => IPlatformInvitation, { - description: - 'Invite a User to join the platform and the specified Community as a member.', - }) - @Profiling.api - async inviteUserToPlatformAndCommunity( - @CurrentUser() agentInfo: AgentInfo, - @Args('invitationData') - invitationData: CreatePlatformInvitationOnCommunityInput - ): Promise { - const community = await this.communityService.getCommunityOrFail( - invitationData.roleSetID, - { - relations: { - parentCommunity: { - authorization: true, - }, - }, - } - ); - - this.authorizationService.grantAccessOrFail( - agentInfo, - community.authorization, - AuthorizationPrivilege.COMMUNITY_INVITE, - `create invitation external community: ${community.id}` - ); - - const existingUser = await this.userService.getUserByEmail( - invitationData.email, - { - relations: { - agent: true, - }, - } - ); - - if (existingUser) { - throw new CommunityInvitationException( - `User already has a profile (${existingUser.email})`, - LogContext.COMMUNITY - ); - } - - // Logic is that the ability to invite to a subspace requires the ability to invite to the - // parent community if the user is not a member there - if (community.parentCommunity) { - const parentCommunityAuthorization = - community.parentCommunity.authorization; - const canInviteToParent = this.authorizationService.isAccessGranted( - agentInfo, - parentCommunityAuthorization, - AuthorizationPrivilege.COMMUNITY_INVITE - ); - - // Not an existing user - if (!canInviteToParent) { - throw new CommunityInvitationException( - `New external user (${invitationData.email}) and the current user (${agentInfo.email}) does not have the privilege to invite to the parent community: ${community.parentCommunity.id}`, - LogContext.COMMUNITY - ); - } else { - invitationData.communityInvitedToParent = true; - } - } - - let platformInvitation = - await this.communityRoleService.createPlatformInvitation( - invitationData, - agentInfo - ); - - platformInvitation = - await this.platformInvitationService.save(platformInvitation); - const authorizations = - await this.platformInvitationAuthorizationService.applyAuthorizationPolicy( - platformInvitation, - community.authorization - ); - await this.authorizationPolicyService.save(authorizations); - - const notificationInput: NotificationInputPlatformInvitation = { - triggeredBy: agentInfo.userID, - community: community, - invitedUser: invitationData.email, - welcomeMessage: invitationData.welcomeMessage, - }; - await this.notificationAdapter.platformInvitationCreated(notificationInput); - return platformInvitation; - } - - @UseGuards(GraphqlGuard) - @Mutation(() => ICommunity, { - description: - 'Join the specified Community as a member, without going through an approval process.', - }) - @Profiling.api - async joinCommunity( - @CurrentUser() agentInfo: AgentInfo, - @Args('joinCommunityData') joiningData: CommunityJoinInput - ): Promise { - const community = await this.communityService.getCommunityOrFail( - joiningData.roleSetID - ); - const membershipStatus = - await this.communityRoleService.getMembershipStatus(agentInfo, community); - if (membershipStatus === CommunityMembershipStatus.INVITATION_PENDING) { - throw new CommunityMembershipException( - `Unable to join Community (${community.id}): invitation to join is pending.`, - LogContext.COMMUNITY - ); - } - - await this.authorizationService.grantAccessOrFail( - agentInfo, - community.authorization, - AuthorizationPrivilege.COMMUNITY_JOIN, - `join community: ${community.id}` - ); - - await this.communityRoleService.assignUserToRole( - community, - CommunityRoleType.MEMBER, - agentInfo.userID, - agentInfo, - true - ); - - return community; - } - - @UseGuards(GraphqlGuard) - @Mutation(() => IApplication, { - description: 'Trigger an event on the Application.', - }) - async eventOnApplication( - @Args('applicationEventData') - applicationEventData: ApplicationEventInput, - @CurrentUser() agentInfo: AgentInfo - ): Promise { - const application = await this.applicationService.getApplicationOrFail( - applicationEventData.applicationID - ); - await this.authorizationService.grantAccessOrFail( - agentInfo, - application.authorization, - AuthorizationPrivilege.UPDATE, - `event on application: ${application.id}` - ); - return await this.communityLifecycleApplicationOptionsProvider.eventOnApplication( - applicationEventData, - agentInfo - ); - } - - @UseGuards(GraphqlGuard) - @Mutation(() => IInvitation, { - description: 'Trigger an event on the Invitation.', - }) - async eventOnCommunityInvitation( - @Args('invitationEventData') - invitationEventData: InvitationEventInput, - @CurrentUser() agentInfo: AgentInfo - ): Promise { - const invitation = await this.invitationService.getInvitationOrFail( - invitationEventData.invitationID - ); - await this.authorizationService.grantAccessOrFail( - agentInfo, - invitation.authorization, - AuthorizationPrivilege.UPDATE, - `event on invitation: ${invitation.id}` - ); - return await this.communityLifecycleInvitationOptionsProvider.eventOnInvitation( - invitationEventData, - agentInfo - ); - } - - @UseGuards(GraphqlGuard) - @Mutation(() => AgentBeginVerifiedCredentialOfferOutput, { - description: 'Generate community member credential offer', - }) - async beginCommunityMemberVerifiedCredentialOfferInteraction( - @Args({ name: 'communityID', type: () => String }) communityID: string, - @CurrentUser() agentInfo: AgentInfo - ): Promise { - const community = - await this.communityService.getCommunityOrFail(communityID); - await this.authorizationService.grantAccessOrFail( - agentInfo, - community.authorization, - AuthorizationPrivilege.READ, - `beginCommunityMemberCredentialOfferInteraction: ${community.id}` - ); - - return await this.agentService.beginCredentialOfferInteraction( - agentInfo.agentID, - [ - { - type: 'CommunityMemberCredential', - claims: [ - new AlkemioUserClaim({ - userID: agentInfo.userID, - email: agentInfo.email, - }), - new CommunityMemberClaim({ - communityID: community.id, - communityDisplayName: community.id, - }), - ], - }, - ] - ); - } -} diff --git a/src/domain/community/community-role/community.role.service.spec.ts b/src/domain/community/community-role/community.role.service.spec.ts deleted file mode 100644 index f0a9a399b1..0000000000 --- a/src/domain/community/community-role/community.role.service.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { MockCacheManager } from '@test/mocks/cache-manager.mock'; -import { MockWinstonProvider } from '@test/mocks/winston.provider.mock'; -import { defaultMockerFactory } from '@test/utils/default.mocker.factory'; -import { repositoryProviderMockFactory } from '@test/utils/repository.provider.mock.factory'; -import { CommunityRoleService } from './community.role.service'; -import { Community } from '../community/community.entity'; - -describe('CommunityRoleService', () => { - let service: CommunityRoleService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - CommunityRoleService, - repositoryProviderMockFactory(Community), - MockCacheManager, - MockWinstonProvider, - ], - }) - .useMocker(defaultMockerFactory) - .compile(); - - service = module.get(CommunityRoleService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/src/domain/community/community-role/community.role.service.ts b/src/domain/community/community-role/community.role.service.ts deleted file mode 100644 index 757001abb6..0000000000 --- a/src/domain/community/community-role/community.role.service.ts +++ /dev/null @@ -1,1084 +0,0 @@ -import { - CreateApplicationInput, - IApplication, -} from '@domain/access/application'; -import { UserService } from '@domain/community/user/user.service'; -import { Inject, Injectable, LoggerService } from '@nestjs/common'; -import { - CommunityPolicyRoleLimitsException, - EntityNotInitializedException, - ValidationException, -} from '@common/exceptions'; -import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; -import { IUser } from '@domain/community/user/user.interface'; -import { ICommunity } from '@domain/community/community'; -import { ApplicationService } from '@domain/access/application/application.service'; -import { AgentService } from '@domain/agent/agent/agent.service'; -import { LogContext } from '@common/enums/logging.context'; -import { OrganizationService } from '../organization/organization.service'; -import { IOrganization } from '../organization/organization.interface'; -import { IAgent } from '@domain/agent/agent/agent.interface'; -import { CredentialDefinition } from '@domain/agent/credential/credential.definition'; -import { CommunityRoleType } from '@common/enums/community.role'; -import { CommunityContributorsUpdateType } from '@common/enums/community.contributors.update.type'; -import { CommunityContributorType } from '@common/enums/community.contributor.type'; -import { AgentInfo } from '@core/authentication.agent.info/agent.info'; -import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; -import { CreatePlatformInvitationOnCommunityInput } from './dto/community.role.dto.platform.invitation.community'; -import { CommunityResolverService } from '@services/infrastructure/entity-resolver/community.resolver.service'; -import { CommunityMembershipException } from '@common/exceptions/community.membership.exception'; -import { CommunityRoleEventsService } from './community.role.service.events'; -import { IVirtualContributor } from '../virtual-contributor/virtual.contributor.interface'; -import { VirtualContributorService } from '../virtual-contributor/virtual.contributor.service'; -import { CommunityRoleImplicit } from '@common/enums/community.role.implicit'; -import { AuthorizationCredential } from '@common/enums'; -import { ContributorService } from '../contributor/contributor.service'; -import { IContributor } from '../contributor/contributor.interface'; -import { PlatformInvitationService } from '@platform/invitation/platform.invitation.service'; -import { IPlatformInvitation } from '@platform/invitation'; -import { CreatePlatformInvitationInput } from '@platform/invitation/dto/platform.invitation.dto.create'; -import { AiServerAdapter } from '@services/adapters/ai-server-adapter/ai.server.adapter'; -import { CommunityService } from '../community/community.service'; -import { IRoleSet } from '@domain/access/role-set'; -import { RoleSetService } from '@domain/access/role-set/role.set.service'; -import { RoleService } from '@domain/access/role/role.service'; -import { CreateInvitationInput } from '@domain/access/invitation/dto/invitation.dto.create'; -import { IInvitation } from '@domain/access/invitation/invitation.interface'; -import { InvitationService } from '@domain/access/invitation/invitation.service'; - -@Injectable() -export class CommunityRoleService { - constructor( - private agentService: AgentService, - private userService: UserService, - private contributorService: ContributorService, - private organizationService: OrganizationService, - private virtualContributorService: VirtualContributorService, - private applicationService: ApplicationService, - private invitationService: InvitationService, - private platformInvitationService: PlatformInvitationService, - private communityResolverService: CommunityResolverService, - private communityRoleEventsService: CommunityRoleEventsService, - private communityService: CommunityService, - private roleSetService: RoleSetService, - private roleService: RoleService, - private aiServerAdapter: AiServerAdapter, //TODO: remove this asap - @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService - ) {} - - public async removeAllCommunityRoles(roleSet: IRoleSet) { - // Remove all issued role credentials for contributors - for (const roleType of Object.values(CommunityRoleType)) { - const users = await this.getUsersWithRole(roleSet, roleType); - for (const user of users) { - await this.removeUserFromRole(roleSet, roleType, user.id, false); - } - - const organizations = await this.getOrganizationsWithRole( - roleSet, - roleType - ); - for (const organization of organizations) { - await this.removeOrganizationFromRole( - roleSet, - roleType, - organization.id, - false - ); - } - } - } - - async getMembershipStatus( - agentInfo: AgentInfo, - community: ICommunity - ): Promise { - if (!agentInfo.agentID) { - return CommunityMembershipStatus.NOT_MEMBER; - } - const agent = await this.agentService.getAgentOrFail(agentInfo.agentID); - const isMember = await this.isMember(agent, community); - if (isMember) return CommunityMembershipStatus.MEMBER; - - const openApplication = await this.findOpenApplication( - agentInfo.userID, - community.id - ); - if (openApplication) return CommunityMembershipStatus.APPLICATION_PENDING; - - const openInvitation = await this.findOpenInvitation( - agentInfo.userID, - community.id - ); - - if ( - openInvitation && - (await this.invitationService.canInvitationBeAccepted(openInvitation.id)) - ) { - return CommunityMembershipStatus.INVITATION_PENDING; - } - - return CommunityMembershipStatus.NOT_MEMBER; - } - - async getCommunityRoles( - agentInfo: AgentInfo, - community: ICommunity - ): Promise { - const result: CommunityRoleType[] = []; - const agent = await this.agentService.getAgentOrFail(agentInfo.agentID); - const roles: CommunityRoleType[] = Object.values( - CommunityRoleType - ) as CommunityRoleType[]; - for (const role of roles) { - const hasAgentRole = await this.isInRole(agent, community, role); - if (hasAgentRole) { - result.push(role); - } - } - - return result; - } - - async getCommunityImplicitRoles( - agentInfo: AgentInfo, - community: ICommunity - ): Promise { - const result: CommunityRoleImplicit[] = []; - const agent = await this.agentService.getAgentOrFail(agentInfo.agentID); - - const rolesImplicit: CommunityRoleImplicit[] = Object.values( - CommunityRoleImplicit - ) as CommunityRoleImplicit[]; - for (const role of rolesImplicit) { - const hasAgentRole = await this.isInRoleImplicit(agent, community, role); - if (hasAgentRole) { - result.push(role); - } - } - return result; - } - - private async findOpenApplication( - userID: string, - roleSetID: string - ): Promise { - const applications = await this.applicationService.findExistingApplications( - userID, - roleSetID - ); - for (const application of applications) { - // skip any finalized applications; only want to return pending applications - const isFinalized = await this.applicationService.isFinalizedApplication( - application.id - ); - if (isFinalized) continue; - return application; - } - return undefined; - } - - private async findOpenInvitation( - contributorID: string, - roleSetID: string - ): Promise { - const invitations = await this.invitationService.findExistingInvitations( - contributorID, - roleSetID - ); - for (const invitation of invitations) { - // skip any finalized applications; only want to return pending applications - const isFinalized = await this.invitationService.isFinalizedInvitation( - invitation.id - ); - if (isFinalized) continue; - return invitation; - } - return undefined; - } - - async getUsersWithRole( - roleSet: IRoleSet, - roleType: CommunityRoleType, - limit?: number - ): Promise { - const membershipCredential = this.getCredentialDefinitionForRole( - roleSet, - roleType - ); - return await this.userService.usersWithCredentials( - { - type: membershipCredential.type, - resourceID: membershipCredential.resourceID, - }, - limit - ); - } - - async getVirtualContributorsWithRole( - roleSet: IRoleSet, - roleType: CommunityRoleType - ): Promise { - const membershipCredential = this.getCredentialDefinitionForRole( - roleSet, - roleType - ); - return await this.virtualContributorService.virtualContributorsWithCredentials( - { - type: membershipCredential.type, - resourceID: membershipCredential.resourceID, - } - ); - } - - async getOrganizationsWithRole( - roleSet: IRoleSet, - roleType: CommunityRoleType - ): Promise { - const membershipCredential = this.getCredentialDefinitionForRole( - roleSet, - roleType - ); - return await this.organizationService.organizationsWithCredentials({ - type: membershipCredential.type, - resourceID: membershipCredential.resourceID, - }); - } - - async countContributorsPerRole( - roleSet: IRoleSet, - roleType: CommunityRoleType, - contributorType: CommunityContributorType - ): Promise { - const membershipCredential = this.getCredentialDefinitionForRole( - roleSet, - roleType - ); - - if (contributorType === CommunityContributorType.ORGANIZATION) { - return await this.organizationService.countOrganizationsWithCredentials({ - type: membershipCredential.type, - resourceID: membershipCredential.resourceID, - }); - } - - if (contributorType === CommunityContributorType.USER) { - return await this.userService.countUsersWithCredentials({ - type: membershipCredential.type, - resourceID: membershipCredential.resourceID, - }); - } - - return 0; - } - - getCredentialDefinitionForRole( - roleSet: IRoleSet, - role: CommunityRoleType - ): CredentialDefinition { - const credential = this.roleSetService.getCredentialForRole(roleSet, role); - return credential; - } - - async assignContributorToRole( - roleSet: IRoleSet, - roleType: CommunityRoleType, - contributorID: string, - contributorType: CommunityContributorType, - agentInfo?: AgentInfo, - triggerNewMemberEvents = false - ): Promise { - switch (contributorType) { - case CommunityContributorType.USER: - return await this.assignUserToRole( - roleSet, - roleType, - contributorID, - agentInfo, - triggerNewMemberEvents - ); - case CommunityContributorType.ORGANIZATION: - return await this.assignOrganizationToRole( - roleSet, - roleType, - contributorID - ); - case CommunityContributorType.VIRTUAL: - return await this.assignVirtualToRole( - roleSet, - roleType, - contributorID, - agentInfo, - triggerNewMemberEvents - ); - default: - throw new EntityNotInitializedException( - `Invalid community contributor type: ${contributorType}`, - LogContext.ROLES - ); - } - } - - async assignUserToRole( - roleSet: IRoleSet, - roleType: CommunityRoleType, - userID: string, - agentInfo?: AgentInfo, - triggerNewMemberEvents = false - ): Promise { - const { user, agent } = await this.userService.getUserAndAgent(userID); - const { isMember: hasMemberRoleInParent, parentCommunity } = - await this.isMemberInParentCommunity(agent, roleSet.id); - if (!hasMemberRoleInParent) { - throw new ValidationException( - `Unable to assign Agent (${agent.id}) to community (${roleSet.id}): agent is not a member of parent community ${parentCommunity?.id}`, - LogContext.SPACES - ); - } - - const userAlreadyHasRole = await this.isInRole(agent, roleSet, roleType); - if (userAlreadyHasRole) { - return user; - } - - user.agent = await this.assignContributorAgentToRole( - roleSet, - roleType, - agent, - CommunityContributorType.USER - ); - if (roleType === CommunityRoleType.ADMIN && parentCommunity) { - // also assign as subspace admin in parent community if there is a parent community - const credential = this.getCredentialForImplicitRole( - parentCommunity, - CommunityRoleImplicit.SUBSPACE_ADMIN - ); - const alreadyHasSubspaceAdmin = - await this.agentService.hasValidCredential(agent.id, credential); - if (!alreadyHasSubspaceAdmin) { - await this.assignContributorToImplicitRole( - parentCommunity, - agent, - CommunityRoleImplicit.SUBSPACE_ADMIN - ); - } - } - - await this.contributorAddedToRole( - user, - roleSet, - roleType, - agentInfo, - triggerNewMemberEvents - ); - - return user; - } - - private async contributorAddedToRole( - contributor: IContributor, - roleSet: IRoleSet, - role: CommunityRoleType, - agentInfo?: AgentInfo, - triggerNewMemberEvents = false - ) { - if (role === CommunityRoleType.MEMBER) { - const community = - await this.communityResolverService.getCommunityForRoleSet(roleSet.id); - this.communityService.addMemberToCommunication(contributor, community); - - if (agentInfo) { - await this.communityRoleEventsService.registerCommunityNewMemberActivity( - community, - contributor, - agentInfo - ); - - if (triggerNewMemberEvents) { - const levelZeroSpaceID = - await this.communityService.getLevelZeroSpaceIdForCommunity( - roleSet - ); - const displayName = - await this.communityService.getDisplayName(community); - await this.communityRoleEventsService.processCommunityNewMemberEvents( - community, - levelZeroSpaceID, - displayName, - agentInfo, - contributor - ); - } - } - } - } - - async assignVirtualToRole( - roleSet: IRoleSet, - roleType: CommunityRoleType, - virtualContributorID: string, - agentInfo?: AgentInfo, - triggerNewMemberEvents = false - ): Promise { - const { virtualContributor, agent } = - await this.virtualContributorService.getVirtualContributorAndAgent( - virtualContributorID - ); - const { isMember: hasMemberRoleInParent, parentCommunity } = - await this.isMemberInParentCommunity(agent, roleSet.id); - if (!hasMemberRoleInParent) { - if (!parentCommunity) { - throw new ValidationException( - `Unable to find parent community for community ${roleSet.id}`, - LogContext.SPACES - ); - } - throw new ValidationException( - `Unable to assign Agent (${agent.id}) to community (${roleSet.id}): agent is not a member of parent community ${parentCommunity.id}`, - LogContext.SPACES - ); - } - - const virtualAlreadyHasRole = await this.isInRole(agent, roleSet, roleType); - if (virtualAlreadyHasRole) { - return virtualContributor; - } - - virtualContributor.agent = await this.assignContributorAgentToRole( - roleSet, - roleType, - agent, - CommunityContributorType.VIRTUAL - ); - - await this.contributorAddedToRole( - virtualContributor, - roleSet, - roleType, - agentInfo, - triggerNewMemberEvents - ); - // TO: THIS BREAKS THE DECOUPLING - const space = await this.communityResolverService.getSpaceForRoleSetOrFail( - roleSet.id - ); - this.aiServerAdapter.ensureContextIsLoaded(space.id); - return virtualContributor; - } - - private async isMemberInParentCommunity( - agent: IAgent, - communityID: string - ): Promise<{ parentCommunity: ICommunity | undefined; isMember: boolean }> { - const community = await this.communityService.getCommunityOrFail( - communityID, - { - relations: { parentCommunity: true }, - } - ); - - // If the parent community is set, then check if the user is also a member there - if (community.parentCommunity) { - const isParentMember = await this.isMember( - agent, - community.parentCommunity - ); - return { - parentCommunity: community?.parentCommunity, - isMember: isParentMember, - }; - } - return { - parentCommunity: undefined, - isMember: true, - }; - } - - async assignOrganizationToRole( - roleSet: IRoleSet, - roleType: CommunityRoleType, - organizationID: string - ): Promise { - const { organization, agent } = - await this.organizationService.getOrganizationAndAgent(organizationID); - - organization.agent = await this.assignContributorAgentToRole( - roleSet, - roleType, - agent, - CommunityContributorType.ORGANIZATION - ); - - return organization; - } - - async removeUserFromRole( - roleSet: IRoleSet, - roleType: CommunityRoleType, - userID: string, - validatePolicyLimits = true - ): Promise { - const { user, agent } = await this.userService.getUserAndAgent(userID); - - user.agent = await this.removeContributorFromRole( - roleSet, - roleType, - agent, - CommunityContributorType.USER, - validatePolicyLimits - ); - - const parentRoleSet = await this.roleSetService.getParentRoleSet(roleSet); - if (roleType === CommunityRoleType.ADMIN && parentRoleSet) { - // Check if an admin anywhere else in the community - const peerRoleSets = await this.roleSetService.getPeerRoleSets( - parentRoleSet, - roleSet - ); - const hasAnotherAdminRole = peerRoleSets.some(pc => - this.isInRole(agent, pc, CommunityRoleType.ADMIN) - ); - - if (!hasAnotherAdminRole) { - await this.removeContributorToImplicitRole( - parentRoleSet, - agent, - CommunityRoleImplicit.SUBSPACE_ADMIN - ); - } - } - - if (roleType === CommunityRoleType.MEMBER) { - const community = - await this.communityResolverService.getCommunityForRoleSet(roleSet.id); - - await this.communityService.removeMemberFromCommunication( - community, - user - ); - } - - return user; - } - - async removeOrganizationFromRole( - roleSet: IRoleSet, - roleType: CommunityRoleType, - organizationID: string, - validatePolicyLimits = true - ): Promise { - const { organization, agent } = - await this.organizationService.getOrganizationAndAgent(organizationID); - - organization.agent = await this.removeContributorFromRole( - roleSet, - roleType, - agent, - CommunityContributorType.ORGANIZATION, - validatePolicyLimits - ); - - return organization; - } - - async removeVirtualFromRole( - roleSet: IRoleSet, - roleType: CommunityRoleType, - virtualContributorID: string, - validatePolicyLimits = true - ): Promise { - const { virtualContributor, agent } = - await this.virtualContributorService.getVirtualContributorAndAgent( - virtualContributorID - ); - - virtualContributor.agent = await this.removeContributorFromRole( - roleSet, - roleType, - agent, - CommunityContributorType.VIRTUAL, - validatePolicyLimits - ); - - return virtualContributor; - } - - public async isCommunityAccountMatchingVcAccount( - communityID: string, - virtualContributorID: string - ): Promise { - return await this.communityService.isCommunityAccountMatchingVcAccount( - communityID, - virtualContributorID - ); - } - - private async validateUserCommunityPolicy( - roleSet: IRoleSet, - roleType: CommunityRoleType, - action: CommunityContributorsUpdateType - ) { - const userMembersCount = await this.countContributorsPerRole( - roleSet, - roleType, - CommunityContributorType.USER - ); - - const roleDefinition = this.roleSetService.getRoleDefinition( - roleSet, - roleType - ); - - const userPolicy = this.roleService.getUserPolicy(roleDefinition); - - if (action === CommunityContributorsUpdateType.ASSIGN) { - if (userMembersCount === userPolicy.maximum) { - throw new CommunityPolicyRoleLimitsException( - `Max limit of users reached for role '${roleType}': ${userPolicy.maximum}, cannot assign new user.`, - LogContext.COMMUNITY - ); - } - } - - if (action === CommunityContributorsUpdateType.REMOVE) { - if (userMembersCount === userPolicy.minimum) { - throw new CommunityPolicyRoleLimitsException( - `Min limit of users reached for role '${roleType}': ${userPolicy.minimum}, cannot remove user.`, - LogContext.COMMUNITY - ); - } - } - } - - private async validateOrganizationCommunityPolicy( - roleSet: IRoleSet, - roleType: CommunityRoleType, - action: CommunityContributorsUpdateType - ) { - const orgMemberCount = await this.countContributorsPerRole( - roleSet, - roleType, - CommunityContributorType.ORGANIZATION - ); - - const roleDefinition = this.roleSetService.getRoleDefinition( - roleSet, - roleType - ); - - const organizationPolicy = - this.roleService.getOrganizationPolicy(roleDefinition); - - if (action === CommunityContributorsUpdateType.ASSIGN) { - if (orgMemberCount === organizationPolicy.maximum) { - throw new CommunityPolicyRoleLimitsException( - `Max limit of organizations reached for role '${roleType}': ${organizationPolicy.maximum}, cannot assign new organization.`, - LogContext.COMMUNITY - ); - } - } - - if (action === CommunityContributorsUpdateType.REMOVE) { - if (orgMemberCount === organizationPolicy.minimum) { - throw new CommunityPolicyRoleLimitsException( - `Min limit of organizations reached for role '${roleType}': ${organizationPolicy.minimum}, cannot remove organization.`, - LogContext.COMMUNITY - ); - } - } - } - - private async validateCommunityPolicyLimits( - roleSet: IRoleSet, - roleType: CommunityRoleType, - action: CommunityContributorsUpdateType, - contributorType: CommunityContributorType - ) { - if (contributorType === CommunityContributorType.USER) - await this.validateUserCommunityPolicy(roleSet, roleType, action); - - if (contributorType === CommunityContributorType.ORGANIZATION) - await this.validateOrganizationCommunityPolicy(roleSet, roleType, action); - } - - public async assignContributorAgentToRole( - roleSet: IRoleSet, - roleType: CommunityRoleType, - agent: IAgent, - contributorType: CommunityContributorType - ): Promise { - await this.validateCommunityPolicyLimits( - roleSet, - roleType, - CommunityContributorsUpdateType.ASSIGN, - contributorType - ); - - const roleCredential = this.roleSetService.getCredentialForRole( - roleSet, - roleType - ); - - return await this.agentService.grantCredential({ - agentID: agent.id, - type: roleCredential.type, - resourceID: roleCredential.resourceID, - }); - } - - private async assignContributorToImplicitRole( - roleSet: IRoleSet, - agent: IAgent, - role: CommunityRoleImplicit - ): Promise { - const credential = this.getCredentialForImplicitRole(roleSet, role); - - return await this.agentService.grantCredential({ - agentID: agent.id, - type: credential.type, - resourceID: credential.resourceID, - }); - } - - private async removeContributorToImplicitRole( - roleSet: IRoleSet, - agent: IAgent, - role: CommunityRoleImplicit - ): Promise { - const credential = this.getCredentialForImplicitRole(roleSet, role); - - return await this.agentService.revokeCredential({ - agentID: agent.id, - type: credential.type, - resourceID: credential.resourceID, - }); - } - - private getCredentialForImplicitRole( - roleSet: IRoleSet, - role: CommunityRoleImplicit - ): CredentialDefinition { - // Use the admin credential to get the resourceID - const adminCredential = this.getCredentialDefinitionForRole( - roleSet, - CommunityRoleType.ADMIN - ); - const resourceID = adminCredential.resourceID; - switch (role) { - case CommunityRoleImplicit.SUBSPACE_ADMIN: - return { - type: AuthorizationCredential.SPACE_SUBSPACE_ADMIN, - resourceID, - }; - default: { - throw new CommunityMembershipException( - `Invalid implicit role: ${role}`, - LogContext.COMMUNITY - ); - } - } - } - - private async removeContributorFromRole( - roleSet: IRoleSet, - roleType: CommunityRoleType, - agent: IAgent, - contributorType: CommunityContributorType, - validatePolicyLimits: boolean - ): Promise { - if (validatePolicyLimits) { - await this.validateCommunityPolicyLimits( - roleSet, - roleType, - CommunityContributorsUpdateType.REMOVE, - contributorType - ); - } - - const roleCredential = this.roleSetService.getCredentialForRole( - roleSet, - roleType - ); - - return await this.agentService.revokeCredential({ - agentID: agent.id, - type: roleCredential.type, - resourceID: roleCredential.resourceID, - }); - } - - async isMember(agent: IAgent, roleSet: IRoleSet): Promise { - const membershipCredential = this.getCredentialDefinitionForRole( - roleSet, - CommunityRoleType.MEMBER - ); - - const validCredential = await this.agentService.hasValidCredential( - agent.id, - { - type: membershipCredential.type, - resourceID: membershipCredential.resourceID, - } - ); - return validCredential; - } - - async isInRole( - agent: IAgent, - roleSet: IRoleSet, - role: CommunityRoleType - ): Promise { - const membershipCredential = this.getCredentialDefinitionForRole( - roleSet, - role - ); - - const validCredential = await this.agentService.hasValidCredential( - agent.id, - { - type: membershipCredential.type, - resourceID: membershipCredential.resourceID, - } - ); - return validCredential; - } - - async isInRoleImplicit( - agent: IAgent, - roleSet: IRoleSet, - role: CommunityRoleImplicit - ): Promise { - const membershipCredential = this.getCredentialForImplicitRole( - roleSet, - role - ); - - const validCredential = await this.agentService.hasValidCredential( - agent.id, - { - type: membershipCredential.type, - resourceID: membershipCredential.resourceID, - } - ); - return validCredential; - } - - async createApplication( - applicationData: CreateApplicationInput - ): Promise { - const { user, agent } = await this.userService.getUserAndAgent( - applicationData.userID - ); - const roleSet = await this.roleSetService.getRoleSetOrFail( - applicationData.roleSetID, - { - relations: { - parentRoleSet: true, - }, - } - ); - - await this.validateApplicationFromUser(user, agent, roleSet); - - const application = - await this.applicationService.createApplication(applicationData); - application.roleSet = roleSet; - return await this.applicationService.save(application); - } - - async createInvitationExistingContributor( - invitationData: CreateInvitationInput - ): Promise { - const { contributor: contributor, agent } = - await this.contributorService.getContributorAndAgent( - invitationData.invitedContributor - ); - const roleSet = await this.roleSetService.getRoleSetOrFail( - invitationData.roleSetID - ); - - await this.validateInvitationToExistingContributor( - contributor, - agent, - roleSet - ); - - const invitation = await this.invitationService.createInvitation( - invitationData, - contributor - ); - invitation.roleSet = roleSet; - - return await this.invitationService.save(invitation); - } - - async createPlatformInvitation( - invitationData: CreatePlatformInvitationOnCommunityInput, - agentInfo: AgentInfo - ): Promise { - await this.validateInvitationToExternalUser( - invitationData.email, - invitationData.roleSetID - ); - const roleSet = await this.roleSetService.getRoleSetOrFail( - invitationData.roleSetID, - { - relations: {}, - } - ); - - const externalInvitationInput: CreatePlatformInvitationInput = { - ...invitationData, - createdBy: agentInfo.userID, - }; - const externalInvitation = - await this.platformInvitationService.createPlatformInvitation( - externalInvitationInput - ); - externalInvitation.roleSet = roleSet; - return await this.platformInvitationService.save(externalInvitation); - } - - private async validateApplicationFromUser( - user: IUser, - agent: IAgent, - roleSet: IRoleSet - ) { - const openApplication = await this.findOpenApplication(user.id, roleSet.id); - if (openApplication) { - throw new CommunityMembershipException( - `Application not possible: An open application (ID: ${openApplication.id}) already exists for contributor ${openApplication.user?.id} on Community: ${roleSet.id}.`, - LogContext.COMMUNITY - ); - } - - const openInvitation = await this.findOpenInvitation(user.id, roleSet.id); - if (openInvitation) { - throw new CommunityMembershipException( - `Application not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on Community: ${roleSet.id}.`, - LogContext.COMMUNITY - ); - } - - // Check if the user is already a member; if so do not allow an application - const isExistingMember = await this.isMember(agent, roleSet); - if (isExistingMember) - throw new CommunityMembershipException( - `Application not possible: Contributor ${user.id} is already a member of the Community: ${roleSet.id}.`, - LogContext.COMMUNITY - ); - } - - private async validateInvitationToExistingContributor( - contributor: IContributor, - agent: IAgent, - roleSet: IRoleSet - ) { - const openInvitation = await this.findOpenInvitation( - contributor.id, - roleSet.id - ); - if (openInvitation) { - throw new CommunityMembershipException( - `Invitation not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on Community: ${roleSet.id}.`, - LogContext.COMMUNITY - ); - } - - const openApplication = await this.findOpenApplication( - contributor.id, - roleSet.id - ); - if (openApplication) { - throw new CommunityMembershipException( - `Invitation not possible: An open application (ID: ${openApplication.id}) already exists for contributor ${openApplication.user?.id} on Community: ${roleSet.id}.`, - LogContext.COMMUNITY - ); - } - - // Check if the user is already a member; if so do not allow an application - const isExistingMember = await this.isMember(agent, roleSet); - if (isExistingMember) - throw new CommunityMembershipException( - `Invitation not possible: Contributor ${contributor.id} is already a member of the Community: ${roleSet.id}.`, - LogContext.COMMUNITY - ); - } - - private async validateInvitationToExternalUser( - email: string, - roleSetID: string - ) { - // Check if a user with the provided email address already exists or not - const isExistingUser = await this.userService.isRegisteredUser(email); - if (isExistingUser) { - throw new CommunityMembershipException( - `User with the provided email address already exists: ${email}`, - LogContext.COMMUNITY - ); - } - - const platformInvitations = - await this.platformInvitationService.findPlatformInvitationsForUser( - email - ); - for (const platformInvitation of platformInvitations) { - if ( - platformInvitation.roleSet && - platformInvitation.roleSet.id === roleSetID - ) { - throw new CommunityMembershipException( - `An invitation with the provided email address (${email}) already exists for the specified community: ${roleSetID}`, - LogContext.COMMUNITY - ); - } - } - } - - async getApplications(roleSet: IRoleSet): Promise { - const communityApplications = await this.roleSetService.getRoleSetOrFail( - roleSet.id, - { - relations: { applications: true }, - } - ); - return communityApplications?.applications || []; - } - - async getInvitations(roleSet: IRoleSet): Promise { - const roleSetInvitations = await this.roleSetService.getRoleSetOrFail( - roleSet.id, - { - relations: { invitations: true }, - } - ); - return roleSetInvitations?.invitations || []; - } - - async getPlatformInvitations( - roleSet: IRoleSet - ): Promise { - const communityInvs = await this.roleSetService.getRoleSetOrFail( - roleSet.id, - { - relations: { platformInvitations: true }, - } - ); - return communityInvs?.platformInvitations || []; - } - - async getMembersCount(community: ICommunity): Promise { - const membershipCredential = this.getCredentialDefinitionForRole( - community, - CommunityRoleType.MEMBER - ); - - const credentialMatches = - await this.agentService.countAgentsWithMatchingCredentials({ - type: membershipCredential.type, - resourceID: membershipCredential.resourceID, - }); - - return credentialMatches; - } -} diff --git a/src/domain/community/community-role/index.ts b/src/domain/community/community-role/index.ts deleted file mode 100644 index 9be83bbd04..0000000000 --- a/src/domain/community/community-role/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './dto/community.role.dto.role.assign.user'; -export * from './dto/community.role.dto.role.remove.user'; diff --git a/src/domain/community/community/community.resolver.fields.ts b/src/domain/community/community/community.resolver.fields.ts index 7f41b7e60f..4fff486615 100644 --- a/src/domain/community/community/community.resolver.fields.ts +++ b/src/domain/community/community/community.resolver.fields.ts @@ -1,7 +1,11 @@ import { GraphqlGuard } from '@core/authorization'; import { UseGuards } from '@nestjs/common'; import { Parent, ResolveField, Resolver, Args } from '@nestjs/graphql'; -import { AuthorizationAgentPrivilege, Profiling } from '@src/common/decorators'; +import { + AuthorizationAgentPrivilege, + CurrentUser, + Profiling, +} from '@src/common/decorators'; import { Community, ICommunity } from '@domain/community/community'; import { CommunityService } from './community.service'; import { IUserGroup } from '@domain/community/user-group'; @@ -10,10 +14,18 @@ import { ICommunication } from '@domain/communication/communication/communicatio import { UUID } from '@domain/common/scalars/scalar.uuid'; import { ICommunityGuidelines } from '../community-guidelines/community.guidelines.interface'; import { IRoleSet } from '@domain/access/role-set'; +import { AgentInfo } from '@core/authentication.agent.info/agent.info'; +import { CommunityRoleImplicit } from '@common/enums/community.role.implicit'; +import { RoleSetService } from '@domain/access/role-set/role.set.service'; +import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; +import { CommunityRoleType } from '@common/enums/community.role'; @Resolver(() => ICommunity) export class CommunityResolverFields { - constructor(private communityService: CommunityService) {} + constructor( + private communityService: CommunityService, + private roleSetService: RoleSetService + ) {} @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) @UseGuards(GraphqlGuard) @@ -72,4 +84,42 @@ export class CommunityResolverFields { ): Promise { return await this.communityService.getCommunityGuidelines(community); } + + @UseGuards(GraphqlGuard) + @ResolveField('myMembershipStatus', () => CommunityMembershipStatus, { + nullable: true, + description: 'The membership status of the currently logged in user.', + }) + async myMembershipStatus( + @CurrentUser() agentInfo: AgentInfo, + @Parent() community: ICommunity + ): Promise { + return this.roleSetService.getMembershipStatus(agentInfo, community); + } + + @UseGuards(GraphqlGuard) + @ResolveField('myRoles', () => [CommunityRoleType], { + nullable: false, + description: + 'The roles on this community for the currently logged in user.', + }) + async myRoles( + @CurrentUser() agentInfo: AgentInfo, + @Parent() community: ICommunity + ): Promise { + return this.roleSetService.getCommunityRoles(agentInfo, community); + } + + @UseGuards(GraphqlGuard) + @ResolveField('myRolesImplicit', () => [CommunityRoleImplicit], { + nullable: false, + description: + 'The implicit roles on this community for the currently logged in user.', + }) + async myRolesImplicit( + @CurrentUser() agentInfo: AgentInfo, + @Parent() community: ICommunity + ): Promise { + return this.roleSetService.getCommunityImplicitRoles(agentInfo, community); + } } diff --git a/src/domain/community/community/community.service.authorization.ts b/src/domain/community/community/community.service.authorization.ts index be53b5e7f6..d956123f6b 100644 --- a/src/domain/community/community/community.service.authorization.ts +++ b/src/domain/community/community/community.service.authorization.ts @@ -1,6 +1,5 @@ import { Injectable } from '@nestjs/common'; import { CommunityService } from './community.service'; -import { ICommunity } from '@domain/community/community'; import { AuthorizationCredential, AuthorizationPrivilege, @@ -14,13 +13,11 @@ import { AuthorizationPolicyRuleVerifiedCredential } from '@core/authorization/a import { IAuthorizationPolicyRuleCredential } from '@core/authorization/authorization.policy.rule.credential.interface'; import { CREDENTIAL_RULE_TYPES_COMMUNITY_READ_GLOBAL_REGISTERED, - CREDENTIAL_RULE_COMMUNITY_SELF_REMOVAL, CREDENTIAL_RULE_TYPES_ACCESS_VIRTUAL_CONTRIBUTORS, CREDENTIAL_RULE_TYPES_COMMUNITY_ADD_MEMBERS, CREDENTIAL_RULE_TYPES_COMMUNITY_INVITE_MEMBERS, POLICY_RULE_COMMUNITY_ADD_VC, POLICY_RULE_COMMUNITY_INVITE_MEMBER, - CREDENTIAL_RULE_COMMUNITY_VIRTUAL_CONTRIBUTOR_REMOVAL, CREDENTIAL_RULE_SPACE_HOST_ASSOCIATES_JOIN, CREDENTIAL_RULE_TYPES_SPACE_COMMUNITY_JOIN_GLOBAL_REGISTERED, CREDENTIAL_RULE_TYPES_SPACE_COMMUNITY_APPLY_GLOBAL_REGISTERED, @@ -411,72 +408,6 @@ export class CommunityAuthorizationService { ); } - public extendAuthorizationPolicyForSelfRemoval( - community: ICommunity, - userToBeRemovedID: string - ): IAuthorizationPolicy { - const newRules: IAuthorizationPolicyRuleCredential[] = []; - - const userSelfRemovalRule = - this.authorizationPolicyService.createCredentialRule( - [AuthorizationPrivilege.GRANT], - [ - { - type: AuthorizationCredential.USER_SELF_MANAGEMENT, - resourceID: userToBeRemovedID, - }, - ], - CREDENTIAL_RULE_COMMUNITY_SELF_REMOVAL - ); - newRules.push(userSelfRemovalRule); - - const clonedCommunityAuthorization = - this.authorizationPolicyService.cloneAuthorizationPolicy( - community.authorization - ); - - const updatedAuthorization = - this.authorizationPolicyService.appendCredentialAuthorizationRules( - clonedCommunityAuthorization, - newRules - ); - - return updatedAuthorization; - } - - public async extendAuthorizationPolicyForVirtualContributorRemoval( - community: ICommunity, - virtualContributorToBeRemoved: string - ): Promise { - const newRules: IAuthorizationPolicyRuleCredential[] = []; - - const accountHostCredentials = - await this.virtualContributorService.getAccountHostCredentials( - virtualContributorToBeRemoved - ); - - const userSelfRemovalRule = - this.authorizationPolicyService.createCredentialRule( - [AuthorizationPrivilege.GRANT], - accountHostCredentials, - CREDENTIAL_RULE_COMMUNITY_VIRTUAL_CONTRIBUTOR_REMOVAL - ); - newRules.push(userSelfRemovalRule); - - const clonedCommunityAuthorization = - this.authorizationPolicyService.cloneAuthorizationPolicy( - community.authorization - ); - - const updatedAuthorization = - this.authorizationPolicyService.appendCredentialAuthorizationRules( - clonedCommunityAuthorization, - newRules - ); - - return updatedAuthorization; - } - private appendPrivilegeRules( authorization: IAuthorizationPolicy ): IAuthorizationPolicy { diff --git a/src/domain/space/space/space.module.ts b/src/domain/space/space/space.module.ts index 1d5e2c5a90..526f2e468f 100644 --- a/src/domain/space/space/space.module.ts +++ b/src/domain/space/space/space.module.ts @@ -24,7 +24,6 @@ import { PlatformAuthorizationPolicyModule } from '@platform/authorization/platf import { NamingModule } from '@services/infrastructure/naming/naming.module'; import { SpaceDefaultsModule } from '../space.defaults/space.defaults.module'; import { SpaceSettingssModule } from '../space.settings/space.settings.module'; -import { CommunityRoleModule } from '@domain/community/community-role/community.role.module'; import { TemplatesSetModule } from '@domain/template/templates-set/templates.set.module'; import { AccountHostModule } from '../account.host/account.host.module'; import { LicensingModule } from '@platform/licensing/licensing.module'; @@ -42,7 +41,6 @@ import { RoleSetModule } from '@domain/access/role-set/role.set.module'; AuthorizationModule, ContextModule, CommunityModule, - CommunityRoleModule, ProfileModule, LicensingModule, LicenseIssuerModule, diff --git a/src/domain/space/space/space.service.ts b/src/domain/space/space/space.service.ts index e8e08c81af..a6dd36e222 100644 --- a/src/domain/space/space/space.service.ts +++ b/src/domain/space/space/space.service.ts @@ -57,7 +57,6 @@ import { SpaceLevel } from '@common/enums/space.level'; import { UpdateSpaceSettingsInput } from './dto/space.dto.update.settings'; import { IContributor } from '@domain/community/contributor/contributor.interface'; import { CommunityContributorType } from '@common/enums/community.contributor.type'; -import { CommunityRoleService } from '@domain/community/community-role/community.role.service'; import { IStorageAggregator } from '@domain/storage/storage-aggregator/storage.aggregator.interface'; import { TemplatesSetService } from '@domain/template/templates-set/templates.set.service'; import { ITemplatesSet } from '@domain/template/templates-set/templates.set.interface'; @@ -90,7 +89,7 @@ export class SpaceService { private contextService: ContextService, private agentService: AgentService, private communityService: CommunityService, - private communityRoleService: CommunityRoleService, + private roleSetService: RoleSetService, private namingService: NamingService, private profileService: ProfileService, private spaceSettingsService: SpaceSettingsService, @@ -101,7 +100,6 @@ export class SpaceService { private licensingService: LicensingService, private licenseEngineService: LicenseEngineService, private templateService: TemplateService, - private roleSetService: RoleSetService, private inputCreatorService: InputCreatorService, @InjectRepository(Space) private spaceRepository: Repository, @@ -947,7 +945,7 @@ export class SpaceService { // Before assigning roles in the subspace check that the user is a member if (agentInfo) { const agent = await this.agentService.getAgentOrFail(agentInfo?.agentID); - const isMember = await this.communityRoleService.isMember( + const isMember = await this.roleSetService.isMember( agent, space.community ); @@ -1006,7 +1004,7 @@ export class SpaceService { ); } - await this.communityRoleService.assignContributorAgentToRole( + await this.roleSetService.assignContributorAgentToRole( space.community, role, contributor.agent, @@ -1037,21 +1035,21 @@ export class SpaceService { ); } if (agentInfo) { - await this.communityRoleService.assignUserToRole( + await this.roleSetService.assignUserToRole( space.community, CommunityRoleType.MEMBER, agentInfo.userID, agentInfo ); - await this.communityRoleService.assignUserToRole( + await this.roleSetService.assignUserToRole( space.community, CommunityRoleType.LEAD, agentInfo.userID, agentInfo ); - await this.communityRoleService.assignUserToRole( + await this.roleSetService.assignUserToRole( space.community, CommunityRoleType.ADMIN, agentInfo.userID, @@ -1120,7 +1118,7 @@ export class SpaceService { await this.contextService.removeContext(space.context.id); await this.collaborationService.deleteCollaboration(space.collaboration.id); - await this.communityRoleService.removeAllCommunityRoles(space.community); + await this.roleSetService.removeAllCommunityRoles(space.community); await this.communityService.removeCommunity(space.community.id); await this.profileService.deleteProfile(space.profile.id); await this.agentService.deleteAgent(space.agent.id); @@ -1425,7 +1423,7 @@ export class SpaceService { public async getMembersCount(space: ISpace): Promise { const community = await this.getCommunity(space.id); - return await this.communityRoleService.getMembersCount(community); + return await this.roleSetService.getMembersCount(community); } public async getPostsCount(space: ISpace): Promise { @@ -1469,8 +1467,7 @@ export class SpaceService { const community = await this.getCommunity(space.id); // Members - const membersCount = - await this.communityRoleService.getMembersCount(community); + const membersCount = await this.roleSetService.getMembersCount(community); const membersTopic = new NVP('members', membersCount.toString()); membersTopic.id = `members-${space.id}`; metrics.push(membersTopic); diff --git a/src/platform/admin/communication/admin.communication.module.ts b/src/platform/admin/communication/admin.communication.module.ts index d0d092d455..ffd5dcd6c6 100644 --- a/src/platform/admin/communication/admin.communication.module.ts +++ b/src/platform/admin/communication/admin.communication.module.ts @@ -7,14 +7,14 @@ import { AdminCommunicationResolverMutations } from './admin.communication.resol import { AdminCommunicationResolverQueries } from './admin.communication.resolver.queries'; import { CommunicationModule } from '@domain/communication/communication/communication.module'; import { CommunityModule } from '@domain/community/community/community.module'; -import { CommunityRoleModule } from '@domain/community/community-role/community.role.module'; +import { RoleSetModule } from '@domain/access/role-set/role.set.module'; @Module({ imports: [ AuthorizationModule, AuthorizationPolicyModule, CommunityModule, - CommunityRoleModule, + RoleSetModule, CommunicationModule, CommunicationAdapterModule, ], diff --git a/src/platform/admin/communication/admin.communication.service.ts b/src/platform/admin/communication/admin.communication.service.ts index fd8d9b8214..a21f91fbf7 100644 --- a/src/platform/admin/communication/admin.communication.service.ts +++ b/src/platform/admin/communication/admin.communication.service.ts @@ -15,7 +15,7 @@ import { CommunicationAdminRemoveOrphanedRoomInput } from './dto/admin.communica import { ValidationException } from '@common/exceptions'; import { CommunityRoleType } from '@common/enums/community.role'; import { IRoom } from '@domain/communication/room/room.interface'; -import { CommunityRoleService } from '@domain/community/community-role/community.role.service'; +import { RoleSetService } from '@domain/access/role-set/role.set.service'; @Injectable() export class AdminCommunicationService { @@ -23,7 +23,7 @@ export class AdminCommunicationService { private communicationAdapter: CommunicationAdapter, private communicationService: CommunicationService, private communityService: CommunityService, - private communityRoleService: CommunityRoleService, + private roleSetService: RoleSetService, @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService ) {} @@ -37,7 +37,7 @@ export class AdminCommunicationService { const community = await this.communityService.getCommunityOrFail( communicationData.communityID ); - const communityMembers = await this.communityRoleService.getUsersWithRole( + const communityMembers = await this.roleSetService.getUsersWithRole( community, CommunityRoleType.MEMBER ); @@ -108,7 +108,7 @@ export class AdminCommunicationService { const communication = await this.communityService.getCommunication( community.id ); - const communityMembers = await this.communityRoleService.getUsersWithRole( + const communityMembers = await this.roleSetService.getUsersWithRole( community, CommunityRoleType.MEMBER ); diff --git a/src/services/api/conversion/conversion.module.ts b/src/services/api/conversion/conversion.module.ts index f9459a3a34..7d61922e32 100644 --- a/src/services/api/conversion/conversion.module.ts +++ b/src/services/api/conversion/conversion.module.ts @@ -7,8 +7,8 @@ import { AuthorizationPolicyModule } from '@domain/common/authorization-policy/a import { CommunicationModule } from '@domain/communication/communication/communication.module'; import { AccountModule } from '@domain/space/account/account.module'; import { NamingModule } from '@services/infrastructure/naming/naming.module'; -import { CommunityRoleModule } from '@domain/community/community-role/community.role.module'; import { CommunityModule } from '@domain/community/community/community.module'; +import { RoleSetModule } from '@domain/access/role-set/role.set.module'; @Module({ imports: [ @@ -16,7 +16,7 @@ import { CommunityModule } from '@domain/community/community/community.module'; AccountModule, SpaceModule, CommunityModule, - CommunityRoleModule, + RoleSetModule, AuthorizationModule, AuthorizationPolicyModule, NamingModule, diff --git a/src/services/api/conversion/conversion.service.ts b/src/services/api/conversion/conversion.service.ts index d8aa447852..933585d387 100644 --- a/src/services/api/conversion/conversion.service.ts +++ b/src/services/api/conversion/conversion.service.ts @@ -22,10 +22,10 @@ import { SpaceService } from '@domain/space/space/space.service'; import { CreateSubspaceInput } from '@domain/space/space/dto/space.dto.create.subspace'; import { NamingService } from '@services/infrastructure/naming/naming.service'; import { SpaceLevel } from '@common/enums/space.level'; -import { CommunityRoleService } from '@domain/community/community-role/community.role.service'; import { CommunityService } from '@domain/community/community/community.service'; import { CreateSpaceOnAccountInput } from '@domain/space/account/dto/account.dto.create.space'; import { IRoleSet } from '@domain/access/role-set'; +import { RoleSetService } from '@domain/access/role-set/role.set.service'; export class ConversionService { constructor( @@ -33,7 +33,7 @@ export class ConversionService { private spaceService: SpaceService, private namingService: NamingService, private communityService: CommunityService, - private communityRoleService: CommunityRoleService, + private roleSetService: RoleSetService, private communicationService: CommunicationService, @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService ) {} @@ -83,7 +83,7 @@ export class ConversionService { // check the community is in a fit state const challengeCommunityLeadOrgs = - await this.communityRoleService.getOrganizationsWithRole( + await this.roleSetService.getOrganizationsWithRole( subspace.community, CommunityRoleType.LEAD ); @@ -130,15 +130,15 @@ export class ConversionService { } const spaceRoleSet = space.community.roleSet; - const userMembers = await this.communityRoleService.getUsersWithRole( + const userMembers = await this.roleSetService.getUsersWithRole( spaceRoleSet, CommunityRoleType.MEMBER ); - const userLeads = await this.communityRoleService.getUsersWithRole( + const userLeads = await this.roleSetService.getUsersWithRole( spaceRoleSet, CommunityRoleType.LEAD ); - const orgMembers = await this.communityRoleService.getOrganizationsWithRole( + const orgMembers = await this.roleSetService.getOrganizationsWithRole( spaceRoleSet, CommunityRoleType.MEMBER ); @@ -152,7 +152,7 @@ export class ConversionService { challengeCommunityLeadOrgs ); - await this.communityRoleService.removeUserFromRole( + await this.roleSetService.removeUserFromRole( spaceRoleSet, CommunityRoleType.MEMBER, agentInfo.userID @@ -308,19 +308,19 @@ export class ConversionService { } const roleSet = subsubspace.community.roleSet; - const userMembers = await this.communityRoleService.getUsersWithRole( + const userMembers = await this.roleSetService.getUsersWithRole( roleSet, CommunityRoleType.MEMBER ); - const userLeads = await this.communityRoleService.getUsersWithRole( + const userLeads = await this.roleSetService.getUsersWithRole( roleSet, CommunityRoleType.LEAD ); - const orgMembers = await this.communityRoleService.getOrganizationsWithRole( + const orgMembers = await this.roleSetService.getOrganizationsWithRole( roleSet, CommunityRoleType.MEMBER ); - const orgLeads = await this.communityRoleService.getOrganizationsWithRole( + const orgLeads = await this.roleSetService.getOrganizationsWithRole( roleSet, CommunityRoleType.LEAD ); @@ -335,12 +335,12 @@ export class ConversionService { ); // also remove the current user from the members of the newly created Challenge, otherwise will end up re-assigning - await this.communityRoleService.removeUserFromRole( + await this.roleSetService.removeUserFromRole( subspace.community.roleSet, CommunityRoleType.MEMBER, agentInfo.userID ); - await this.communityRoleService.removeUserFromRole( + await this.roleSetService.removeUserFromRole( subspace.community.roleSet, CommunityRoleType.LEAD, agentInfo.userID @@ -528,28 +528,28 @@ export class ConversionService { orgLeads: IOrganization[] ) { for (const userMember of userMembers) { - await this.communityRoleService.removeUserFromRole( + await this.roleSetService.removeUserFromRole( roleSet, CommunityRoleType.MEMBER, userMember.id ); } for (const userLead of userLeads) { - await this.communityRoleService.removeUserFromRole( + await this.roleSetService.removeUserFromRole( roleSet, CommunityRoleType.LEAD, userLead.id ); } for (const orgMember of orgMembers) { - await this.communityRoleService.removeOrganizationFromRole( + await this.roleSetService.removeOrganizationFromRole( roleSet, CommunityRoleType.MEMBER, orgMember.id ); } for (const orgLead of orgLeads) { - await this.communityRoleService.removeOrganizationFromRole( + await this.roleSetService.removeOrganizationFromRole( roleSet, CommunityRoleType.LEAD, orgLead.id @@ -565,21 +565,21 @@ export class ConversionService { orgLeads?: IOrganization[] ) { for (const userMember of userMembers) { - await this.communityRoleService.assignUserToRole( + await this.roleSetService.assignUserToRole( roleSet, CommunityRoleType.MEMBER, userMember.id ); } for (const userLead of userLeads) { - await this.communityRoleService.assignUserToRole( + await this.roleSetService.assignUserToRole( roleSet, CommunityRoleType.LEAD, userLead.id ); } for (const orgMember of orgMembers) { - await this.communityRoleService.assignOrganizationToRole( + await this.roleSetService.assignOrganizationToRole( roleSet, CommunityRoleType.MEMBER, orgMember.id @@ -587,7 +587,7 @@ export class ConversionService { } if (orgLeads) { for (const orgLead of orgLeads) { - await this.communityRoleService.assignOrganizationToRole( + await this.roleSetService.assignOrganizationToRole( roleSet, CommunityRoleType.LEAD, orgLead.id diff --git a/src/services/api/me/me.module.ts b/src/services/api/me/me.module.ts index d3c67e735a..a466bf55e6 100644 --- a/src/services/api/me/me.module.ts +++ b/src/services/api/me/me.module.ts @@ -11,7 +11,6 @@ import { RolesModule } from '../roles/roles.module'; import { ActivityLogModule } from '../activity-log/activity.log.module'; import { ActivityModule } from '@platform/activity/activity.module'; import { EntityResolverModule } from '@services/infrastructure/entity-resolver/entity.resolver.module'; -import { CommunityRoleModule } from '@domain/community/community-role/community.role.module'; import { ContributorModule } from '@domain/community/contributor/contributor.module'; @Module({ @@ -25,7 +24,6 @@ import { ContributorModule } from '@domain/community/contributor/contributor.mod RolesModule, ActivityLogModule, ActivityModule, - CommunityRoleModule, EntityResolverModule, ], providers: [MeService, MeResolverQueries, MeResolverFields], diff --git a/src/services/api/registration/registration.module.ts b/src/services/api/registration/registration.module.ts index 3f5bfb273e..3892b8687f 100644 --- a/src/services/api/registration/registration.module.ts +++ b/src/services/api/registration/registration.module.ts @@ -10,11 +10,11 @@ import { ApplicationModule } from '@domain/access/application/application.module import { PreferenceSetModule } from '@domain/common/preference-set/preference.set.module'; import { PlatformInvitationModule } from '@platform/invitation/platform.invitation.module'; import { PlatformRoleModule } from '@platform/platfrom.role/platform.role.module'; -import { CommunityRoleModule } from '@domain/community/community-role/community.role.module'; import { OrganizationRoleModule } from '@domain/community/organization-role/organization.role.module'; import { PlatformAuthorizationPolicyModule } from '@platform/authorization/platform.authorization.policy.module'; import { AuthorizationPolicyModule } from '@domain/common/authorization-policy/authorization.policy.module'; import { AccountModule } from '@domain/space/account/account.module'; +import { RoleSetModule } from '@domain/access/role-set/role.set.module'; @Module({ imports: [ @@ -22,7 +22,7 @@ import { AccountModule } from '@domain/space/account/account.module'; AuthorizationModule, AuthorizationPolicyModule, NotificationAdapterModule, - CommunityRoleModule, + RoleSetModule, UserModule, PreferenceSetModule, OrganizationModule, diff --git a/src/services/api/registration/registration.service.ts b/src/services/api/registration/registration.service.ts index 1b61895fc0..76aefe4083 100644 --- a/src/services/api/registration/registration.service.ts +++ b/src/services/api/registration/registration.service.ts @@ -19,11 +19,11 @@ import { ApplicationService } from '@domain/access/application/application.servi import { OrganizationRole } from '@common/enums/organization.role'; import { PlatformInvitationService } from '@platform/invitation/platform.invitation.service'; import { PlatformRoleService } from '@platform/platfrom.role/platform.role.service'; -import { CommunityRoleService } from '@domain/community/community-role/community.role.service'; import { OrganizationRoleService } from '@domain/community/organization-role/organization.role.service'; import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service'; import { AccountService } from '@domain/space/account/account.service'; import { IOrganization } from '@domain/community/organization'; +import { RoleSetService } from '@domain/access/role-set/role.set.service'; export class RegistrationService { constructor( @@ -33,12 +33,12 @@ export class RegistrationService { private organizationService: OrganizationService, private organizationRoleService: OrganizationRoleService, private preferenceSetService: PreferenceSetService, - private communityRoleService: CommunityRoleService, private platformInvitationService: PlatformInvitationService, private platformRoleService: PlatformRoleService, private invitationAuthorizationService: InvitationAuthorizationService, private invitationService: InvitationService, private applicationService: ApplicationService, + private roleSetService: RoleSetService, @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService ) {} @@ -130,7 +130,7 @@ export class RegistrationService { invitedToParent: platformInvitation.communityInvitedToParent, }; let invitation = - await this.communityRoleService.createInvitationExistingContributor( + await this.roleSetService.createInvitationExistingContributor( invitationInput ); invitation.invitedToParent = From 9b97770a016df747c607a75517e800d33701dcad Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Mon, 16 Sep 2024 17:58:17 +0100 Subject: [PATCH 08/78] addressed module imports for RoleSet --- src/domain/access/role-set/role.set.module.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/domain/access/role-set/role.set.module.ts b/src/domain/access/role-set/role.set.module.ts index ac8bae6d3d..1e7bc9580c 100644 --- a/src/domain/access/role-set/role.set.module.ts +++ b/src/domain/access/role-set/role.set.module.ts @@ -14,6 +14,19 @@ import { InvitationModule } from '@domain/access/invitation/invitation.module'; import { ApplicationModule } from '@domain/access/application/application.module'; import { VirtualContributorModule } from '@domain/community/virtual-contributor/virtual.contributor.module'; import { RoleModule } from '../role/role.module'; +import { AgentModule } from '@domain/agent/agent/agent.module'; +import { UserModule } from '@domain/community/user/user.module'; +import { OrganizationModule } from '@domain/community/organization/organization.module'; +import { ContributorModule } from '@domain/community/contributor/contributor.module'; +import { EntityResolverModule } from '@services/infrastructure/entity-resolver/entity.resolver.module'; +import { RoleSetEventsService } from './role.set.service.events'; +import { AiServerAdapterModule } from '@services/adapters/ai-server-adapter/ai.server.adapter.module'; +import { NotificationAdapterModule } from '@services/adapters/notification-adapter/notification.adapter.module'; +import { RoleSetApplicationLifecycleOptionsProvider } from './role.set.lifecycle.application.options.provider'; +import { RoleSetInvitationLifecycleOptionsProvider } from './role.set.lifecycle.invitation.options.provider'; +import { ContributionReporterModule } from '@services/external/elasticsearch/contribution-reporter'; +import { ActivityAdapterModule } from '@services/adapters/activity-adapter/activity.adapter.module'; +import { LifecycleModule } from '@domain/common/lifecycle/lifecycle.module'; @Module({ imports: [ @@ -21,11 +34,21 @@ import { RoleModule } from '../role/role.module'; AuthorizationPolicyModule, LicenseEngineModule, FormModule, + AgentModule, + UserModule, + OrganizationModule, + ContributorModule, RoleModule, InvitationModule, + EntityResolverModule, ApplicationModule, PlatformInvitationModule, VirtualContributorModule, + AiServerAdapterModule, + NotificationAdapterModule, + ContributionReporterModule, + ActivityAdapterModule, + LifecycleModule, TypeOrmModule.forFeature([RoleSet]), ], providers: [ @@ -33,6 +56,9 @@ import { RoleModule } from '../role/role.module'; RoleSetAuthorizationService, RoleSetResolverMutations, RoleSetResolverFields, + RoleSetEventsService, + RoleSetApplicationLifecycleOptionsProvider, + RoleSetInvitationLifecycleOptionsProvider, ], exports: [RoleSetService, RoleSetAuthorizationService], }) From 94895a8f9fe5980b8bdfbbcc1f96ce3467caf18a Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Mon, 16 Sep 2024 18:07:53 +0100 Subject: [PATCH 09/78] renamed DTOs related to RoleSet --- .../validation/handlers/base/base.handler.ts | 12 ++--- .../access/role-set/dto/role.set.dto.apply.ts | 2 +- .../dto/role.set.dto.invite.contributor.ts | 2 +- .../access/role-set/dto/role.set.dto.join.ts | 2 +- ...e.set.dto.platform.invitation.community.ts | 2 +- .../role.set.dto.role.assign.organization.ts | 2 +- .../dto/role.set.dto.role.assign.user.ts | 2 +- .../dto/role.set.dto.role.assign.virtual.ts | 2 +- .../role.set.dto.role.remove.organization.ts | 2 +- .../dto/role.set.dto.role.remove.user.ts | 2 +- .../dto/role.set.dto.role.remove.virtual.ts | 2 +- .../role.set.dto.update.application.form.ts | 2 +- .../role-set/role.set.resolver.mutations.ts | 44 +++++++++---------- .../access/role-set/role.set.service.ts | 4 +- 14 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/core/validation/handlers/base/base.handler.ts b/src/core/validation/handlers/base/base.handler.ts index 2020e055d0..ade6dd8cfa 100644 --- a/src/core/validation/handlers/base/base.handler.ts +++ b/src/core/validation/handlers/base/base.handler.ts @@ -72,9 +72,9 @@ import { UpdateCommunityGuidelinesInput } from '@domain/community/community-guid import { ForumCreateDiscussionInput } from '@platform/forum/dto/forum.dto.create.discussion'; import { CreateCollaborationOnSpaceInput } from '@domain/space/space/dto/space.dto.create.collaboration'; import { UpdateInnovationFlowEntityInput } from '@domain/collaboration/innovation-flow/dto/innovation.flow.dto.update.entity'; -import { CommunityRoleApplyInput } from '@domain/access/role-set/dto/role.set.dto.apply'; -import { CreateInvitationForContributorsOnCommunityInput } from '@domain/access/role-set/dto/role.set.dto.invite.contributor'; -import { CreatePlatformInvitationOnCommunityInput } from '@domain/access/role-set/dto/role.set.dto.platform.invitation.community'; +import { ApplyForRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.apply'; +import { InviteContributorForRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.invite.contributor'; +import { InviteNewContributorForRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.platform.invitation.community'; export class BaseHandler extends AbstractHandler { public async handle( @@ -131,9 +131,9 @@ export class BaseHandler extends AbstractHandler { UpdateSpaceSettingsEntityInput, UpdateSpaceSettingsInput, VisualUploadImageInput, - CommunityRoleApplyInput, - CreateInvitationForContributorsOnCommunityInput, - CreatePlatformInvitationOnCommunityInput, + ApplyForRoleOnRoleSetInput, + InviteContributorForRoleOnRoleSetInput, + InviteNewContributorForRoleOnRoleSetInput, ForumCreateDiscussionInput, SendMessageOnCalloutInput, CreateCalloutOnCollaborationInput, diff --git a/src/domain/access/role-set/dto/role.set.dto.apply.ts b/src/domain/access/role-set/dto/role.set.dto.apply.ts index 8c10ff789f..a8de5dcdeb 100644 --- a/src/domain/access/role-set/dto/role.set.dto.apply.ts +++ b/src/domain/access/role-set/dto/role.set.dto.apply.ts @@ -6,7 +6,7 @@ import { Type } from 'class-transformer'; import { UUID_LENGTH } from '@common/constants'; @InputType() -export class CommunityRoleApplyInput { +export class ApplyForRoleOnRoleSetInput { @Field(() => UUID, { nullable: false }) @MaxLength(UUID_LENGTH) roleSetID!: string; diff --git a/src/domain/access/role-set/dto/role.set.dto.invite.contributor.ts b/src/domain/access/role-set/dto/role.set.dto.invite.contributor.ts index 0a215ce1e8..9fabc1cc99 100644 --- a/src/domain/access/role-set/dto/role.set.dto.invite.contributor.ts +++ b/src/domain/access/role-set/dto/role.set.dto.invite.contributor.ts @@ -4,7 +4,7 @@ import { IsOptional, MaxLength } from 'class-validator'; import { MID_TEXT_LENGTH, UUID_LENGTH } from '@common/constants'; @InputType() -export class CreateInvitationForContributorsOnCommunityInput { +export class InviteContributorForRoleOnRoleSetInput { @Field(() => UUID, { nullable: false }) @MaxLength(UUID_LENGTH) roleSetID!: string; diff --git a/src/domain/access/role-set/dto/role.set.dto.join.ts b/src/domain/access/role-set/dto/role.set.dto.join.ts index a5861d6f69..6dcf0aa937 100644 --- a/src/domain/access/role-set/dto/role.set.dto.join.ts +++ b/src/domain/access/role-set/dto/role.set.dto.join.ts @@ -4,7 +4,7 @@ import { MaxLength } from 'class-validator'; import { UUID_LENGTH } from '@common/constants'; @InputType() -export class CommunityJoinInput { +export class JoinAsBaseRoleOnRoleSetInput { @Field(() => UUID, { nullable: false }) @MaxLength(UUID_LENGTH) roleSetID!: string; diff --git a/src/domain/access/role-set/dto/role.set.dto.platform.invitation.community.ts b/src/domain/access/role-set/dto/role.set.dto.platform.invitation.community.ts index 6767ef483b..7971456286 100644 --- a/src/domain/access/role-set/dto/role.set.dto.platform.invitation.community.ts +++ b/src/domain/access/role-set/dto/role.set.dto.platform.invitation.community.ts @@ -5,7 +5,7 @@ import { UUID_LENGTH } from '@common/constants'; import { CreatePlatformInvitationInput } from '@platform/invitation/dto/platform.invitation.dto.create'; @InputType() -export class CreatePlatformInvitationOnCommunityInput extends CreatePlatformInvitationInput { +export class InviteNewContributorForRoleOnRoleSetInput extends CreatePlatformInvitationInput { @Field(() => UUID, { nullable: false }) @MaxLength(UUID_LENGTH) roleSetID!: string; diff --git a/src/domain/access/role-set/dto/role.set.dto.role.assign.organization.ts b/src/domain/access/role-set/dto/role.set.dto.role.assign.organization.ts index fc260f34a8..4b3b24ed22 100644 --- a/src/domain/access/role-set/dto/role.set.dto.role.assign.organization.ts +++ b/src/domain/access/role-set/dto/role.set.dto.role.assign.organization.ts @@ -3,7 +3,7 @@ import { UUID, UUID_NAMEID } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @InputType() -export class AssignCommunityRoleToOrganizationInput { +export class AssignRoleOnRoleSetToOrganizationInput { @Field(() => UUID, { nullable: false }) roleSetID!: string; diff --git a/src/domain/access/role-set/dto/role.set.dto.role.assign.user.ts b/src/domain/access/role-set/dto/role.set.dto.role.assign.user.ts index 7e632cffec..64142f03ec 100644 --- a/src/domain/access/role-set/dto/role.set.dto.role.assign.user.ts +++ b/src/domain/access/role-set/dto/role.set.dto.role.assign.user.ts @@ -3,7 +3,7 @@ import { UUID, UUID_NAMEID_EMAIL } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @InputType() -export class AssignCommunityRoleToUserInput { +export class AssignRoleOnRoleSetToUserInput { @Field(() => UUID, { nullable: false }) roleSetID!: string; diff --git a/src/domain/access/role-set/dto/role.set.dto.role.assign.virtual.ts b/src/domain/access/role-set/dto/role.set.dto.role.assign.virtual.ts index e9b0214b15..e63bea8a66 100644 --- a/src/domain/access/role-set/dto/role.set.dto.role.assign.virtual.ts +++ b/src/domain/access/role-set/dto/role.set.dto.role.assign.virtual.ts @@ -3,7 +3,7 @@ import { UUID, UUID_NAMEID } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @InputType() -export class AssignCommunityRoleToVirtualInput { +export class AssignRoleOnRoleSetToVirtualContributorInput { @Field(() => UUID, { nullable: false }) roleSetID!: string; diff --git a/src/domain/access/role-set/dto/role.set.dto.role.remove.organization.ts b/src/domain/access/role-set/dto/role.set.dto.role.remove.organization.ts index b01feca053..d1c3ed5167 100644 --- a/src/domain/access/role-set/dto/role.set.dto.role.remove.organization.ts +++ b/src/domain/access/role-set/dto/role.set.dto.role.remove.organization.ts @@ -3,7 +3,7 @@ import { UUID, UUID_NAMEID } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @InputType() -export class RemoveCommunityRoleFromOrganizationInput { +export class RemoveRoleOnRoleSetFromOrganizationInput { @Field(() => UUID, { nullable: false }) roleSetID!: string; diff --git a/src/domain/access/role-set/dto/role.set.dto.role.remove.user.ts b/src/domain/access/role-set/dto/role.set.dto.role.remove.user.ts index 01e1c70663..6791846875 100644 --- a/src/domain/access/role-set/dto/role.set.dto.role.remove.user.ts +++ b/src/domain/access/role-set/dto/role.set.dto.role.remove.user.ts @@ -3,7 +3,7 @@ import { UUID, UUID_NAMEID_EMAIL } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @InputType() -export class RemoveCommunityRoleFromUserInput { +export class RemoveRoleOnRoleSetFromUserInput { @Field(() => UUID, { nullable: false }) roleSetID!: string; diff --git a/src/domain/access/role-set/dto/role.set.dto.role.remove.virtual.ts b/src/domain/access/role-set/dto/role.set.dto.role.remove.virtual.ts index 97917357d5..8432c1ecb6 100644 --- a/src/domain/access/role-set/dto/role.set.dto.role.remove.virtual.ts +++ b/src/domain/access/role-set/dto/role.set.dto.role.remove.virtual.ts @@ -3,7 +3,7 @@ import { UUID, UUID_NAMEID } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @InputType() -export class RemoveCommunityRoleFromVirtualInput { +export class RemoveRoleOnRoleSetFromVirtualContributorInput { @Field(() => UUID, { nullable: false }) roleSetID!: string; diff --git a/src/domain/access/role-set/dto/role.set.dto.update.application.form.ts b/src/domain/access/role-set/dto/role.set.dto.update.application.form.ts index 1fddbda871..dc6579e471 100644 --- a/src/domain/access/role-set/dto/role.set.dto.update.application.form.ts +++ b/src/domain/access/role-set/dto/role.set.dto.update.application.form.ts @@ -5,7 +5,7 @@ import { UpdateFormInput } from '@domain/common/form/dto/form.dto.update'; import { UUID } from '@domain/common/scalars'; @InputType() -export class UpdateRoleSetApplicationFormInput { +export class UpdateApplicationFormOnRoleSetInput { @Field(() => UUID, { nullable: false }) roleSetID!: string; diff --git a/src/domain/access/role-set/role.set.resolver.mutations.ts b/src/domain/access/role-set/role.set.resolver.mutations.ts index 54c6775886..2a5d261015 100644 --- a/src/domain/access/role-set/role.set.resolver.mutations.ts +++ b/src/domain/access/role-set/role.set.resolver.mutations.ts @@ -6,7 +6,7 @@ import { GraphqlGuard } from '@core/authorization'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { AuthorizationPrivilege, LogContext } from '@common/enums'; import { AuthorizationService } from '@core/authorization/authorization.service'; -import { UpdateRoleSetApplicationFormInput } from './dto/role.set.dto.update.application.form'; +import { UpdateApplicationFormOnRoleSetInput } from './dto/role.set.dto.update.application.form'; import { IRoleSet } from './role.set.interface'; import { IInvitation } from '../invitation/invitation.interface'; import { InvitationEventInput } from '../invitation/dto/invitation.dto.event'; @@ -14,7 +14,7 @@ import { ApplicationEventInput } from '../application/dto/application.dto.event' import { IApplication } from '../application/application.interface'; import { CommunityRoleType } from '@common/enums/community.role'; import { RoleSetMembershipException } from '@common/exceptions/role.set.membership.exception'; -import { CommunityJoinInput } from './dto/role.set.dto.join'; +import { JoinAsBaseRoleOnRoleSetInput } from './dto/role.set.dto.join'; import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; import { NotificationInputPlatformInvitation } from '@services/adapters/notification-adapter/dto/notification.dto.input.platform.invitation'; import { ApplicationService } from '../application/application.service'; @@ -32,18 +32,18 @@ import { CommunityResolverService } from '@services/infrastructure/entity-resolv import { RoleSetApplicationLifecycleOptionsProvider } from './role.set.lifecycle.application.options.provider'; import { RoleSetInvitationLifecycleOptionsProvider } from './role.set.lifecycle.invitation.options.provider'; import { PlatformInvitationService } from '@platform/invitation/platform.invitation.service'; -import { AssignCommunityRoleToUserInput } from './dto/role.set.dto.role.assign.user'; +import { AssignRoleOnRoleSetToUserInput } from './dto/role.set.dto.role.assign.user'; import { IUser } from '@domain/community/user/user.interface'; import { IOrganization } from '@domain/community/organization/organization.interface'; -import { AssignCommunityRoleToOrganizationInput } from './dto/role.set.dto.role.assign.organization'; +import { AssignRoleOnRoleSetToOrganizationInput } from './dto/role.set.dto.role.assign.organization'; import { IVirtualContributor } from '@domain/community/virtual-contributor/virtual.contributor.interface'; -import { AssignCommunityRoleToVirtualInput } from './dto/role.set.dto.role.assign.virtual'; -import { RemoveCommunityRoleFromUserInput } from './dto/role.set.dto.role.remove.user'; -import { RemoveCommunityRoleFromOrganizationInput } from './dto/role.set.dto.role.remove.organization'; -import { RemoveCommunityRoleFromVirtualInput } from './dto/role.set.dto.role.remove.virtual'; -import { CommunityRoleApplyInput } from './dto/role.set.dto.apply'; +import { AssignRoleOnRoleSetToVirtualContributorInput } from './dto/role.set.dto.role.assign.virtual'; +import { RemoveRoleOnRoleSetFromUserInput } from './dto/role.set.dto.role.remove.user'; +import { RemoveRoleOnRoleSetFromOrganizationInput } from './dto/role.set.dto.role.remove.organization'; +import { RemoveRoleOnRoleSetFromVirtualContributorInput } from './dto/role.set.dto.role.remove.virtual'; +import { ApplyForRoleOnRoleSetInput } from './dto/role.set.dto.apply'; import { NotificationInputCommunityApplication } from '@services/adapters/notification-adapter/dto/notification.dto.input.community.application'; -import { CreateInvitationForContributorsOnCommunityInput } from './dto/role.set.dto.invite.contributor'; +import { InviteContributorForRoleOnRoleSetInput } from './dto/role.set.dto.invite.contributor'; import { RoleSetInvitationException } from '@common/exceptions/role.set.invitation.exception'; import { IContributor } from '@domain/community/contributor/contributor.interface'; import { EntityNotInitializedException } from '@common/exceptions/entity.not.initialized.exception'; @@ -51,7 +51,7 @@ import { CreateInvitationInput } from '../invitation/dto/invitation.dto.create'; import { VirtualContributor } from '@domain/community/virtual-contributor/virtual.contributor.entity'; import { NotificationInputCommunityInvitationVirtualContributor } from '@services/adapters/notification-adapter/dto/notification.dto.input.community.invitation.vc'; import { IPlatformInvitation } from '@platform/invitation/platform.invitation.interface'; -import { CreatePlatformInvitationOnCommunityInput } from './dto/role.set.dto.platform.invitation.community'; +import { InviteNewContributorForRoleOnRoleSetInput } from './dto/role.set.dto.platform.invitation.community'; import { NotificationInputCommunityInvitation } from '@services/adapters/notification-adapter/dto/notification.dto.input.community.invitation'; import { RoleSetAuthorizationService } from './role.set.service.authorization'; @@ -85,7 +85,7 @@ export class RoleSetResolverMutations { @Profiling.api async assignCommunityRoleToUser( @CurrentUser() agentInfo: AgentInfo, - @Args('roleData') roleData: AssignCommunityRoleToUserInput + @Args('roleData') roleData: AssignRoleOnRoleSetToUserInput ): Promise { const roleSet = await this.roleSetService.getRoleSetOrFail( roleData.roleSetID @@ -127,7 +127,7 @@ export class RoleSetResolverMutations { async assignCommunityRoleToOrganization( @CurrentUser() agentInfo: AgentInfo, @Args('roleData') - roleData: AssignCommunityRoleToOrganizationInput + roleData: AssignRoleOnRoleSetToOrganizationInput ): Promise { const roleSet = await this.roleSetService.getRoleSetOrFail( roleData.roleSetID @@ -154,7 +154,7 @@ export class RoleSetResolverMutations { @Profiling.api async assignCommunityRoleToVirtual( @CurrentUser() agentInfo: AgentInfo, - @Args('roleData') roleData: AssignCommunityRoleToVirtualInput + @Args('roleData') roleData: AssignRoleOnRoleSetToVirtualContributorInput ): Promise { const roleSet = await this.roleSetService.getRoleSetOrFail( roleData.roleSetID @@ -210,7 +210,7 @@ export class RoleSetResolverMutations { @Profiling.api async removeCommunityRoleFromUser( @CurrentUser() agentInfo: AgentInfo, - @Args('roleData') roleData: RemoveCommunityRoleFromUserInput + @Args('roleData') roleData: RemoveRoleOnRoleSetFromUserInput ): Promise { const roleSet = await this.roleSetService.getRoleSetOrFail( roleData.roleSetID @@ -257,7 +257,7 @@ export class RoleSetResolverMutations { @Profiling.api async removeCommunityRoleFromOrganization( @CurrentUser() agentInfo: AgentInfo, - @Args('roleData') roleData: RemoveCommunityRoleFromOrganizationInput + @Args('roleData') roleData: RemoveRoleOnRoleSetFromOrganizationInput ): Promise { const roleSet = await this.roleSetService.getRoleSetOrFail( roleData.roleSetID @@ -283,7 +283,7 @@ export class RoleSetResolverMutations { @Profiling.api async removeCommunityRoleFromVirtual( @CurrentUser() agentInfo: AgentInfo, - @Args('roleData') roleData: RemoveCommunityRoleFromVirtualInput + @Args('roleData') roleData: RemoveRoleOnRoleSetFromVirtualContributorInput ): Promise { const roleSet = await this.roleSetService.getRoleSetOrFail( roleData.roleSetID @@ -323,7 +323,7 @@ export class RoleSetResolverMutations { @Profiling.api async applyForCommunityMembership( @CurrentUser() agentInfo: AgentInfo, - @Args('applicationData') applicationData: CommunityRoleApplyInput + @Args('applicationData') applicationData: ApplyForRoleOnRoleSetInput ): Promise { const roleSet = await this.roleSetService.getRoleSetOrFail( applicationData.roleSetID, @@ -395,7 +395,7 @@ export class RoleSetResolverMutations { async inviteContributorsForCommunityMembership( @CurrentUser() agentInfo: AgentInfo, @Args('invitationData') - invitationData: CreateInvitationForContributorsOnCommunityInput + invitationData: InviteContributorForRoleOnRoleSetInput ): Promise { const roleSet = await this.roleSetService.getRoleSetOrFail( invitationData.roleSetID, @@ -552,7 +552,7 @@ export class RoleSetResolverMutations { async inviteUserToPlatformAndCommunity( @CurrentUser() agentInfo: AgentInfo, @Args('invitationData') - invitationData: CreatePlatformInvitationOnCommunityInput + invitationData: InviteNewContributorForRoleOnRoleSetInput ): Promise { const roleSet = await this.roleSetService.getRoleSetOrFail( invitationData.roleSetID, @@ -644,7 +644,7 @@ export class RoleSetResolverMutations { @Profiling.api async joinRoleSet( @CurrentUser() agentInfo: AgentInfo, - @Args('joinCommunityData') joiningData: CommunityJoinInput + @Args('joinCommunityData') joiningData: JoinAsBaseRoleOnRoleSetInput ): Promise { const roleSet = await this.roleSetService.getRoleSetOrFail( joiningData.roleSetID @@ -734,7 +734,7 @@ export class RoleSetResolverMutations { async updateRoleSetApplicationForm( @CurrentUser() agentInfo: AgentInfo, @Args('applicationFormData') - applicationFormData: UpdateRoleSetApplicationFormInput + applicationFormData: UpdateApplicationFormOnRoleSetInput ): Promise { const roleSet = await this.roleSetService.getRoleSetOrFail( applicationFormData.roleSetID diff --git a/src/domain/access/role-set/role.set.service.ts b/src/domain/access/role-set/role.set.service.ts index d45005949a..880b720ffd 100644 --- a/src/domain/access/role-set/role.set.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -47,7 +47,7 @@ import { CommunityContributorsUpdateType } from '@common/enums/community.contrib import { RoleSetMembershipException } from '@common/exceptions/role.set.membership.exception'; import { CreateApplicationInput } from '../application/dto/application.dto.create'; import { CreateInvitationInput } from '../invitation/dto/invitation.dto.create'; -import { CreatePlatformInvitationOnCommunityInput } from './dto/role.set.dto.platform.invitation.community'; +import { InviteNewContributorForRoleOnRoleSetInput } from './dto/role.set.dto.platform.invitation.community'; import { IPlatformInvitation } from '@platform/invitation/platform.invitation.interface'; import { CreatePlatformInvitationInput } from '@platform/invitation/dto/platform.invitation.dto.create'; import { ContributorService } from '@domain/community/contributor/contributor.service'; @@ -1030,7 +1030,7 @@ export class RoleSetService { } async createPlatformInvitation( - invitationData: CreatePlatformInvitationOnCommunityInput, + invitationData: InviteNewContributorForRoleOnRoleSetInput, agentInfo: AgentInfo ): Promise { await this.validateInvitationToExternalUser( From 098fe4c6060a3d4ae12bea963af1e9e3fd2fbb89 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Tue, 17 Sep 2024 13:32:05 +0200 Subject: [PATCH 10/78] revert testing changes --- .../access/role-set/role.set.interface.ts | 4 +- .../role-set/role.set.resolver.mutations.ts | 13 +--- src/domain/access/role/role.entity.ts | 2 +- src/domain/access/role/role.interface.ts | 2 +- src/domain/access/role/role.module.ts | 5 +- .../access/role/role.resolver.fields.ts | 60 ++++++++++--------- 6 files changed, 39 insertions(+), 47 deletions(-) diff --git a/src/domain/access/role-set/role.set.interface.ts b/src/domain/access/role-set/role.set.interface.ts index 20057a5621..93fa4420c1 100644 --- a/src/domain/access/role-set/role.set.interface.ts +++ b/src/domain/access/role-set/role.set.interface.ts @@ -1,12 +1,12 @@ import { ObjectType } from '@nestjs/graphql'; import { IAuthorizable } from '@domain/common/entity/authorizable-entity'; import { IForm } from '@domain/common/form/form.interface'; -import { IPlatformInvitation } from '@platform/invitation'; +import { IPlatformInvitation } from '@platform/invitation/platform.invitation.interface'; import { IApplication } from '@domain/access/application/application.interface'; import { IInvitation } from '@domain/access/invitation/invitation.interface'; import { IRole } from '../role/role.interface'; -@ObjectType('RoleSet') +@ObjectType('RoleSet2') export abstract class IRoleSet extends IAuthorizable { roles?: IRole[]; applications?: IApplication[]; diff --git a/src/domain/access/role-set/role.set.resolver.mutations.ts b/src/domain/access/role-set/role.set.resolver.mutations.ts index 2a5d261015..7bed937410 100644 --- a/src/domain/access/role-set/role.set.resolver.mutations.ts +++ b/src/domain/access/role-set/role.set.resolver.mutations.ts @@ -1,7 +1,7 @@ import { UseGuards } from '@nestjs/common'; import { Args, Mutation, Resolver } from '@nestjs/graphql'; import { RoleSetService } from './role.set.service'; -import { CurrentUser, Profiling } from '@src/common/decorators'; +import { CurrentUser } from '@src/common/decorators'; import { GraphqlGuard } from '@core/authorization'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { AuthorizationPrivilege, LogContext } from '@common/enums'; @@ -82,7 +82,6 @@ export class RoleSetResolverMutations { @Mutation(() => IUser, { description: 'Assigns a User to a role in the specified Community.', }) - @Profiling.api async assignCommunityRoleToUser( @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: AssignRoleOnRoleSetToUserInput @@ -123,7 +122,6 @@ export class RoleSetResolverMutations { @Mutation(() => IOrganization, { description: 'Assigns an Organization a Role in the specified Community.', }) - @Profiling.api async assignCommunityRoleToOrganization( @CurrentUser() agentInfo: AgentInfo, @Args('roleData') @@ -151,7 +149,6 @@ export class RoleSetResolverMutations { description: 'Assigns a Virtual Contributor to a role in the specified Community.', }) - @Profiling.api async assignCommunityRoleToVirtual( @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: AssignRoleOnRoleSetToVirtualContributorInput @@ -207,7 +204,6 @@ export class RoleSetResolverMutations { @Mutation(() => IUser, { description: 'Removes a User from a Role in the specified Community.', }) - @Profiling.api async removeCommunityRoleFromUser( @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: RemoveRoleOnRoleSetFromUserInput @@ -254,7 +250,6 @@ export class RoleSetResolverMutations { description: 'Removes an Organization from a Role in the specified Community.', }) - @Profiling.api async removeCommunityRoleFromOrganization( @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: RemoveRoleOnRoleSetFromOrganizationInput @@ -280,7 +275,6 @@ export class RoleSetResolverMutations { @Mutation(() => IVirtualContributor, { description: 'Removes a Virtual from a Role in the specified Community.', }) - @Profiling.api async removeCommunityRoleFromVirtual( @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: RemoveRoleOnRoleSetFromVirtualContributorInput @@ -320,7 +314,6 @@ export class RoleSetResolverMutations { @Mutation(() => IApplication, { description: 'Apply to join the specified Community as a member.', }) - @Profiling.api async applyForCommunityMembership( @CurrentUser() agentInfo: AgentInfo, @Args('applicationData') applicationData: ApplyForRoleOnRoleSetInput @@ -391,7 +384,6 @@ export class RoleSetResolverMutations { description: 'Invite an existing Contriburor to join the specified Community as a member.', }) - @Profiling.api async inviteContributorsForCommunityMembership( @CurrentUser() agentInfo: AgentInfo, @Args('invitationData') @@ -548,7 +540,6 @@ export class RoleSetResolverMutations { description: 'Invite a User to join the platform and the specified Community as a member.', }) - @Profiling.api async inviteUserToPlatformAndCommunity( @CurrentUser() agentInfo: AgentInfo, @Args('invitationData') @@ -641,7 +632,6 @@ export class RoleSetResolverMutations { description: 'Join the specified Community as a member, without going through an approval process.', }) - @Profiling.api async joinRoleSet( @CurrentUser() agentInfo: AgentInfo, @Args('joinCommunityData') joiningData: JoinAsBaseRoleOnRoleSetInput @@ -730,7 +720,6 @@ export class RoleSetResolverMutations { @Mutation(() => IRoleSet, { description: 'Update the Application Form used by this RoleSet.', }) - @Profiling.api async updateRoleSetApplicationForm( @CurrentUser() agentInfo: AgentInfo, @Args('applicationFormData') diff --git a/src/domain/access/role/role.entity.ts b/src/domain/access/role/role.entity.ts index 89db6a1158..3745370a33 100644 --- a/src/domain/access/role/role.entity.ts +++ b/src/domain/access/role/role.entity.ts @@ -7,7 +7,7 @@ import { RoleSet } from '../role-set/role.set.entity'; @Entity() export class Role extends BaseAlkemioEntity implements IRole { - @ManyToOne(() => RoleSet, manager => manager.roles, { + @ManyToOne(() => RoleSet, roleSet => roleSet.roles, { eager: false, cascade: false, onDelete: 'CASCADE', diff --git a/src/domain/access/role/role.interface.ts b/src/domain/access/role/role.interface.ts index 9ecbfd0381..1fb26727a3 100644 --- a/src/domain/access/role/role.interface.ts +++ b/src/domain/access/role/role.interface.ts @@ -1,5 +1,5 @@ import { CommunityRoleType } from '@common/enums/community.role'; -import { IBaseAlkemio } from '@domain/common/entity/base-entity'; +import { IBaseAlkemio } from '@domain/common/entity/base-entity/base.alkemio.interface'; import { Field, ObjectType } from '@nestjs/graphql'; @ObjectType('Role') diff --git a/src/domain/access/role/role.module.ts b/src/domain/access/role/role.module.ts index 8da65602e7..e1c2932aa2 100644 --- a/src/domain/access/role/role.module.ts +++ b/src/domain/access/role/role.module.ts @@ -3,10 +3,11 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { Role } from './role.entity'; import { RoleResolverFields } from './role.resolver.fields'; import { RoleService } from './role.service'; +import { AuthorizationModule } from '@core/authorization/authorization.module'; @Module({ - imports: [TypeOrmModule.forFeature([Role])], - providers: [RoleResolverFields, RoleService], + imports: [AuthorizationModule, TypeOrmModule.forFeature([Role])], + providers: [RoleService, RoleResolverFields], exports: [RoleService], }) export class RoleModule {} diff --git a/src/domain/access/role/role.resolver.fields.ts b/src/domain/access/role/role.resolver.fields.ts index 0d60d61204..5720741d35 100644 --- a/src/domain/access/role/role.resolver.fields.ts +++ b/src/domain/access/role/role.resolver.fields.ts @@ -2,44 +2,46 @@ import { Parent, ResolveField, Resolver } from '@nestjs/graphql'; import { IRole } from './role.interface'; import { RoleService } from './role.service'; import { IContributorRolePolicy } from './contributor.role.policy.interface'; -import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; +import { GraphqlGuard } from '@core/authorization/graphql.guard'; +import { UseGuards } from '@nestjs/common'; @Resolver(() => IRole) export class RoleResolverFields { constructor(private roleService: RoleService) {} - @ResolveField('credential', () => ICredentialDefinition, { - nullable: false, - description: 'The Credential associated with this Role.', - }) - credential(@Parent() role: IRole): ICredentialDefinition { - return this.roleService.getCredentialForRole(role); - } + // @ResolveField('credential', () => ICredentialDefinition, { + // nullable: false, + // description: 'The Credential associated with this Role.', + // }) + // credential(@Parent() role: IRole2): ICredentialDefinition { + // return this.roleService.getCredentialForRole(role); + // } - @ResolveField('parentCredentials', () => [ICredentialDefinition], { - nullable: false, - description: 'The Credential associated with this Role.', - }) - parentCredentials(@Parent() role: IRole): ICredentialDefinition[] { - return this.roleService.getParentCredentialsForRole(role); - } + // @ResolveField('parentCredentials', () => [ICredentialDefinition], { + // nullable: false, + // description: 'The Credential associated with this Role.', + // }) + // parentCredentials(@Parent() role: IRole2): ICredentialDefinition[] { + // return this.roleService.getParentCredentialsForRole(role); + // } - @ResolveField('userPolicy', () => IContributorRolePolicy, { - nullable: false, - description: 'The role policy that applies for Users in this Role.', - }) - userPolicy(@Parent() role: IRole): IContributorRolePolicy { - return this.roleService.getUserPolicy(role); - } + // @ResolveField('userPolicy', () => IContributorRolePolicy, { + // nullable: false, + // description: 'The role policy that applies for Users in this Role.', + // }) + // userPolicy(@Parent() role: IRole2): IContributorRolePolicy { + // return this.roleService.getUserPolicy(role); + // } - @ResolveField('organizationPolicy', () => IContributorRolePolicy, { - nullable: false, - description: 'The role policy that applies for Organizations in this Role.', - }) - organizationPolicy(@Parent() role: IRole): IContributorRolePolicy { - return this.roleService.getOrganizationPolicy(role); - } + // @ResolveField('organizationPolicy', () => IContributorRolePolicy, { + // nullable: false, + // description: 'The role policy that applies for Organizations in this Role.', + // }) + // organizationPolicy(@Parent() role: IRole2): IContributorRolePolicy { + // return this.roleService.getOrganizationPolicy(role); + // } + @UseGuards(GraphqlGuard) @ResolveField('virtualContributorPolicy', () => IContributorRolePolicy, { nullable: false, description: From 2bd55f344c045b4745e96ef7a2f9b8fb80a098c2 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Fri, 20 Sep 2024 16:48:14 +0200 Subject: [PATCH 11/78] made retrieval of credentials for roles async; fixed issue with module loading --- .../access/role-set/role.set.interface.ts | 2 +- .../role-set/role.set.resolver.fields.ts | 19 ++- .../role.set.service.authorization.ts | 16 +-- .../access/role-set/role.set.service.ts | 125 ++++++++++-------- .../access/role/role.resolver.fields.ts | 57 ++++---- ...lout.contribution.service.authorization.ts | 8 +- .../collaboration.service.authorization.ts | 32 +++-- .../post/post.service.authorization.ts | 8 +- .../community.service.authorization.ts | 16 +-- .../community/community/community.service.ts | 6 +- .../space/space.service.authorization.ts | 41 +++--- src/domain/space/space/space.service.ts | 17 ++- 12 files changed, 193 insertions(+), 154 deletions(-) diff --git a/src/domain/access/role-set/role.set.interface.ts b/src/domain/access/role-set/role.set.interface.ts index 93fa4420c1..1eb1ed4431 100644 --- a/src/domain/access/role-set/role.set.interface.ts +++ b/src/domain/access/role-set/role.set.interface.ts @@ -6,7 +6,7 @@ import { IApplication } from '@domain/access/application/application.interface'; import { IInvitation } from '@domain/access/invitation/invitation.interface'; import { IRole } from '../role/role.interface'; -@ObjectType('RoleSet2') +@ObjectType('RoleSet') export abstract class IRoleSet extends IAuthorizable { roles?: IRole[]; applications?: IApplication[]; diff --git a/src/domain/access/role-set/role.set.resolver.fields.ts b/src/domain/access/role-set/role.set.resolver.fields.ts index c27375cf8a..bd56e282fc 100644 --- a/src/domain/access/role-set/role.set.resolver.fields.ts +++ b/src/domain/access/role-set/role.set.resolver.fields.ts @@ -19,6 +19,7 @@ import { IOrganization } from '@domain/community/organization/organization.inter import { IVirtualContributor } from '@domain/community/virtual-contributor/virtual.contributor.interface'; import { IInvitation } from '../invitation/invitation.interface'; import { IPlatformInvitation } from '@platform/invitation/platform.invitation.interface'; +import { IRole } from '../role/role.interface'; @Resolver(() => IRoleSet) export class RoleSetResolverFields { @@ -71,7 +72,7 @@ export class RoleSetResolverFields { @Args('filter', { nullable: true }) filter?: UserFilterInput ) { const memberRoleCredentials = - this.roleSetService.getCredentialDefinitionForRole( + await this.roleSetService.getCredentialDefinitionForRole( roleSet, CommunityRoleType.MEMBER ); @@ -79,7 +80,7 @@ export class RoleSetResolverFields { const parentCommunity = await this.roleSetService.getParentRoleSet(roleSet); const parentCommunityMemberCredentials = parentCommunity - ? this.roleSetService.getCredentialDefinitionForRole( + ? await this.roleSetService.getCredentialDefinitionForRole( parentCommunity, CommunityRoleType.MEMBER ) @@ -160,13 +161,13 @@ export class RoleSetResolverFields { @Args('filter', { nullable: true }) filter?: UserFilterInput ) { const memberRoleCredentials = - this.roleSetService.getCredentialDefinitionForRole( + await this.roleSetService.getCredentialDefinitionForRole( roleSet, CommunityRoleType.MEMBER ); const leadRoleCredential = - this.roleSetService.getCredentialDefinitionForRole( + await this.roleSetService.getCredentialDefinitionForRole( roleSet, CommunityRoleType.LEAD ); @@ -228,4 +229,14 @@ export class RoleSetResolverFields { async applicationForm(@Parent() roleSet: RoleSet): Promise { return await this.roleSetService.getApplicationForm(roleSet); } + + @UseGuards(GraphqlGuard) + @ResolveField('roles', () => [IRole], { + nullable: false, + description: 'The Role Definitions included in this roleSet.', + }) + @Profiling.api + async roles(@Parent() roleSet: RoleSet): Promise { + return await this.roleSetService.getRoleDefinitions(roleSet); + } } diff --git a/src/domain/access/role-set/role.set.service.authorization.ts b/src/domain/access/role-set/role.set.service.authorization.ts index c5797874f6..68d28d9338 100644 --- a/src/domain/access/role-set/role.set.service.authorization.ts +++ b/src/domain/access/role-set/role.set.service.authorization.ts @@ -108,7 +108,7 @@ export class RoleSetAuthorizationService { ); } if (isSubspace) { - roleSet.authorization = this.extendAuthorizationPolicySubspace( + roleSet.authorization = await this.extendAuthorizationPolicySubspace( roleSet, roleSet.authorization, spaceSettings @@ -232,7 +232,7 @@ export class RoleSetAuthorizationService { newRules.push(globalAdminAddMembers); const inviteMembersCriterias: ICredentialDefinition[] = - this.roleSetService.getCredentialsForRoleWithParents( + await this.roleSetService.getCredentialsForRoleWithParents( roleSet, CommunityRoleType.ADMIN, spaceSettings @@ -240,7 +240,7 @@ export class RoleSetAuthorizationService { if (spaceSettings.membership.allowSubspaceAdminsToInviteMembers) { // use the member credential to create subspace admin credential const subspaceAdminCredential: ICredentialDefinition = - this.roleSetService.getCredentialForRole( + await this.roleSetService.getCredentialForRole( roleSet, CommunityRoleType.MEMBER ); @@ -277,7 +277,7 @@ export class RoleSetAuthorizationService { ); if (accessVirtualContributors) { const criterias: ICredentialDefinition[] = - this.roleSetService.getCredentialsForRoleWithParents( + await this.roleSetService.getCredentialsForRoleWithParents( roleSet, CommunityRoleType.ADMIN, spaceSettings @@ -306,11 +306,11 @@ export class RoleSetAuthorizationService { return updatedAuthorization; } - private extendAuthorizationPolicySubspace( + private async extendAuthorizationPolicySubspace( roleSet: IRoleSet, authorization: IAuthorizationPolicy | undefined, spaceSettings: ISpaceSettings - ): IAuthorizationPolicy { + ): Promise { if (!authorization) throw new EntityNotInitializedException( 'Authorization definition not found', @@ -320,7 +320,7 @@ export class RoleSetAuthorizationService { const newRules: IAuthorizationPolicyRuleCredential[] = []; const parentRoleSetCredential = - this.roleSetService.getDirectParentCredentialForRole( + await this.roleSetService.getDirectParentCredentialForRole( roleSet, CommunityRoleType.MEMBER ); @@ -353,7 +353,7 @@ export class RoleSetAuthorizationService { } const adminCredentials = - this.roleSetService.getCredentialsForRoleWithParents( + await this.roleSetService.getCredentialsForRoleWithParents( roleSet, CommunityRoleType.ADMIN, spaceSettings diff --git a/src/domain/access/role-set/role.set.service.ts b/src/domain/access/role-set/role.set.service.ts index 880b720ffd..2fb7adb944 100644 --- a/src/domain/access/role-set/role.set.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -217,8 +217,11 @@ export class RoleSetService { } // Update the Community policy to have the right resource ID - public updateRoleResourceID(roleSet: IRoleSet, resourceID: string): IRoleSet { - const roleDefinitions = this.getRoleDefinitions(roleSet); + public async updateRoleResourceID( + roleSet: IRoleSet, + resourceID: string + ): Promise { + const roleDefinitions = await this.getRoleDefinitions(roleSet); for (const roleDefinition of roleDefinitions) { const credential = this.roleService.getCredentialForRole(roleDefinition); credential.resourceID = resourceID; @@ -339,12 +342,12 @@ export class RoleSetService { return undefined; } - async getUsersWithRole( + public async getUsersWithRole( roleSet: IRoleSet, roleType: CommunityRoleType, limit?: number ): Promise { - const membershipCredential = this.getCredentialDefinitionForRole( + const membershipCredential = await this.getCredentialDefinitionForRole( roleSet, roleType ); @@ -357,11 +360,11 @@ export class RoleSetService { ); } - async getVirtualContributorsWithRole( + public async getVirtualContributorsWithRole( roleSet: IRoleSet, roleType: CommunityRoleType ): Promise { - const membershipCredential = this.getCredentialDefinitionForRole( + const membershipCredential = await this.getCredentialDefinitionForRole( roleSet, roleType ); @@ -373,11 +376,11 @@ export class RoleSetService { ); } - async getOrganizationsWithRole( + public async getOrganizationsWithRole( roleSet: IRoleSet, roleType: CommunityRoleType ): Promise { - const membershipCredential = this.getCredentialDefinitionForRole( + const membershipCredential = await this.getCredentialDefinitionForRole( roleSet, roleType ); @@ -387,12 +390,12 @@ export class RoleSetService { }); } - async countContributorsPerRole( + public async countContributorsPerRole( roleSet: IRoleSet, roleType: CommunityRoleType, contributorType: CommunityContributorType ): Promise { - const membershipCredential = this.getCredentialDefinitionForRole( + const membershipCredential = await this.getCredentialDefinitionForRole( roleSet, roleType ); @@ -414,15 +417,15 @@ export class RoleSetService { return 0; } - getCredentialDefinitionForRole( + public async getCredentialDefinitionForRole( roleSet: IRoleSet, role: CommunityRoleType - ): ICredentialDefinition { + ): Promise { const credential = this.getCredentialForRole(roleSet, role); return credential; } - async assignContributorToRole( + public async assignContributorToRole( roleSet: IRoleSet, roleType: CommunityRoleType, contributorID: string, @@ -491,7 +494,7 @@ export class RoleSetService { ); if (roleType === CommunityRoleType.ADMIN && parentRoleSet) { // also assign as subspace admin in parent roleSet if there is a parent roleSet - const credential = this.getCredentialForImplicitRole( + const credential = await this.getCredentialForImplicitRole( parentRoleSet, CommunityRoleImplicit.SUBSPACE_ADMIN ); @@ -758,7 +761,7 @@ export class RoleSetService { CommunityContributorType.USER ); - const roleDefinition = this.getRoleDefinition(roleSet, roleType); + const roleDefinition = await this.getRoleDefinition(roleSet, roleType); const userPolicy = this.roleService.getUserPolicy(roleDefinition); @@ -792,7 +795,7 @@ export class RoleSetService { CommunityContributorType.ORGANIZATION ); - const roleDefinition = this.getRoleDefinition(roleSet, roleType); + const roleDefinition = await this.getRoleDefinition(roleSet, roleType); const organizationPolicy = this.roleService.getOrganizationPolicy(roleDefinition); @@ -842,7 +845,7 @@ export class RoleSetService { contributorType ); - const roleCredential = this.getCredentialForRole(roleSet, roleType); + const roleCredential = await this.getCredentialForRole(roleSet, roleType); return await this.agentService.grantCredential({ agentID: agent.id, @@ -856,7 +859,7 @@ export class RoleSetService { agent: IAgent, role: CommunityRoleImplicit ): Promise { - const credential = this.getCredentialForImplicitRole(roleSet, role); + const credential = await this.getCredentialForImplicitRole(roleSet, role); return await this.agentService.grantCredential({ agentID: agent.id, @@ -870,7 +873,7 @@ export class RoleSetService { agent: IAgent, role: CommunityRoleImplicit ): Promise { - const credential = this.getCredentialForImplicitRole(roleSet, role); + const credential = await this.getCredentialForImplicitRole(roleSet, role); return await this.agentService.revokeCredential({ agentID: agent.id, @@ -879,12 +882,12 @@ export class RoleSetService { }); } - private getCredentialForImplicitRole( + private async getCredentialForImplicitRole( roleSet: IRoleSet, role: CommunityRoleImplicit - ): ICredentialDefinition { + ): Promise { // Use the admin credential to get the resourceID - const adminCredential = this.getCredentialDefinitionForRole( + const adminCredential = await this.getCredentialDefinitionForRole( roleSet, CommunityRoleType.ADMIN ); @@ -920,7 +923,7 @@ export class RoleSetService { ); } - const roleCredential = this.getCredentialForRole(roleSet, roleType); + const roleCredential = await this.getCredentialForRole(roleSet, roleType); return await this.agentService.revokeCredential({ agentID: agent.id, @@ -929,8 +932,8 @@ export class RoleSetService { }); } - async isMember(agent: IAgent, roleSet: IRoleSet): Promise { - const membershipCredential = this.getCredentialDefinitionForRole( + public async isMember(agent: IAgent, roleSet: IRoleSet): Promise { + const membershipCredential = await this.getCredentialDefinitionForRole( roleSet, CommunityRoleType.MEMBER ); @@ -945,12 +948,12 @@ export class RoleSetService { return validCredential; } - async isInRole( + public async isInRole( agent: IAgent, roleSet: IRoleSet, role: CommunityRoleType ): Promise { - const membershipCredential = this.getCredentialDefinitionForRole( + const membershipCredential = await this.getCredentialDefinitionForRole( roleSet, role ); @@ -970,7 +973,7 @@ export class RoleSetService { roleSet: IRoleSet, role: CommunityRoleImplicit ): Promise { - const membershipCredential = this.getCredentialForImplicitRole( + const membershipCredential = await this.getCredentialForImplicitRole( roleSet, role ); @@ -1173,7 +1176,7 @@ export class RoleSetService { } async getMembersCount(roleSet: IRoleSet): Promise { - const membershipCredential = this.getCredentialDefinitionForRole( + const membershipCredential = await this.getCredentialDefinitionForRole( roleSet, CommunityRoleType.MEMBER ); @@ -1226,7 +1229,7 @@ export class RoleSetService { return result; } - public inheritParentCredentials(roleSet: IRoleSet): IRoleSet { + public async inheritParentCredentials(roleSet: IRoleSet): Promise { const roleSetParent = roleSet.parentRoleSet; if (!roleSetParent) { throw new RelationshipNotFoundException( @@ -1234,10 +1237,10 @@ export class RoleSetService { LogContext.ROLES ); } - const roleDefinitions = this.getRoleDefinitions(roleSet); + const roleDefinitions = await this.getRoleDefinitions(roleSet); for (const roleDefinition of roleDefinitions) { - const parentRoleDefinition = this.getRoleDefinition( + const parentRoleDefinition = await this.getRoleDefinition( roleSetParent, roleDefinition.type ); @@ -1256,11 +1259,11 @@ export class RoleSetService { return roleSet; } - getDirectParentCredentialForRole( + public async getDirectParentCredentialForRole( roleSet: IRoleSet, roleType: CommunityRoleType - ): ICredentialDefinition | undefined { - const parentCredentials = this.getParentCredentialsForRole( + ): Promise { + const parentCredentials = await this.getParentCredentialsForRole( roleSet, roleType ); @@ -1273,29 +1276,37 @@ export class RoleSetService { return directParentCredential; } - public getParentCredentialsForRole( + public async getParentCredentialsForRole( roleSet: IRoleSet, roleType: CommunityRoleType - ): ICredentialDefinition[] { - const roleDefinition = this.getRoleDefinition(roleSet, roleType); + ): Promise { + const roleDefinition = await this.getRoleDefinition(roleSet, roleType); return this.roleService.getParentCredentialsForRole(roleDefinition); } - public getCredentialsForRoleWithParents( + public async getCredentialsForRoleWithParents( roleSet: IRoleSet, roleType: CommunityRoleType, spaceSettings: ISpaceSettings - ): ICredentialDefinition[] { - const result = this.getCredentialsForRole(roleSet, roleType, spaceSettings); - return result.concat(this.getParentCredentialsForRole(roleSet, roleType)); + ): Promise { + const result = await this.getCredentialsForRole( + roleSet, + roleType, + spaceSettings + ); + const parentCredentials = await this.getParentCredentialsForRole( + roleSet, + roleType + ); + return result.concat(parentCredentials); } - public getCredentialsForRole( + public async getCredentialsForRole( roleSet: IRoleSet, roleType: CommunityRoleType, spaceSettings: ISpaceSettings // TODO: would like not to have this here; for later - ): ICredentialDefinition[] { - const result = [this.getCredentialForRole(roleSet, roleType)]; + ): Promise { + const result = [await this.getCredentialForRole(roleSet, roleType)]; if ( roleType === CommunityRoleType.ADMIN && spaceSettings.privacy.allowPlatformSupportAsAdmin @@ -1308,30 +1319,36 @@ export class RoleSetService { return result; } - public getCredentialForRole( + public async getCredentialForRole( roleSet: IRoleSet, roleType: CommunityRoleType - ): ICredentialDefinition { - const roleDefinition = this.getRoleDefinition(roleSet, roleType); + ): Promise { + const roleDefinition = await this.getRoleDefinition(roleSet, roleType); return this.roleService.getCredentialForRole(roleDefinition); } - public getRoleDefinitions(roleSet: IRoleSet): IRole[] { - const roleDefinitions = roleSet.roles; + public async getRoleDefinitions(roleSetInput: IRoleSet): Promise { + let roleDefinitions = roleSetInput.roles; + if (!roleDefinitions) { + const roleSet = await this.getRoleSetOrFail(roleSetInput.id, { + relations: { roles: true }, + }); + roleDefinitions = roleSet.roles; + } if (!roleDefinitions) { throw new RelationshipNotFoundException( - `Unable to load roles for RoleSet: ${roleSet.id}`, + `Unable to load roles for RoleSet: ${roleSetInput.id}`, LogContext.COMMUNITY ); } return roleDefinitions; } - public getRoleDefinition( + public async getRoleDefinition( roleSet: IRoleSet, roleType: CommunityRoleType - ): IRole { - const roleDefinitions = this.getRoleDefinitions(roleSet); + ): Promise { + const roleDefinitions = await this.getRoleDefinitions(roleSet); const role = roleDefinitions.find(rd => rd.type === roleType); if (!role) { throw new RelationshipNotFoundException( diff --git a/src/domain/access/role/role.resolver.fields.ts b/src/domain/access/role/role.resolver.fields.ts index 5720741d35..59e7c93f68 100644 --- a/src/domain/access/role/role.resolver.fields.ts +++ b/src/domain/access/role/role.resolver.fields.ts @@ -4,42 +4,43 @@ import { RoleService } from './role.service'; import { IContributorRolePolicy } from './contributor.role.policy.interface'; import { GraphqlGuard } from '@core/authorization/graphql.guard'; import { UseGuards } from '@nestjs/common'; +import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; @Resolver(() => IRole) export class RoleResolverFields { constructor(private roleService: RoleService) {} - // @ResolveField('credential', () => ICredentialDefinition, { - // nullable: false, - // description: 'The Credential associated with this Role.', - // }) - // credential(@Parent() role: IRole2): ICredentialDefinition { - // return this.roleService.getCredentialForRole(role); - // } + @ResolveField('credential', () => ICredentialDefinition, { + nullable: false, + description: 'The Credential associated with this Role.', + }) + credential(@Parent() role: IRole): ICredentialDefinition { + return this.roleService.getCredentialForRole(role); + } - // @ResolveField('parentCredentials', () => [ICredentialDefinition], { - // nullable: false, - // description: 'The Credential associated with this Role.', - // }) - // parentCredentials(@Parent() role: IRole2): ICredentialDefinition[] { - // return this.roleService.getParentCredentialsForRole(role); - // } + @ResolveField('parentCredentials', () => [ICredentialDefinition], { + nullable: false, + description: 'The Credential associated with this Role.', + }) + parentCredentials(@Parent() role: IRole): ICredentialDefinition[] { + return this.roleService.getParentCredentialsForRole(role); + } - // @ResolveField('userPolicy', () => IContributorRolePolicy, { - // nullable: false, - // description: 'The role policy that applies for Users in this Role.', - // }) - // userPolicy(@Parent() role: IRole2): IContributorRolePolicy { - // return this.roleService.getUserPolicy(role); - // } + @ResolveField('userPolicy', () => IContributorRolePolicy, { + nullable: false, + description: 'The role policy that applies for Users in this Role.', + }) + userPolicy(@Parent() role: IRole): IContributorRolePolicy { + return this.roleService.getUserPolicy(role); + } - // @ResolveField('organizationPolicy', () => IContributorRolePolicy, { - // nullable: false, - // description: 'The role policy that applies for Organizations in this Role.', - // }) - // organizationPolicy(@Parent() role: IRole2): IContributorRolePolicy { - // return this.roleService.getOrganizationPolicy(role); - // } + @ResolveField('organizationPolicy', () => IContributorRolePolicy, { + nullable: false, + description: 'The role policy that applies for Organizations in this Role.', + }) + organizationPolicy(@Parent() role: IRole): IContributorRolePolicy { + return this.roleService.getOrganizationPolicy(role); + } @UseGuards(GraphqlGuard) @ResolveField('virtualContributorPolicy', () => IContributorRolePolicy, { diff --git a/src/domain/collaboration/callout-contribution/callout.contribution.service.authorization.ts b/src/domain/collaboration/callout-contribution/callout.contribution.service.authorization.ts index 5d75b54d6a..4c29426273 100644 --- a/src/domain/collaboration/callout-contribution/callout.contribution.service.authorization.ts +++ b/src/domain/collaboration/callout-contribution/callout.contribution.service.authorization.ts @@ -109,7 +109,7 @@ export class CalloutContributionAuthorizationService { ); // Extend to give the user creating the contribution more rights - contribution.authorization = this.appendCredentialRules( + contribution.authorization = await this.appendCredentialRules( contribution, roleSet, spaceSettings @@ -148,11 +148,11 @@ export class CalloutContributionAuthorizationService { return updatedAuthorizations; } - private appendCredentialRules( + private async appendCredentialRules( contribution: ICalloutContribution, communityPolicy?: IRoleSet, spaceSettings?: ISpaceSettings - ): IAuthorizationPolicy { + ): Promise { const authorization = contribution.authorization; if (!authorization) throw new EntityNotInitializedException( @@ -204,7 +204,7 @@ export class CalloutContributionAuthorizationService { ]; if (communityPolicy && spaceSettings) { const roleCredentials = - this.roleSetService.getCredentialsForRoleWithParents( + await this.roleSetService.getCredentialsForRoleWithParents( communityPolicy, CommunityRoleType.ADMIN, spaceSettings diff --git a/src/domain/collaboration/collaboration/collaboration.service.authorization.ts b/src/domain/collaboration/collaboration/collaboration.service.authorization.ts index 48c321ab6b..01ad0aae89 100644 --- a/src/domain/collaboration/collaboration/collaboration.service.authorization.ts +++ b/src/domain/collaboration/collaboration/collaboration.service.authorization.ts @@ -87,11 +87,12 @@ export class CollaborationAuthorizationService { spaceAgent ); if (roleSet && spaceSettings && spaceAgent) { - collaboration.authorization = this.appendCredentialRulesForContributors( - collaboration.authorization, - roleSet, - spaceSettings - ); + collaboration.authorization = + await this.appendCredentialRulesForContributors( + collaboration.authorization, + roleSet, + spaceSettings + ); collaboration.authorization = await this.appendPrivilegeRules( collaboration.authorization, @@ -149,7 +150,7 @@ export class CollaborationAuthorizationService { if (roleSet && spaceSettings) { const extendedAuthorizationContributors = - this.appendCredentialRulesForContributors( + await this.appendCredentialRulesForContributors( clonedAuthorization, roleSet, spaceSettings @@ -172,12 +173,12 @@ export class CollaborationAuthorizationService { return updatedAuthorizations; } - private getContributorCredentials( + private async getContributorCredentials( roleSet: IRoleSet, spaceSettings: ISpaceSettings - ): ICredentialDefinition[] { + ): Promise { // add challenge members - let contributorCriterias = this.roleSetService.getCredentialsForRole( + let contributorCriterias = await this.roleSetService.getCredentialsForRole( roleSet, CommunityRoleType.MEMBER, spaceSettings @@ -185,7 +186,7 @@ export class CollaborationAuthorizationService { // optionally add space members if (spaceSettings.collaboration.inheritMembershipRights) { contributorCriterias = - this.roleSetService.getCredentialsForRoleWithParents( + await this.roleSetService.getCredentialsForRoleWithParents( roleSet, CommunityRoleType.MEMBER, spaceSettings @@ -225,7 +226,7 @@ export class CollaborationAuthorizationService { spaceAgent ); if (saveAsTemplateEnabled) { - const adminCriterias = this.roleSetService.getCredentialsForRole( + const adminCriterias = await this.roleSetService.getCredentialsForRole( roleSet, CommunityRoleType.ADMIN, spaceSettings @@ -251,11 +252,11 @@ export class CollaborationAuthorizationService { ); } - public appendCredentialRulesForContributors( + public async appendCredentialRulesForContributors( authorization: IAuthorizationPolicy | undefined, policy: IRoleSet, spaceSettings: ISpaceSettings - ): IAuthorizationPolicy { + ): Promise { if (!authorization) throw new EntityNotInitializedException( `Authorization definition not found for Context: ${JSON.stringify( @@ -267,7 +268,10 @@ export class CollaborationAuthorizationService { const newRules: IAuthorizationPolicyRuleCredential[] = []; // Who is able to contribute - const contributors = this.getContributorCredentials(policy, spaceSettings); + const contributors = await this.getContributorCredentials( + policy, + spaceSettings + ); const contributorsRule = this.authorizationPolicyService.createCredentialRule( [AuthorizationPrivilege.CONTRIBUTE], diff --git a/src/domain/collaboration/post/post.service.authorization.ts b/src/domain/collaboration/post/post.service.authorization.ts index fdd8928f0c..46667bbb77 100644 --- a/src/domain/collaboration/post/post.service.authorization.ts +++ b/src/domain/collaboration/post/post.service.authorization.ts @@ -71,7 +71,7 @@ export class PostAuthorizationService { } // Extend to give the user creating the post more rights - post.authorization = this.appendCredentialRules( + post.authorization = await this.appendCredentialRules( post, roleSet, spaceSettings @@ -89,11 +89,11 @@ export class PostAuthorizationService { return updatedAuthorizations; } - private appendCredentialRules( + private async appendCredentialRules( post: IPost, roleSet?: IRoleSet, spaceSettings?: ISpaceSettings - ): IAuthorizationPolicy { + ): Promise { const authorization = post.authorization; if (!authorization) throw new EntityNotInitializedException( @@ -126,7 +126,7 @@ export class PostAuthorizationService { if (roleSet && spaceSettings) { const roleCredentials = - this.roleSetService.getCredentialsForRoleWithParents( + await this.roleSetService.getCredentialsForRoleWithParents( roleSet, CommunityRoleType.ADMIN, spaceSettings diff --git a/src/domain/community/community/community.service.authorization.ts b/src/domain/community/community/community.service.authorization.ts index d956123f6b..9e994f4025 100644 --- a/src/domain/community/community/community.service.authorization.ts +++ b/src/domain/community/community/community.service.authorization.ts @@ -119,7 +119,7 @@ export class CommunityAuthorizationService { ); } if (isSubspace) { - community.authorization = this.extendAuthorizationPolicySubspace( + community.authorization = await this.extendAuthorizationPolicySubspace( community.authorization, community.roleSet, spaceSettings @@ -253,7 +253,7 @@ export class CommunityAuthorizationService { newRules.push(globalAdminAddMembers); const inviteMembersCriterias: ICredentialDefinition[] = - this.roleSetService.getCredentialsForRoleWithParents( + await this.roleSetService.getCredentialsForRoleWithParents( roleSet, CommunityRoleType.ADMIN, spaceSettings @@ -261,7 +261,7 @@ export class CommunityAuthorizationService { if (spaceSettings.membership.allowSubspaceAdminsToInviteMembers) { // use the member credential to create subspace admin credential const subspaceAdminCredential: ICredentialDefinition = - this.roleSetService.getCredentialForRole( + await this.roleSetService.getCredentialForRole( roleSet, CommunityRoleType.MEMBER ); @@ -298,7 +298,7 @@ export class CommunityAuthorizationService { ); if (accessVirtualContributors) { const criterias: ICredentialDefinition[] = - this.roleSetService.getCredentialsForRoleWithParents( + await this.roleSetService.getCredentialsForRoleWithParents( roleSet, CommunityRoleType.ADMIN, spaceSettings @@ -327,11 +327,11 @@ export class CommunityAuthorizationService { return updatedAuthorization; } - private extendAuthorizationPolicySubspace( + private async extendAuthorizationPolicySubspace( authorization: IAuthorizationPolicy | undefined, roleSet: IRoleSet, spaceSettings: ISpaceSettings - ): IAuthorizationPolicy { + ): Promise { if (!authorization) throw new EntityNotInitializedException( 'Authorization definition not found', @@ -341,7 +341,7 @@ export class CommunityAuthorizationService { const newRules: IAuthorizationPolicyRuleCredential[] = []; const parentCommunityCredential = - this.roleSetService.getDirectParentCredentialForRole( + await this.roleSetService.getDirectParentCredentialForRole( roleSet, CommunityRoleType.MEMBER ); @@ -374,7 +374,7 @@ export class CommunityAuthorizationService { } const adminCredentials = - this.roleSetService.getCredentialsForRoleWithParents( + await this.roleSetService.getCredentialsForRoleWithParents( roleSet, CommunityRoleType.ADMIN, spaceSettings diff --git a/src/domain/community/community/community.service.ts b/src/domain/community/community/community.service.ts index c85984771e..1140053eda 100644 --- a/src/domain/community/community/community.service.ts +++ b/src/domain/community/community/community.service.ts @@ -220,10 +220,10 @@ export class CommunityService { return undefined; } - public setParentCommunity( + public async setParentCommunity( community?: ICommunity, parentCommunity?: ICommunity - ): ICommunity { + ): Promise { if (!community || !parentCommunity) { throw new EntityNotInitializedException( 'Community not set', @@ -232,7 +232,7 @@ export class CommunityService { } community.parentCommunity = parentCommunity; // Also update the communityPolicy - community.roleSet = this.roleSetService.inheritParentCredentials( + community.roleSet = await this.roleSetService.inheritParentCredentials( community.roleSet ); diff --git a/src/domain/space/space/space.service.authorization.ts b/src/domain/space/space/space.service.authorization.ts index 4517b54369..709b6b007d 100644 --- a/src/domain/space/space/space.service.authorization.ts +++ b/src/domain/space/space/space.service.authorization.ts @@ -115,7 +115,7 @@ export class SpaceAuthorizationService { spaceInput.settingsStr ); parentSpaceAdminCredentialCriterias = - this.roleSetService.getCredentialsForRole( + await this.roleSetService.getCredentialsForRole( parentSpaceCommunity.roleSet, CommunityRoleType.ADMIN, spaceSettings @@ -162,7 +162,7 @@ export class SpaceAuthorizationService { switch (spaceVisibility) { case SpaceVisibility.ACTIVE: case SpaceVisibility.DEMO: - space.authorization = this.extendAuthorizationPolicyLocal( + space.authorization = await this.extendAuthorizationPolicyLocal( space.authorization, space.community.roleSet, spaceSettings, @@ -173,7 +173,7 @@ export class SpaceAuthorizationService { if (privateSpace) { space.authorization.anonymousReadAccess = false; if (space.level !== SpaceLevel.SPACE) { - space.authorization = this.extendPrivateSubspaceAdmins( + space.authorization = await this.extendPrivateSubspaceAdmins( space.authorization, space.community.roleSet, spaceSettings @@ -365,12 +365,12 @@ export class SpaceAuthorizationService { return authorization; } - private extendAuthorizationPolicyLocal( + private async extendAuthorizationPolicyLocal( authorization: IAuthorizationPolicy, roleSet: IRoleSet, spaceSettings: ISpaceSettings, deletionCredentialCriterias: ICredentialDefinition[] - ): IAuthorizationPolicy { + ): Promise { this.extendPrivilegeRuleCreateSubspace(authorization); const newRules: IAuthorizationPolicyRuleCredential[] = []; @@ -386,7 +386,7 @@ export class SpaceAuthorizationService { newRules.push(deleteSubspaces); } - const memberCriteras = this.roleSetService.getCredentialsForRole( + const memberCriteras = await this.roleSetService.getCredentialsForRole( roleSet, CommunityRoleType.MEMBER, spaceSettings @@ -398,7 +398,7 @@ export class SpaceAuthorizationService { ); newRules.push(spaceMember); - const spaceAdminCriterias = this.roleSetService.getCredentialsForRole( + const spaceAdminCriterias = await this.roleSetService.getCredentialsForRole( roleSet, CommunityRoleType.ADMIN, spaceSettings @@ -418,7 +418,10 @@ export class SpaceAuthorizationService { const collaborationSettings = spaceSettings.collaboration; if (collaborationSettings.allowMembersToCreateSubspaces) { - const criteria = this.getContributorCriteria(roleSet, spaceSettings); + const criteria = await this.getContributorCriteria( + roleSet, + spaceSettings + ); const createSubspacePrilegeRule = this.authorizationPolicyService.createCredentialRule( [AuthorizationPrivilege.CREATE_SUBSPACE], @@ -437,11 +440,11 @@ export class SpaceAuthorizationService { return authorization; } - private getContributorCriteria( + private async getContributorCriteria( roleSet: IRoleSet, spaceSettings: ISpaceSettings - ): ICredentialDefinition[] { - const memberCriteria = this.roleSetService.getCredentialsForRole( + ): Promise { + const memberCriteria = await this.roleSetService.getCredentialsForRole( roleSet, CommunityRoleType.MEMBER, spaceSettings @@ -452,7 +455,7 @@ export class SpaceAuthorizationService { spaceSettings.privacy.mode === SpacePrivacyMode.PUBLIC ) { const parentCredential = - this.roleSetService.getDirectParentCredentialForRole( + await this.roleSetService.getDirectParentCredentialForRole( roleSet, CommunityRoleType.MEMBER ); @@ -461,25 +464,25 @@ export class SpaceAuthorizationService { return memberCriteria; } - private extendPrivateSubspaceAdmins( + private async extendPrivateSubspaceAdmins( authorization: IAuthorizationPolicy | undefined, roleSet: IRoleSet, spaceSettings: ISpaceSettings - ): IAuthorizationPolicy { + ): Promise { if (!authorization) throw new EntityNotInitializedException( `Authorization definition not found for: ${JSON.stringify(roleSet)}`, LogContext.SPACES ); const rules: IAuthorizationPolicyRuleCredential[] = []; - - const spaceAdminCriteria = [ - ...this.roleSetService.getCredentialsForRoleWithParents( + const credentials = + await this.roleSetService.getCredentialsForRoleWithParents( roleSet, CommunityRoleType.ADMIN, spaceSettings - ), - ]; + ); + + const spaceAdminCriteria = [...credentials]; const subspaceSpaceAdmins = this.authorizationPolicyService.createCredentialRule( [ diff --git a/src/domain/space/space/space.service.ts b/src/domain/space/space/space.service.ts index a6dd36e222..1c091dc84f 100644 --- a/src/domain/space/space/space.service.ts +++ b/src/domain/space/space/space.service.ts @@ -261,7 +261,7 @@ export class SpaceService { ); } space.community.parentID = space.id; - space.community.roleSet = this.roleSetService.updateRoleResourceID( + space.community.roleSet = await this.roleSetService.updateRoleResourceID( space.community.roleSet, space.id ); @@ -939,7 +939,7 @@ export class SpaceService { agentInfo ); - subspace = this.addSubspaceToSpace(space, subspace); + subspace = await this.addSubspaceToSpace(space, subspace); subspace = await this.save(subspace); // Before assigning roles in the subspace check that the user is a member @@ -957,7 +957,10 @@ export class SpaceService { return subspace; } - public addSubspaceToSpace(space: ISpace, subspace: ISpace): ISpace { + public async addSubspaceToSpace( + space: ISpace, + subspace: ISpace + ): Promise { if (!space.community) { throw new ValidationException( `Unable to add Subspace to space, missing relations: ${space.id}`, @@ -970,7 +973,7 @@ export class SpaceService { subspace.levelZeroSpaceID = space.levelZeroSpaceID; // Finally set the community relationship - subspace.community = this.setCommunityHierarchyForSubspace( + subspace.community = await this.setCommunityHierarchyForSubspace( space.community, subspace.community ); @@ -1192,10 +1195,10 @@ export class SpaceService { return this.spaceSettingsService.getSettings(space.settingsStr); } - public setCommunityHierarchyForSubspace( + public async setCommunityHierarchyForSubspace( parentCommunity: ICommunity, childCommunity: ICommunity | undefined - ): ICommunity { + ): Promise { if (!childCommunity) { throw new RelationshipNotFoundException( `Unable to set subspace community relationship, child community not provied: ${parentCommunity.id}`, @@ -1203,7 +1206,7 @@ export class SpaceService { ); } // Finally set the community relationship - return this.communityService.setParentCommunity( + return await this.communityService.setParentCommunity( childCommunity, parentCommunity ); From 210578656f579bedf325037d63e46cad77b3cd7f Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Fri, 20 Sep 2024 17:26:43 +0200 Subject: [PATCH 12/78] first pass at migration --- src/common/enums/authorization.policy.type.ts | 2 +- .../access/role-set/role.set.service.ts | 2 +- src/migrations/1726843779059-roleSet.ts | 347 ++++++++++++++++++ 3 files changed, 349 insertions(+), 2 deletions(-) create mode 100644 src/migrations/1726843779059-roleSet.ts diff --git a/src/common/enums/authorization.policy.type.ts b/src/common/enums/authorization.policy.type.ts index 8e72829e39..1d01b50468 100644 --- a/src/common/enums/authorization.policy.type.ts +++ b/src/common/enums/authorization.policy.type.ts @@ -22,7 +22,7 @@ export enum AuthorizationPolicyType { ROOM = 'room', AI_PERSONA = 'ai-persona', APPLICATION = 'application', - ROLE_MANAGER = 'role-manager', + ROLE_SET = 'role-set', COMMUNITY = 'community', COMMUNITY_GUIDELINES = 'community-guidelines', INVITATION = 'invitation', diff --git a/src/domain/access/role-set/role.set.service.ts b/src/domain/access/role-set/role.set.service.ts index 2fb7adb944..56610f1c8b 100644 --- a/src/domain/access/role-set/role.set.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -80,7 +80,7 @@ export class RoleSetService { async createRoleSet(roleSetData: CreateRoleSetInput): Promise { const roleSet: IRoleSet = new RoleSet(); roleSet.authorization = new AuthorizationPolicy( - AuthorizationPolicyType.ROLE_MANAGER + AuthorizationPolicyType.ROLE_SET ); roleSet.roles = []; roleSet.applications = []; diff --git a/src/migrations/1726843779059-roleSet.ts b/src/migrations/1726843779059-roleSet.ts new file mode 100644 index 0000000000..b180c4ee0d --- /dev/null +++ b/src/migrations/1726843779059-roleSet.ts @@ -0,0 +1,347 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; +import { randomUUID } from 'crypto'; + +export class RoleSet1726843779059 implements MigrationInterface { + name = 'RoleSet1726843779059'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` DROP FOREIGN KEY \`FK_b3d3f3c2ce851d1059c4ed26ba2\`` + ); + await queryRunner.query( + `ALTER TABLE \`application\` DROP FOREIGN KEY \`FK_500cee6f635849f50e19c7e2b76\`` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` DROP FOREIGN KEY \`FK_339c1fe2a9c5caef5b982303fb0\`` + ); + await queryRunner.query( + `ALTER TABLE \`community\` DROP FOREIGN KEY \`FK_3823de95920943655430125fa93\`` + ); + await queryRunner.query( + `ALTER TABLE \`community\` DROP FOREIGN KEY \`FK_c7d74dd6b92d4202c705cd36769\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_3823de95920943655430125fa9\` ON \`community\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_c7d74dd6b92d4202c705cd3676\` ON \`community\`` + ); + await queryRunner.query( + `CREATE TABLE \`role\` (\`id\` char(36) NOT NULL, + \`createdDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + \`updatedDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + \`version\` int NOT NULL, + \`type\` varchar(128) NOT NULL, + \`credential\` text NOT NULL, \`parentCredentials\` text NOT NULL, + \`requiresBaseRole\` tinyint NOT NULL, + \`requiresParentRole\` tinyint NOT NULL, + \`userPolicy\` text NOT NULL, + \`organizationPolicy\` text NOT NULL, + \`virtualContributorPolicy\` text NOT NULL, + \`roleSetId\` char(36) NULL, + PRIMARY KEY (\`id\`)) ENGINE=InnoDB` + ); + await queryRunner.query( + `CREATE TABLE \`role_set\` (\`id\` char(36) NOT NULL, + \`createdDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + \`updatedDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + \`version\` int NOT NULL, + \`authorizationId\` char(36) NULL, + \`applicationFormId\` char(36) NULL, + \`parentRoleSetId\` char(36) NULL, + UNIQUE INDEX \`REL_b038f74c8d4eadb839e78b99ce\` (\`authorizationId\`), + UNIQUE INDEX \`REL_00905b142498f63e76d38fb254\` (\`applicationFormId\`), + PRIMARY KEY (\`id\`)) ENGINE=InnoDB` + ); + await queryRunner.query( + `ALTER TABLE \`community\` ADD \`roleSetId\` char(36) NULL` + ); + await queryRunner.query( + `ALTER TABLE \`community\` ADD UNIQUE INDEX \`IDX_3b8f390d76263ef5996869da07\` (\`roleSetId\`)` + ); + + // Migrate the data! First loop ensures all are setup. + const communities: { + id: string; + policyId: string; + applicationFormId: string; + }[] = await queryRunner.query( + `SELECT id, policyId, applicationFormId FROM \`community\`` + ); + for (const community of communities) { + const [policy]: { + id: string; + member: string; + lead: string; + admin: string; + }[] = await queryRunner.query( + `SELECT id, community_policy.member, community_policy.lead, community_policy.admin FROM community_policy WHERE id = '${community.policyId}'` + ); + if (!policy) { + throw Error(`No policy found for community: ${community.id}`); + } + const roleSetID = randomUUID(); + const roleSetAuthID = await createAuthorizationPolicy( + queryRunner, + 'role-set' + ); + + await this.createRole( + queryRunner, + 'member', + policy.member, + roleSetID, + 0, + 9, + false, + true + ); + await this.createRole( + queryRunner, + 'lead', + policy.lead, + roleSetID, + 0, + 0, + true, + false + ); + await this.createRole( + queryRunner, + 'admin', + policy.member, + roleSetID, + 0, + 0, + true, + false + ); + + await queryRunner.query( + `INSERT INTO role_set (id, version, authorizationId, applicationFormId) VALUES ('${roleSetID}', 1, '${roleSetAuthID}', '${community.applicationFormId}')` + ); + } + + // Second loop makes the hierarchy linked + const communitiesWithRoleSets: { + id: string; + roleSetId: string; + parentCommunityId: string; + }[] = await queryRunner.query( + `SELECT id, roleSetId, parentCommunityId FROM \`community\`` + ); + for (const community of communitiesWithRoleSets) { + if (!community.parentCommunityId) { + continue; + } + const [parentCommunity]: { + id: string; + roleSetId: string; + }[] = await queryRunner.query( + `SELECT id, roleSetId FROM community WHERE id = '${community.parentCommunityId}'` + ); + if (!parentCommunity) { + throw Error( + `Unable to find parent community for community: ${community.id}` + ); + } + await queryRunner.query( + `UPDATE role_set SET parentRoleSetId = '${parentCommunity.roleSetId}' WHERE id = '${community.roleSetId}'` + ); + } + + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` CHANGE \`communityId\` \`roleSetId\` char(36) NULL` + ); + await queryRunner.query( + `ALTER TABLE \`application\` CHANGE \`communityId\` \`roleSetId\` char(36) NULL` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` CHANGE \`communityId\` \`roleSetId\` char(36) NULL` + ); + + await queryRunner.query( + `ALTER TABLE \`community\` DROP COLUMN \`applicationFormId\`` + ); + await queryRunner.query( + `ALTER TABLE \`community\` DROP COLUMN \`policyId\`` + ); + + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_3b8f390d76263ef5996869da07\` ON \`community\` (\`roleSetId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`role\` ADD CONSTRAINT \`FK_66d695b73839e9b66ff1350d34f\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` ADD CONSTRAINT \`FK_562dce4a08bb214f08107b3631e\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`application\` ADD CONSTRAINT \`FK_8fb220ad1ac1f9c86ec39d134e4\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` ADD CONSTRAINT \`FK_6a3b86c6db10582baae7058f5b9\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`role_set\` ADD CONSTRAINT \`FK_b038f74c8d4eadb839e78b99ce5\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`role_set\` ADD CONSTRAINT \`FK_00905b142498f63e76d38fb254e\` FOREIGN KEY (\`applicationFormId\`) REFERENCES \`form\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`role_set\` ADD CONSTRAINT \`FK_86acc254af20d20c9d87c3503d5\` FOREIGN KEY (\`parentRoleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`community\` ADD CONSTRAINT \`FK_3b8f390d76263ef5996869da071\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + + await queryRunner.query(`DROP TABLE \`community_policy\``); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE \`community\` DROP FOREIGN KEY \`FK_3b8f390d76263ef5996869da071\`` + ); + await queryRunner.query( + `ALTER TABLE \`role_set\` DROP FOREIGN KEY \`FK_86acc254af20d20c9d87c3503d5\`` + ); + await queryRunner.query( + `ALTER TABLE \`role_set\` DROP FOREIGN KEY \`FK_00905b142498f63e76d38fb254e\`` + ); + await queryRunner.query( + `ALTER TABLE \`role_set\` DROP FOREIGN KEY \`FK_b038f74c8d4eadb839e78b99ce5\`` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` DROP FOREIGN KEY \`FK_6a3b86c6db10582baae7058f5b9\`` + ); + await queryRunner.query( + `ALTER TABLE \`application\` DROP FOREIGN KEY \`FK_8fb220ad1ac1f9c86ec39d134e4\`` + ); + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` DROP FOREIGN KEY \`FK_562dce4a08bb214f08107b3631e\`` + ); + await queryRunner.query( + `ALTER TABLE \`role\` DROP FOREIGN KEY \`FK_66d695b73839e9b66ff1350d34f\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_3b8f390d76263ef5996869da07\` ON \`community\`` + ); + await queryRunner.query( + `ALTER TABLE \`community\` DROP INDEX \`IDX_3b8f390d76263ef5996869da07\`` + ); + await queryRunner.query( + `ALTER TABLE \`community\` DROP COLUMN \`roleSetId\`` + ); + await queryRunner.query( + `ALTER TABLE \`community\` ADD \`policyId\` char(36) NULL` + ); + await queryRunner.query( + `ALTER TABLE \`community\` ADD \`applicationFormId\` char(36) NULL` + ); + await queryRunner.query( + `DROP INDEX \`REL_00905b142498f63e76d38fb254\` ON \`role_set\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_b038f74c8d4eadb839e78b99ce\` ON \`role_set\`` + ); + await queryRunner.query(`DROP TABLE \`role_set\``); + await queryRunner.query(`DROP TABLE \`role\``); + await queryRunner.query( + `ALTER TABLE \`invitation\` CHANGE \`roleSetId\` \`communityId\` char(36) NULL` + ); + await queryRunner.query( + `ALTER TABLE \`application\` CHANGE \`roleSetId\` \`communityId\` char(36) NULL` + ); + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` CHANGE \`roleSetId\` \`communityId\` char(36) NULL` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_c7d74dd6b92d4202c705cd3676\` ON \`community\` (\`applicationFormId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_3823de95920943655430125fa9\` ON \`community\` (\`policyId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`community\` ADD CONSTRAINT \`FK_c7d74dd6b92d4202c705cd36769\` FOREIGN KEY (\`applicationFormId\`) REFERENCES \`form\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`community\` ADD CONSTRAINT \`FK_3823de95920943655430125fa93\` FOREIGN KEY (\`policyId\`) REFERENCES \`community_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` ADD CONSTRAINT \`FK_339c1fe2a9c5caef5b982303fb0\` FOREIGN KEY (\`communityId\`) REFERENCES \`community\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`application\` ADD CONSTRAINT \`FK_500cee6f635849f50e19c7e2b76\` FOREIGN KEY (\`communityId\`) REFERENCES \`community\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` ADD CONSTRAINT \`FK_b3d3f3c2ce851d1059c4ed26ba2\` FOREIGN KEY (\`communityId\`) REFERENCES \`community\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + } + + private async createRole( + queryRunner: QueryRunner, + type: string, + communityPolicyStr: string, + roleSetID: string, + vcMin: number, + vcMax: number, + requiresBaseRole: boolean, + requiresParentRole: boolean + ) { + const roleID = randomUUID(); + const communityRolePolicy: CommunityPolicy = JSON.parse(communityPolicyStr); + const userPolicy = { + minimum: communityRolePolicy.minUser, + maximum: communityRolePolicy.maxUser, + }; + const organizationPolicy = { + minimum: communityRolePolicy.minOrg, + maximum: communityRolePolicy.maxOrg, + }; + const vcPolicy = { + minimum: vcMin, + maximum: vcMax, + }; + + // Create the role for the member role + await queryRunner.query(` + INSERT INTO role (id, version, type, credential, parentCredentials, requiresBaseRole, requiresParentRole, userPolicy, organizationPolicy, virtualContributorPolicy, roleSetId) VALUES + ('${roleID}', 1, + '${type}', + '${JSON.stringify(communityRolePolicy.credential)}', + '${JSON.stringify(communityRolePolicy.parentCredentials)}', + ${requiresBaseRole}, + ${requiresParentRole}, + '${JSON.stringify(userPolicy)}', + '${JSON.stringify(organizationPolicy)}', + '${JSON.stringify(vcPolicy)}', + '${roleSetID}')`); + } +} + +const createAuthorizationPolicy = async ( + queryRunner: QueryRunner, + policyType: string +) => { + const authID = randomUUID(); + await queryRunner.query( + `INSERT INTO authorization_policy (id, version, credentialRules, verifiedCredentialRules, anonymousReadAccess, privilegeRules, type) VALUES + ('${authID}', + 1, '', '', 0, '', '${policyType}')` + ); + return authID; +}; + +export type CredentialDefinition = { + type: string; + resourceID: string; +}; +export type CommunityPolicy = { + credential: CredentialDefinition; + parentCredentials: CredentialDefinition[]; + minUser: number; + maxUser: number; + minOrg: number; + maxOrg: number; + enabled: boolean; +}; From 5c1957da556443aa3d263cba50eb7768cb9c2f3b Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Fri, 20 Sep 2024 17:28:07 +0200 Subject: [PATCH 13/78] remove an additional index --- src/migrations/1726843779059-roleSet.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/migrations/1726843779059-roleSet.ts b/src/migrations/1726843779059-roleSet.ts index b180c4ee0d..e2dece10c5 100644 --- a/src/migrations/1726843779059-roleSet.ts +++ b/src/migrations/1726843779059-roleSet.ts @@ -194,6 +194,9 @@ export class RoleSet1726843779059 implements MigrationInterface { await queryRunner.query( `ALTER TABLE \`community\` ADD CONSTRAINT \`FK_3b8f390d76263ef5996869da071\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` ); + await queryRunner.query( + `DROP INDEX \`IDX_3b8f390d76263ef5996869da07\` ON \`community\`` + ); await queryRunner.query(`DROP TABLE \`community_policy\``); } From 67d8ef99b41d5352e88f260154f403b5987581cd Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Fri, 20 Sep 2024 18:12:19 +0200 Subject: [PATCH 14/78] parent role not required if the community does not have a parent --- src/migrations/1726843779059-roleSet.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/migrations/1726843779059-roleSet.ts b/src/migrations/1726843779059-roleSet.ts index e2dece10c5..524bbc8bed 100644 --- a/src/migrations/1726843779059-roleSet.ts +++ b/src/migrations/1726843779059-roleSet.ts @@ -65,8 +65,9 @@ export class RoleSet1726843779059 implements MigrationInterface { id: string; policyId: string; applicationFormId: string; + parentCommunityId: string; }[] = await queryRunner.query( - `SELECT id, policyId, applicationFormId FROM \`community\`` + `SELECT id, policyId, applicationFormId, parentCommunityId FROM \`community\`` ); for (const community of communities) { const [policy]: { @@ -85,6 +86,10 @@ export class RoleSet1726843779059 implements MigrationInterface { queryRunner, 'role-set' ); + let memberRequiresParentRole = true; + if (!community.parentCommunityId) { + memberRequiresParentRole = false; + } await this.createRole( queryRunner, @@ -94,7 +99,7 @@ export class RoleSet1726843779059 implements MigrationInterface { 0, 9, false, - true + memberRequiresParentRole ); await this.createRole( queryRunner, From 16c2be0ffac627d330f2af43578a5f610417b714 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Sat, 21 Sep 2024 09:12:08 +0200 Subject: [PATCH 15/78] fixes to api as work through client codegen fixes --- .../validation/handlers/base/base.handler.ts | 9 +- ...ts => role.set.dto.apply.for.base.role.ts} | 2 +- ...s => role.set.dto.invite.for.base.role.ts} | 2 +- .../role-set/role.set.resolver.fields.ts | 147 ++++++++---------- .../role-set/role.set.resolver.mutations.ts | 67 ++------ .../community/community.resolver.mutations.ts | 92 ++++++++++- src/services/api/lookup/lookup.module.ts | 2 + .../api/lookup/lookup.resolver.fields.ts | 25 ++- 8 files changed, 196 insertions(+), 150 deletions(-) rename src/domain/access/role-set/dto/{role.set.dto.apply.ts => role.set.dto.apply.for.base.role.ts} (92%) rename src/domain/access/role-set/dto/{role.set.dto.invite.contributor.ts => role.set.dto.invite.for.base.role.ts} (92%) diff --git a/src/core/validation/handlers/base/base.handler.ts b/src/core/validation/handlers/base/base.handler.ts index ade6dd8cfa..255e42a1b8 100644 --- a/src/core/validation/handlers/base/base.handler.ts +++ b/src/core/validation/handlers/base/base.handler.ts @@ -71,10 +71,9 @@ import { CreateAccountInput } from '@domain/space/account/dto'; import { UpdateCommunityGuidelinesInput } from '@domain/community/community-guidelines/dto/community.guidelines.dto.update'; import { ForumCreateDiscussionInput } from '@platform/forum/dto/forum.dto.create.discussion'; import { CreateCollaborationOnSpaceInput } from '@domain/space/space/dto/space.dto.create.collaboration'; -import { UpdateInnovationFlowEntityInput } from '@domain/collaboration/innovation-flow/dto/innovation.flow.dto.update.entity'; -import { ApplyForRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.apply'; -import { InviteContributorForRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.invite.contributor'; import { InviteNewContributorForRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.platform.invitation.community'; +import { InviteForBaseRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.invite.for.base.role'; +import { ApplyForBaseRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.apply.for.base.role'; export class BaseHandler extends AbstractHandler { public async handle( @@ -131,8 +130,8 @@ export class BaseHandler extends AbstractHandler { UpdateSpaceSettingsEntityInput, UpdateSpaceSettingsInput, VisualUploadImageInput, - ApplyForRoleOnRoleSetInput, - InviteContributorForRoleOnRoleSetInput, + ApplyForBaseRoleOnRoleSetInput, + InviteForBaseRoleOnRoleSetInput, InviteNewContributorForRoleOnRoleSetInput, ForumCreateDiscussionInput, SendMessageOnCalloutInput, diff --git a/src/domain/access/role-set/dto/role.set.dto.apply.ts b/src/domain/access/role-set/dto/role.set.dto.apply.for.base.role.ts similarity index 92% rename from src/domain/access/role-set/dto/role.set.dto.apply.ts rename to src/domain/access/role-set/dto/role.set.dto.apply.for.base.role.ts index a8de5dcdeb..d5fb66d9e3 100644 --- a/src/domain/access/role-set/dto/role.set.dto.apply.ts +++ b/src/domain/access/role-set/dto/role.set.dto.apply.for.base.role.ts @@ -6,7 +6,7 @@ import { Type } from 'class-transformer'; import { UUID_LENGTH } from '@common/constants'; @InputType() -export class ApplyForRoleOnRoleSetInput { +export class ApplyForBaseRoleOnRoleSetInput { @Field(() => UUID, { nullable: false }) @MaxLength(UUID_LENGTH) roleSetID!: string; diff --git a/src/domain/access/role-set/dto/role.set.dto.invite.contributor.ts b/src/domain/access/role-set/dto/role.set.dto.invite.for.base.role.ts similarity index 92% rename from src/domain/access/role-set/dto/role.set.dto.invite.contributor.ts rename to src/domain/access/role-set/dto/role.set.dto.invite.for.base.role.ts index 9fabc1cc99..6dd2d7c8dd 100644 --- a/src/domain/access/role-set/dto/role.set.dto.invite.contributor.ts +++ b/src/domain/access/role-set/dto/role.set.dto.invite.for.base.role.ts @@ -4,7 +4,7 @@ import { IsOptional, MaxLength } from 'class-validator'; import { MID_TEXT_LENGTH, UUID_LENGTH } from '@common/constants'; @InputType() -export class InviteContributorForRoleOnRoleSetInput { +export class InviteForBaseRoleOnRoleSetInput { @Field(() => UUID, { nullable: false }) @MaxLength(UUID_LENGTH) roleSetID!: string; diff --git a/src/domain/access/role-set/role.set.resolver.fields.ts b/src/domain/access/role-set/role.set.resolver.fields.ts index bd56e282fc..a141a5b303 100644 --- a/src/domain/access/role-set/role.set.resolver.fields.ts +++ b/src/domain/access/role-set/role.set.resolver.fields.ts @@ -1,7 +1,7 @@ import { GraphqlGuard } from '@core/authorization'; import { UseGuards } from '@nestjs/common'; import { Args, Float, Parent, ResolveField, Resolver } from '@nestjs/graphql'; -import { AuthorizationAgentPrivilege, Profiling } from '@src/common/decorators'; +import { AuthorizationAgentPrivilege } from '@src/common/decorators'; import { RoleSetService } from './role.set.service'; import { IForm } from '@domain/common/form/form.interface'; import { IRoleSet } from './role.set.interface'; @@ -28,50 +28,18 @@ export class RoleSetResolverFields { private userService: UserService ) {} - @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) - @UseGuards(GraphqlGuard) - @ResolveField('memberUsers', () => [IUser], { - nullable: false, - description: 'All users that are contributing to this Community.', - }) - @Profiling.api - async memberUsers( - @Parent() roleSet: IRoleSet, - @Args({ - name: 'limit', - type: () => Float, - description: - 'The positive number of member users to return; if omitted returns all member users.', - nullable: true, - }) - limit?: number - ) { - if (limit && limit < 0) { - throw new PaginationInputOutOfBoundException( - `Limit expects a positive amount: ${limit} provided instead` - ); - } - - return await this.roleSetService.getUsersWithRole( - roleSet, - CommunityRoleType.MEMBER, - limit - ); - } - @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) @UseGuards(GraphqlGuard) @ResolveField('availableMemberUsers', () => PaginatedUsers, { nullable: false, description: 'All available users that are potential Community members.', }) - @Profiling.api - async availableMemberUsers( + async availableUsersForMemberRole( @Parent() roleSet: IRoleSet, @Args({ nullable: true }) pagination: PaginationArgs, @Args('filter', { nullable: true }) filter?: UserFilterInput ) { - const memberRoleCredentials = + const roleDefinition = await this.roleSetService.getCredentialDefinitionForRole( roleSet, CommunityRoleType.MEMBER @@ -87,7 +55,7 @@ export class RoleSetResolverFields { : undefined; const roleSetMemberCredentials = { - member: memberRoleCredentials, + member: roleDefinition, parentCommunityMember: parentCommunityMemberCredentials, }; @@ -98,19 +66,71 @@ export class RoleSetResolverFields { ); } + @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) + @UseGuards(GraphqlGuard) + @ResolveField('availableLeadUsers', () => PaginatedUsers, { + nullable: false, + description: + 'All users excluding the current lead users in this Community.', + }) + async availableUsersForLeadRole( + @Parent() roleSet: IRoleSet, + @Args('role', { type: () => CommunityRoleType, nullable: false }) + role: CommunityRoleType, + @Args({ nullable: true }) pagination: PaginationArgs, + @Args('filter', { nullable: true }) filter?: UserFilterInput + ) { + const memberRoleCredentials = + await this.roleSetService.getCredentialDefinitionForRole( + roleSet, + CommunityRoleType.MEMBER + ); + + const leadRoleCredential = + await this.roleSetService.getCredentialDefinitionForRole( + roleSet, + CommunityRoleType.LEAD + ); + + const credentialCriteria = { + member: memberRoleCredentials, + lead: leadRoleCredential, + }; + + return this.userService.getPaginatedAvailableLeadUsers( + credentialCriteria, + pagination, + filter + ); + } + @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) @UseGuards(GraphqlGuard) @ResolveField('usersInRole', () => [IUser], { nullable: false, - description: 'All users that have the specified Role in this Community.', + description: + 'All users that are contributing to this Community in the specified Role.', }) - @Profiling.api async usersInRole( @Parent() roleSet: IRoleSet, @Args('role', { type: () => CommunityRoleType, nullable: false }) - role: CommunityRoleType - ) { - return await this.roleSetService.getUsersWithRole(roleSet, role); + role: CommunityRoleType, + @Args({ + name: 'limit', + type: () => Float, + description: + 'The positive number of users to return; if omitted returns all users in the specified role.', + nullable: true, + }) + limit?: number + ): Promise { + if (limit && limit < 0) { + throw new PaginationInputOutOfBoundException( + `Limit expects a positive amount: ${limit} provided instead` + ); + } + + return await this.roleSetService.getUsersWithRole(roleSet, role, limit); } @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) @@ -120,7 +140,6 @@ export class RoleSetResolverFields { description: 'All Organizations that have the specified Role in this Community.', }) - @Profiling.api async organizationsInRole( @Parent() roleSet: IRoleSet, @Args('role', { type: () => CommunityRoleType, nullable: false }) @@ -135,8 +154,7 @@ export class RoleSetResolverFields { nullable: false, description: 'All virtuals that have the specified Role in this Community.', }) - @Profiling.api - async virtualsInRole( + async virtualContributorsInRole( @Parent() roleSet: IRoleSet, @Args('role', { type: () => CommunityRoleType, nullable: false }) role: CommunityRoleType @@ -147,50 +165,12 @@ export class RoleSetResolverFields { ); } - @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) - @UseGuards(GraphqlGuard) - @ResolveField('availableLeadUsers', () => PaginatedUsers, { - nullable: false, - description: - 'All member users excluding the current lead users in this Community.', - }) - @Profiling.api - async availableLeadUsers( - @Parent() roleSet: IRoleSet, - @Args({ nullable: true }) pagination: PaginationArgs, - @Args('filter', { nullable: true }) filter?: UserFilterInput - ) { - const memberRoleCredentials = - await this.roleSetService.getCredentialDefinitionForRole( - roleSet, - CommunityRoleType.MEMBER - ); - - const leadRoleCredential = - await this.roleSetService.getCredentialDefinitionForRole( - roleSet, - CommunityRoleType.LEAD - ); - - const credentialCriteria = { - member: memberRoleCredentials, - lead: leadRoleCredential, - }; - - return this.userService.getPaginatedAvailableLeadUsers( - credentialCriteria, - pagination, - filter - ); - } - @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) @UseGuards(GraphqlGuard) @ResolveField('invitations', () => [IInvitation], { nullable: false, description: 'Invitations for this roleSet.', }) - @Profiling.api async inivitations(@Parent() roleSet: IRoleSet): Promise { return await this.roleSetService.getInvitations(roleSet); } @@ -202,7 +182,6 @@ export class RoleSetResolverFields { description: 'Invitations to join this Community for users not yet on the Alkemio platform.', }) - @Profiling.api async platformInvitations( @Parent() roleSet: IRoleSet ): Promise { @@ -225,7 +204,6 @@ export class RoleSetResolverFields { nullable: false, description: 'The Form used for Applications to this roleSet.', }) - @Profiling.api async applicationForm(@Parent() roleSet: RoleSet): Promise { return await this.roleSetService.getApplicationForm(roleSet); } @@ -235,7 +213,6 @@ export class RoleSetResolverFields { nullable: false, description: 'The Role Definitions included in this roleSet.', }) - @Profiling.api async roles(@Parent() roleSet: RoleSet): Promise { return await this.roleSetService.getRoleDefinitions(roleSet); } diff --git a/src/domain/access/role-set/role.set.resolver.mutations.ts b/src/domain/access/role-set/role.set.resolver.mutations.ts index 7bed937410..448ae6e933 100644 --- a/src/domain/access/role-set/role.set.resolver.mutations.ts +++ b/src/domain/access/role-set/role.set.resolver.mutations.ts @@ -14,8 +14,6 @@ import { ApplicationEventInput } from '../application/dto/application.dto.event' import { IApplication } from '../application/application.interface'; import { CommunityRoleType } from '@common/enums/community.role'; import { RoleSetMembershipException } from '@common/exceptions/role.set.membership.exception'; -import { JoinAsBaseRoleOnRoleSetInput } from './dto/role.set.dto.join'; -import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; import { NotificationInputPlatformInvitation } from '@services/adapters/notification-adapter/dto/notification.dto.input.platform.invitation'; import { ApplicationService } from '../application/application.service'; import { ApplicationAuthorizationService } from '../application/application.service.authorization'; @@ -41,9 +39,9 @@ import { AssignRoleOnRoleSetToVirtualContributorInput } from './dto/role.set.dto import { RemoveRoleOnRoleSetFromUserInput } from './dto/role.set.dto.role.remove.user'; import { RemoveRoleOnRoleSetFromOrganizationInput } from './dto/role.set.dto.role.remove.organization'; import { RemoveRoleOnRoleSetFromVirtualContributorInput } from './dto/role.set.dto.role.remove.virtual'; -import { ApplyForRoleOnRoleSetInput } from './dto/role.set.dto.apply'; +import { ApplyForBaseRoleOnRoleSetInput } from './dto/role.set.dto.apply.for.base.role'; import { NotificationInputCommunityApplication } from '@services/adapters/notification-adapter/dto/notification.dto.input.community.application'; -import { InviteContributorForRoleOnRoleSetInput } from './dto/role.set.dto.invite.contributor'; +import { InviteForBaseRoleOnRoleSetInput } from './dto/role.set.dto.invite.for.base.role'; import { RoleSetInvitationException } from '@common/exceptions/role.set.invitation.exception'; import { IContributor } from '@domain/community/contributor/contributor.interface'; import { EntityNotInitializedException } from '@common/exceptions/entity.not.initialized.exception'; @@ -82,7 +80,7 @@ export class RoleSetResolverMutations { @Mutation(() => IUser, { description: 'Assigns a User to a role in the specified Community.', }) - async assignCommunityRoleToUser( + async assignRoleToUser( @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: AssignRoleOnRoleSetToUserInput ): Promise { @@ -122,7 +120,7 @@ export class RoleSetResolverMutations { @Mutation(() => IOrganization, { description: 'Assigns an Organization a Role in the specified Community.', }) - async assignCommunityRoleToOrganization( + async assignRoleToOrganization( @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: AssignRoleOnRoleSetToOrganizationInput @@ -149,7 +147,7 @@ export class RoleSetResolverMutations { description: 'Assigns a Virtual Contributor to a role in the specified Community.', }) - async assignCommunityRoleToVirtual( + async assignRoleToVirtual( @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: AssignRoleOnRoleSetToVirtualContributorInput ): Promise { @@ -204,7 +202,7 @@ export class RoleSetResolverMutations { @Mutation(() => IUser, { description: 'Removes a User from a Role in the specified Community.', }) - async removeCommunityRoleFromUser( + async removeRoleFromUser( @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: RemoveRoleOnRoleSetFromUserInput ): Promise { @@ -250,7 +248,7 @@ export class RoleSetResolverMutations { description: 'Removes an Organization from a Role in the specified Community.', }) - async removeCommunityRoleFromOrganization( + async removeRoleFromOrganization( @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: RemoveRoleOnRoleSetFromOrganizationInput ): Promise { @@ -275,7 +273,7 @@ export class RoleSetResolverMutations { @Mutation(() => IVirtualContributor, { description: 'Removes a Virtual from a Role in the specified Community.', }) - async removeCommunityRoleFromVirtual( + async removeRoleFromVirtual( @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: RemoveRoleOnRoleSetFromVirtualContributorInput ): Promise { @@ -314,9 +312,9 @@ export class RoleSetResolverMutations { @Mutation(() => IApplication, { description: 'Apply to join the specified Community as a member.', }) - async applyForCommunityMembership( + async applyForBaseRoleOnRoleSet( @CurrentUser() agentInfo: AgentInfo, - @Args('applicationData') applicationData: ApplyForRoleOnRoleSetInput + @Args('applicationData') applicationData: ApplyForBaseRoleOnRoleSetInput ): Promise { const roleSet = await this.roleSetService.getRoleSetOrFail( applicationData.roleSetID, @@ -387,7 +385,7 @@ export class RoleSetResolverMutations { async inviteContributorsForCommunityMembership( @CurrentUser() agentInfo: AgentInfo, @Args('invitationData') - invitationData: InviteContributorForRoleOnRoleSetInput + invitationData: InviteForBaseRoleOnRoleSetInput ): Promise { const roleSet = await this.roleSetService.getRoleSetOrFail( invitationData.roleSetID, @@ -627,47 +625,6 @@ export class RoleSetResolverMutations { return platformInvitation; } - @UseGuards(GraphqlGuard) - @Mutation(() => IRoleSet, { - description: - 'Join the specified Community as a member, without going through an approval process.', - }) - async joinRoleSet( - @CurrentUser() agentInfo: AgentInfo, - @Args('joinCommunityData') joiningData: JoinAsBaseRoleOnRoleSetInput - ): Promise { - const roleSet = await this.roleSetService.getRoleSetOrFail( - joiningData.roleSetID - ); - const membershipStatus = await this.roleSetService.getMembershipStatus( - agentInfo, - roleSet - ); - if (membershipStatus === CommunityMembershipStatus.INVITATION_PENDING) { - throw new RoleSetMembershipException( - `Unable to join Community (${roleSet.id}): invitation to join is pending.`, - LogContext.COMMUNITY - ); - } - - await this.authorizationService.grantAccessOrFail( - agentInfo, - roleSet.authorization, - AuthorizationPrivilege.COMMUNITY_JOIN, - `join community: ${roleSet.id}` - ); - - await this.roleSetService.assignUserToRole( - roleSet, - CommunityRoleType.MEMBER, - agentInfo.userID, - agentInfo, - true - ); - - return roleSet; - } - @UseGuards(GraphqlGuard) @Mutation(() => IApplication, { description: 'Trigger an event on the Application.', @@ -720,7 +677,7 @@ export class RoleSetResolverMutations { @Mutation(() => IRoleSet, { description: 'Update the Application Form used by this RoleSet.', }) - async updateRoleSetApplicationForm( + async updateApplicationFormOnRoleSet( @CurrentUser() agentInfo: AgentInfo, @Args('applicationFormData') applicationFormData: UpdateApplicationFormOnRoleSetInput diff --git a/src/domain/community/community/community.resolver.mutations.ts b/src/domain/community/community/community.resolver.mutations.ts index 94179c953c..d8c1b71d04 100644 --- a/src/domain/community/community/community.resolver.mutations.ts +++ b/src/domain/community/community/community.resolver.mutations.ts @@ -5,12 +5,21 @@ import { CommunityService } from './community.service'; import { CurrentUser, Profiling } from '@src/common/decorators'; import { GraphqlGuard } from '@core/authorization'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; -import { AuthorizationPrivilege } from '@common/enums'; +import { AuthorizationPrivilege, LogContext } from '@common/enums'; import { AuthorizationService } from '@core/authorization/authorization.service'; import { UserGroupAuthorizationService } from '../user-group/user-group.service.authorization'; import { AgentService } from '@domain/agent/agent/agent.service'; import { CreateUserGroupInput } from '../user-group/dto'; import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service'; +import { AgentBeginVerifiedCredentialOfferOutput } from '@domain/agent/agent/dto/agent.dto.verified.credential.offer.begin.output'; +import { AlkemioUserClaim } from '@services/external/trust-registry/trust.registry.claim/claim.alkemio.user'; +import { CommunityMemberClaim } from '@services/external/trust-registry/trust.registry.claim/claim.community.member'; +import { IRoleSet } from '@domain/access/role-set/role.set.interface'; +import { JoinAsBaseRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.join'; +import { RoleSetService } from '@domain/access/role-set/role.set.service'; +import { RoleSetMembershipException } from '@common/exceptions/role.set.membership.exception'; +import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; +import { CommunityRoleType } from '@common/enums/community.role'; @Resolver() export class CommunityResolverMutations { @@ -19,7 +28,8 @@ export class CommunityResolverMutations { private authorizationPolicyService: AuthorizationPolicyService, private userGroupAuthorizationService: UserGroupAuthorizationService, private communityService: CommunityService, - private agentService: AgentService + private agentService: AgentService, + private roleSetService: RoleSetService ) {} @UseGuards(GraphqlGuard) @@ -49,4 +59,82 @@ export class CommunityResolverMutations { await this.authorizationPolicyService.saveAll(authorizations); return group; } + + @UseGuards(GraphqlGuard) + @Mutation(() => AgentBeginVerifiedCredentialOfferOutput, { + description: 'Generate community member credential offer', + }) + async beginCommunityMemberVerifiedCredentialOfferInteraction( + @Args({ name: 'communityID', type: () => String }) communityID: string, + @CurrentUser() agentInfo: AgentInfo + ): Promise { + const community = + await this.communityService.getCommunityOrFail(communityID); + await this.authorizationService.grantAccessOrFail( + agentInfo, + community.authorization, + AuthorizationPrivilege.READ, + `beginCommunityMemberCredentialOfferInteraction: ${community.id}` + ); + + return await this.agentService.beginCredentialOfferInteraction( + agentInfo.agentID, + [ + { + type: 'CommunityMemberCredential', + claims: [ + new AlkemioUserClaim({ + userID: agentInfo.userID, + email: agentInfo.email, + }), + new CommunityMemberClaim({ + communityID: community.id, + communityDisplayName: community.id, + }), + ], + }, + ] + ); + } + + @UseGuards(GraphqlGuard) + @Mutation(() => IRoleSet, { + description: + 'Join the specified Community as a member, without going through an approval process.', + }) + async joinCommunity( + @CurrentUser() agentInfo: AgentInfo, + @Args('joinCommunityData') joiningData: JoinAsBaseRoleOnRoleSetInput + ): Promise { + const roleSet = await this.roleSetService.getRoleSetOrFail( + joiningData.roleSetID + ); + const membershipStatus = await this.roleSetService.getMembershipStatus( + agentInfo, + roleSet + ); + if (membershipStatus === CommunityMembershipStatus.INVITATION_PENDING) { + throw new RoleSetMembershipException( + `Unable to join Community (${roleSet.id}): invitation to join is pending.`, + LogContext.COMMUNITY + ); + } + + await this.authorizationService.grantAccessOrFail( + agentInfo, + roleSet.authorization, + AuthorizationPrivilege.COMMUNITY_JOIN, + `join community: ${roleSet.id}` + ); + + await this.roleSetService.assignUserToRole( + roleSet, + CommunityRoleType.MEMBER, + agentInfo.userID, + agentInfo, + true + ); + + return roleSet; + } } diff --git a/src/services/api/lookup/lookup.module.ts b/src/services/api/lookup/lookup.module.ts index d46d18322e..e0cddf9c6e 100644 --- a/src/services/api/lookup/lookup.module.ts +++ b/src/services/api/lookup/lookup.module.ts @@ -30,6 +30,7 @@ import { InnovationPackModule } from '@library/innovation-pack/innovation.pack.m import { AccountModule } from '@domain/space/account/account.module'; import { TemplateModule } from '@domain/template/template/template.module'; import { TemplatesSetModule } from '@domain/template/templates-set/templates.set.module'; +import { RoleSetModule } from '@domain/access/role-set/role.set.module'; @Module({ imports: [ @@ -61,6 +62,7 @@ import { TemplatesSetModule } from '@domain/template/templates-set/templates.set SpaceModule, CommunityGuidelinesModule, VirtualContributorModule, + RoleSetModule, ], providers: [LookupService, LookupResolverQueries, LookupResolverFields], exports: [LookupService], diff --git a/src/services/api/lookup/lookup.resolver.fields.ts b/src/services/api/lookup/lookup.resolver.fields.ts index 2b8e7c859b..f88ef41c4b 100644 --- a/src/services/api/lookup/lookup.resolver.fields.ts +++ b/src/services/api/lookup/lookup.resolver.fields.ts @@ -60,6 +60,8 @@ import { TemplateService } from '@domain/template/template/template.service'; import { ITemplate } from '@domain/template/template/template.interface'; import { ITemplatesSet } from '@domain/template/templates-set/templates.set.interface'; import { TemplatesSetService } from '@domain/template/templates-set/templates.set.service'; +import { RoleSetService } from '@domain/access/role-set/role.set.service'; +import { IRoleSet } from '@domain/access/role-set/role.set.interface'; @Resolver(() => LookupQueryResults) export class LookupResolverFields { @@ -91,7 +93,8 @@ export class LookupResolverFields { private userService: UserService, private guidelinesService: CommunityGuidelinesService, private virtualContributorService: VirtualContributorService, - private innovationHubService: InnovationHubService + private innovationHubService: InnovationHubService, + private roleSetService: RoleSetService ) {} @UseGuards(GraphqlGuard) @@ -135,6 +138,26 @@ export class LookupResolverFields { return account; } + @UseGuards(GraphqlGuard) + @ResolveField(() => IRoleSet, { + nullable: true, + description: 'Lookup the specified RoleSet', + }) + async roleSet( + @CurrentUser() agentInfo: AgentInfo, + @Args('ID', { type: () => UUID }) id: string + ): Promise { + const roleSet = await this.roleSetService.getRoleSetOrFail(id); + this.authorizationService.grantAccessOrFail( + agentInfo, + roleSet.authorization, + AuthorizationPrivilege.READ, + `lookup RoleSet: ${roleSet.id}` + ); + + return roleSet; + } + @UseGuards(GraphqlGuard) @ResolveField(() => IDocument, { nullable: true, From a54975f775aaf76498da1168c1193eee8c81ebc2 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Sat, 21 Sep 2024 10:41:46 +0200 Subject: [PATCH 16/78] fixes to api as work through client codegen fixes --- .../role-set/role.set.resolver.fields.ts | 23 ++++++++++++++----- .../role-set/role.set.resolver.mutations.ts | 10 ++++---- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/domain/access/role-set/role.set.resolver.fields.ts b/src/domain/access/role-set/role.set.resolver.fields.ts index a141a5b303..89f5353ba3 100644 --- a/src/domain/access/role-set/role.set.resolver.fields.ts +++ b/src/domain/access/role-set/role.set.resolver.fields.ts @@ -30,7 +30,7 @@ export class RoleSetResolverFields { @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) @UseGuards(GraphqlGuard) - @ResolveField('availableMemberUsers', () => PaginatedUsers, { + @ResolveField('availableUsersForMemberRole', () => PaginatedUsers, { nullable: false, description: 'All available users that are potential Community members.', }) @@ -68,15 +68,13 @@ export class RoleSetResolverFields { @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) @UseGuards(GraphqlGuard) - @ResolveField('availableLeadUsers', () => PaginatedUsers, { + @ResolveField('availableUsersForLeadRole', () => PaginatedUsers, { nullable: false, description: 'All users excluding the current lead users in this Community.', }) async availableUsersForLeadRole( @Parent() roleSet: IRoleSet, - @Args('role', { type: () => CommunityRoleType, nullable: false }) - role: CommunityRoleType, @Args({ nullable: true }) pagination: PaginationArgs, @Args('filter', { nullable: true }) filter?: UserFilterInput ) { @@ -209,11 +207,24 @@ export class RoleSetResolverFields { } @UseGuards(GraphqlGuard) - @ResolveField('roles', () => [IRole], { + @ResolveField('roleDefinitions', () => [IRole], { nullable: false, description: 'The Role Definitions included in this roleSet.', }) - async roles(@Parent() roleSet: RoleSet): Promise { + async roleDefinitions(@Parent() roleSet: RoleSet): Promise { return await this.roleSetService.getRoleDefinitions(roleSet); } + + @UseGuards(GraphqlGuard) + @ResolveField('roleDefinition', () => IRole, { + nullable: false, + description: 'The Role Definitions from this RoleSet to return.', + }) + async roleDefinition( + @Parent() roleSet: RoleSet, + @Args('role', { type: () => CommunityRoleType, nullable: false }) + role: CommunityRoleType + ): Promise { + return await this.roleSetService.getRoleDefinition(roleSet, role); + } } diff --git a/src/domain/access/role-set/role.set.resolver.mutations.ts b/src/domain/access/role-set/role.set.resolver.mutations.ts index 448ae6e933..5246b043b3 100644 --- a/src/domain/access/role-set/role.set.resolver.mutations.ts +++ b/src/domain/access/role-set/role.set.resolver.mutations.ts @@ -147,7 +147,7 @@ export class RoleSetResolverMutations { description: 'Assigns a Virtual Contributor to a role in the specified Community.', }) - async assignRoleToVirtual( + async assignRoleToVirtualContributor( @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: AssignRoleOnRoleSetToVirtualContributorInput ): Promise { @@ -273,7 +273,7 @@ export class RoleSetResolverMutations { @Mutation(() => IVirtualContributor, { description: 'Removes a Virtual from a Role in the specified Community.', }) - async removeRoleFromVirtual( + async removeRoleFromVirtualContributor( @CurrentUser() agentInfo: AgentInfo, @Args('roleData') roleData: RemoveRoleOnRoleSetFromVirtualContributorInput ): Promise { @@ -382,7 +382,7 @@ export class RoleSetResolverMutations { description: 'Invite an existing Contriburor to join the specified Community as a member.', }) - async inviteContributorsForCommunityMembership( + async inviteContributorsForRoleSetMembership( @CurrentUser() agentInfo: AgentInfo, @Args('invitationData') invitationData: InviteForBaseRoleOnRoleSetInput @@ -536,9 +536,9 @@ export class RoleSetResolverMutations { @UseGuards(GraphqlGuard) @Mutation(() => IPlatformInvitation, { description: - 'Invite a User to join the platform and the specified Community as a member.', + 'Invite a User to join the platform and the specified RoleSet as a member.', }) - async inviteUserToPlatformAndCommunity( + async inviteUserToPlatformAndRoleSet( @CurrentUser() agentInfo: AgentInfo, @Args('invitationData') invitationData: InviteNewContributorForRoleOnRoleSetInput From 2e7d6a100c64ae9434aa6a282bd28a567e8d2057 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Sat, 21 Sep 2024 11:51:52 +0200 Subject: [PATCH 17/78] tidied up DTO fields for RoleSet mgmt --- .../role.set.dto.role.assign.organization.ts | 6 ++-- .../dto/role.set.dto.role.assign.user.ts | 6 ++-- .../dto/role.set.dto.role.assign.virtual.ts | 6 ++-- .../role.set.dto.role.remove.organization.ts | 6 ++-- .../dto/role.set.dto.role.remove.user.ts | 6 ++-- .../dto/role.set.dto.role.remove.virtual.ts | 6 ++-- .../role-set/role.set.resolver.mutations.ts | 30 +++++++++---------- 7 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/domain/access/role-set/dto/role.set.dto.role.assign.organization.ts b/src/domain/access/role-set/dto/role.set.dto.role.assign.organization.ts index 4b3b24ed22..7a2584c27a 100644 --- a/src/domain/access/role-set/dto/role.set.dto.role.assign.organization.ts +++ b/src/domain/access/role-set/dto/role.set.dto.role.assign.organization.ts @@ -1,5 +1,5 @@ import { CommunityRoleType } from '@common/enums/community.role'; -import { UUID, UUID_NAMEID } from '@domain/common/scalars'; +import { UUID } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @InputType() @@ -7,8 +7,8 @@ export class AssignRoleOnRoleSetToOrganizationInput { @Field(() => UUID, { nullable: false }) roleSetID!: string; - @Field(() => UUID_NAMEID, { nullable: false }) - organizationID!: string; + @Field(() => UUID, { nullable: false }) + contributorID!: string; @Field(() => CommunityRoleType, { nullable: false }) role!: CommunityRoleType; diff --git a/src/domain/access/role-set/dto/role.set.dto.role.assign.user.ts b/src/domain/access/role-set/dto/role.set.dto.role.assign.user.ts index 64142f03ec..223bc1068e 100644 --- a/src/domain/access/role-set/dto/role.set.dto.role.assign.user.ts +++ b/src/domain/access/role-set/dto/role.set.dto.role.assign.user.ts @@ -1,5 +1,5 @@ import { CommunityRoleType } from '@common/enums/community.role'; -import { UUID, UUID_NAMEID_EMAIL } from '@domain/common/scalars'; +import { UUID } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @InputType() @@ -7,8 +7,8 @@ export class AssignRoleOnRoleSetToUserInput { @Field(() => UUID, { nullable: false }) roleSetID!: string; - @Field(() => UUID_NAMEID_EMAIL, { nullable: false }) - userID!: string; + @Field(() => UUID, { nullable: false }) + contributorID!: string; @Field(() => CommunityRoleType, { nullable: false }) role!: CommunityRoleType; diff --git a/src/domain/access/role-set/dto/role.set.dto.role.assign.virtual.ts b/src/domain/access/role-set/dto/role.set.dto.role.assign.virtual.ts index e63bea8a66..9db2e07fc0 100644 --- a/src/domain/access/role-set/dto/role.set.dto.role.assign.virtual.ts +++ b/src/domain/access/role-set/dto/role.set.dto.role.assign.virtual.ts @@ -1,5 +1,5 @@ import { CommunityRoleType } from '@common/enums/community.role'; -import { UUID, UUID_NAMEID } from '@domain/common/scalars'; +import { UUID } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @InputType() @@ -7,8 +7,8 @@ export class AssignRoleOnRoleSetToVirtualContributorInput { @Field(() => UUID, { nullable: false }) roleSetID!: string; - @Field(() => UUID_NAMEID, { nullable: false }) - virtualContributorID!: string; + @Field(() => UUID, { nullable: false }) + contributorID!: string; @Field(() => CommunityRoleType, { nullable: false }) role!: CommunityRoleType; diff --git a/src/domain/access/role-set/dto/role.set.dto.role.remove.organization.ts b/src/domain/access/role-set/dto/role.set.dto.role.remove.organization.ts index d1c3ed5167..fbe1dfe9d3 100644 --- a/src/domain/access/role-set/dto/role.set.dto.role.remove.organization.ts +++ b/src/domain/access/role-set/dto/role.set.dto.role.remove.organization.ts @@ -1,5 +1,5 @@ import { CommunityRoleType } from '@common/enums/community.role'; -import { UUID, UUID_NAMEID } from '@domain/common/scalars'; +import { UUID } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @InputType() @@ -7,8 +7,8 @@ export class RemoveRoleOnRoleSetFromOrganizationInput { @Field(() => UUID, { nullable: false }) roleSetID!: string; - @Field(() => UUID_NAMEID, { nullable: false }) - organizationID!: string; + @Field(() => UUID, { nullable: false }) + contributorID!: string; @Field(() => CommunityRoleType, { nullable: false }) role!: CommunityRoleType; diff --git a/src/domain/access/role-set/dto/role.set.dto.role.remove.user.ts b/src/domain/access/role-set/dto/role.set.dto.role.remove.user.ts index 6791846875..81d2cdf7b5 100644 --- a/src/domain/access/role-set/dto/role.set.dto.role.remove.user.ts +++ b/src/domain/access/role-set/dto/role.set.dto.role.remove.user.ts @@ -1,5 +1,5 @@ import { CommunityRoleType } from '@common/enums/community.role'; -import { UUID, UUID_NAMEID_EMAIL } from '@domain/common/scalars'; +import { UUID } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @InputType() @@ -7,8 +7,8 @@ export class RemoveRoleOnRoleSetFromUserInput { @Field(() => UUID, { nullable: false }) roleSetID!: string; - @Field(() => UUID_NAMEID_EMAIL, { nullable: false }) - userID!: string; + @Field(() => UUID, { nullable: false }) + contributorID!: string; @Field(() => CommunityRoleType, { nullable: false }) role!: CommunityRoleType; diff --git a/src/domain/access/role-set/dto/role.set.dto.role.remove.virtual.ts b/src/domain/access/role-set/dto/role.set.dto.role.remove.virtual.ts index 8432c1ecb6..5b406816fb 100644 --- a/src/domain/access/role-set/dto/role.set.dto.role.remove.virtual.ts +++ b/src/domain/access/role-set/dto/role.set.dto.role.remove.virtual.ts @@ -1,5 +1,5 @@ import { CommunityRoleType } from '@common/enums/community.role'; -import { UUID, UUID_NAMEID } from '@domain/common/scalars'; +import { UUID } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; @InputType() @@ -7,8 +7,8 @@ export class RemoveRoleOnRoleSetFromVirtualContributorInput { @Field(() => UUID, { nullable: false }) roleSetID!: string; - @Field(() => UUID_NAMEID, { nullable: false }) - virtualContributorID!: string; + @Field(() => UUID, { nullable: false }) + contributorID!: string; @Field(() => CommunityRoleType, { nullable: false }) role!: CommunityRoleType; diff --git a/src/domain/access/role-set/role.set.resolver.mutations.ts b/src/domain/access/role-set/role.set.resolver.mutations.ts index 5246b043b3..af9c31cee7 100644 --- a/src/domain/access/role-set/role.set.resolver.mutations.ts +++ b/src/domain/access/role-set/role.set.resolver.mutations.ts @@ -103,17 +103,17 @@ export class RoleSetResolverMutations { await this.roleSetService.assignUserToRole( roleSet, roleData.role, - roleData.userID, + roleData.contributorID, agentInfo, true ); // reset the user authorization policy so that their profile is visible to other community members - const user = await this.userService.getUserOrFail(roleData.userID); + const user = await this.userService.getUserOrFail(roleData.contributorID); const authorizations = await this.userAuthorizationService.applyAuthorizationPolicy(user); await this.authorizationPolicyService.saveAll(authorizations); - return await this.userService.getUserOrFail(roleData.userID); + return await this.userService.getUserOrFail(roleData.contributorID); } @UseGuards(GraphqlGuard) @@ -138,7 +138,7 @@ export class RoleSetResolverMutations { return await this.roleSetService.assignOrganizationToRole( roleSet, roleData.role, - roleData.organizationID + roleData.contributorID ); } @@ -160,7 +160,7 @@ export class RoleSetResolverMutations { const sameAccount = await this.roleSetService.isCommunityAccountMatchingVcAccount( roleSet.id, - roleData.virtualContributorID + roleData.contributorID ); if (sameAccount) { requiredPrivilege = @@ -188,13 +188,13 @@ export class RoleSetResolverMutations { await this.roleSetService.assignVirtualToRole( roleSet, roleData.role, - roleData.virtualContributorID, + roleData.contributorID, agentInfo, true ); return await this.virtualContributorService.getVirtualContributorOrFail( - roleData.virtualContributorID + roleData.contributorID ); } @@ -219,7 +219,7 @@ export class RoleSetResolverMutations { const extendedAuthorization = this.roleSetAuthorizationService.extendAuthorizationPolicyForSelfRemoval( community, - roleData.userID + roleData.contributorID ); await this.authorizationService.grantAccessOrFail( @@ -232,15 +232,15 @@ export class RoleSetResolverMutations { await this.roleSetService.removeUserFromRole( roleSet, roleData.role, - roleData.userID + roleData.contributorID ); // reset the user authorization policy so that their profile is not visible // to other community members - const user = await this.userService.getUserOrFail(roleData.userID); + const user = await this.userService.getUserOrFail(roleData.contributorID); const authorizations = await this.userAuthorizationService.applyAuthorizationPolicy(user); await this.authorizationPolicyService.saveAll(authorizations); - return await this.userService.getUserOrFail(roleData.userID); + return await this.userService.getUserOrFail(roleData.contributorID); } @UseGuards(GraphqlGuard) @@ -265,7 +265,7 @@ export class RoleSetResolverMutations { return await this.roleSetService.removeOrganizationFromRole( roleSet, roleData.role, - roleData.organizationID + roleData.contributorID ); } @@ -287,7 +287,7 @@ export class RoleSetResolverMutations { const extendedAuthorization = await this.roleSetAuthorizationService.extendAuthorizationPolicyForVirtualContributorRemoval( roleSet, - roleData.virtualContributorID + roleData.contributorID ); await this.authorizationService.grantAccessOrFail( @@ -300,11 +300,11 @@ export class RoleSetResolverMutations { await this.roleSetService.removeVirtualFromRole( roleSet, roleData.role, - roleData.virtualContributorID + roleData.contributorID ); return await this.virtualContributorService.getVirtualContributorOrFail( - roleData.virtualContributorID + roleData.contributorID ); } From 57496f782fe3b78bf5c2af4b73eb418a4ab1c1b0 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Sun, 22 Sep 2024 07:51:36 +0200 Subject: [PATCH 18/78] added base role type to roleset --- src/app.module.ts | 2 -- src/domain/access/role-set/role.set.entity.ts | 14 +++++++++++- .../access/role-set/role.set.interface.ts | 11 +++++++++- .../role-set/role.set.resolver.mutations.ts | 5 +---- .../access/role-set/role.set.service.ts | 22 +++++++++++++++++++ src/migrations/1726843779059-roleSet.ts | 3 ++- 6 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/app.module.ts b/src/app.module.ts index 89533daf34..0e4c648345 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -82,7 +82,6 @@ import { LookupByNameModule } from '@services/api/lookup-by-name'; import { PlatformHubModule } from '@platform/platfrom.hub/platform.hub.module'; import { AdminContributorsModule } from '@platform/admin/avatars/admin.avatar.module'; import { InputCreatorModule } from '@services/api/input-creator/input.creator.module'; -import { RoleSetModule } from '@domain/access/role-set/role.set.module'; @Module({ imports: [ @@ -241,7 +240,6 @@ import { RoleSetModule } from '@domain/access/role-set/role.set.module'; SearchModule, ActivityLogModule, RolesModule, - RoleSetModule, KonfigModule, AdminContributorsModule, AdminCommunicationModule, diff --git a/src/domain/access/role-set/role.set.entity.ts b/src/domain/access/role-set/role.set.entity.ts index b73ae1a198..598fdcf01e 100644 --- a/src/domain/access/role-set/role.set.entity.ts +++ b/src/domain/access/role-set/role.set.entity.ts @@ -1,4 +1,11 @@ -import { Entity, JoinColumn, ManyToOne, OneToMany, OneToOne } from 'typeorm'; +import { + Column, + Entity, + JoinColumn, + ManyToOne, + OneToMany, + OneToOne, +} from 'typeorm'; import { IGroupable } from '@src/common/interfaces/groupable.interface'; import { AuthorizableEntity } from '@domain/common/entity/authorizable-entity'; import { Role } from '../role/role.entity'; @@ -7,6 +14,8 @@ import { PlatformInvitation } from '@platform/invitation/platform.invitation.ent import { IRoleSet } from './role.set.interface'; import { Application } from '@domain/access/application/application.entity'; import { Invitation } from '@domain/access/invitation/invitation.entity'; +import { CommunityRoleType } from '@common/enums/community.role'; +import { ENUM_LENGTH } from '@common/constants/entity.field.length.constants'; @Entity() export class RoleSet @@ -27,6 +36,9 @@ export class RoleSet }) roles?: Role[]; + @Column('varchar', { length: ENUM_LENGTH, nullable: false }) + baseRoleType!: CommunityRoleType; + @OneToMany(() => Application, application => application.roleSet, { eager: false, cascade: true, diff --git a/src/domain/access/role-set/role.set.interface.ts b/src/domain/access/role-set/role.set.interface.ts index 1eb1ed4431..dceb2fb6a4 100644 --- a/src/domain/access/role-set/role.set.interface.ts +++ b/src/domain/access/role-set/role.set.interface.ts @@ -1,14 +1,23 @@ -import { ObjectType } from '@nestjs/graphql'; +import { Field, ObjectType } from '@nestjs/graphql'; import { IAuthorizable } from '@domain/common/entity/authorizable-entity'; import { IForm } from '@domain/common/form/form.interface'; import { IPlatformInvitation } from '@platform/invitation/platform.invitation.interface'; import { IApplication } from '@domain/access/application/application.interface'; import { IInvitation } from '@domain/access/invitation/invitation.interface'; import { IRole } from '../role/role.interface'; +import { CommunityRoleType } from '@common/enums/community.role'; @ObjectType('RoleSet') export abstract class IRoleSet extends IAuthorizable { roles?: IRole[]; + + @Field(() => CommunityRoleType, { + nullable: false, + description: + 'The CommunityRole that acts as the base for the RoleSet, so other roles potentially require it.', + }) + baseRoleType!: CommunityRoleType; + applications?: IApplication[]; invitations?: IInvitation[]; platformInvitations?: IPlatformInvitation[]; diff --git a/src/domain/access/role-set/role.set.resolver.mutations.ts b/src/domain/access/role-set/role.set.resolver.mutations.ts index af9c31cee7..f43c671d07 100644 --- a/src/domain/access/role-set/role.set.resolver.mutations.ts +++ b/src/domain/access/role-set/role.set.resolver.mutations.ts @@ -213,12 +213,9 @@ export class RoleSetResolverMutations { // Extend the authorization policy with a credential rule to assign the GRANT privilege // to the user specified in the incoming mutation. Then if it is the same user as is logged // in then the user will have the GRANT privilege + so can carry out the mutation - const community = - await this.communityResolverService.getCommunityForRoleSet(roleSet.id); - const extendedAuthorization = this.roleSetAuthorizationService.extendAuthorizationPolicyForSelfRemoval( - community, + roleSet, roleData.contributorID ); diff --git a/src/domain/access/role-set/role.set.service.ts b/src/domain/access/role-set/role.set.service.ts index 56610f1c8b..f4d59796f0 100644 --- a/src/domain/access/role-set/role.set.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -1358,4 +1358,26 @@ export class RoleSetService { } return role; } + + public async getBaseRoleDefinition(roleSet: IRoleSet): Promise { + const roleDefinitions = await this.getRoleDefinitions(roleSet); + const baseRole = roleDefinitions.find( + rd => rd.type === roleSet.baseRoleType + ); + if (!baseRole) { + throw new RelationshipNotFoundException( + `Unable to find BaseRole of type ${roleSet.baseRoleType} for RoleSet: ${roleSet.id}`, + LogContext.COMMUNITY + ); + } + return baseRole; + } + + public async isBaseRole( + roleSet: IRoleSet, + roleType: CommunityRoleType + ): Promise { + const isBaseRole = roleSet.baseRoleType === roleType; + return isBaseRole; + } } diff --git a/src/migrations/1726843779059-roleSet.ts b/src/migrations/1726843779059-roleSet.ts index 524bbc8bed..9bf94dc04a 100644 --- a/src/migrations/1726843779059-roleSet.ts +++ b/src/migrations/1726843779059-roleSet.ts @@ -49,6 +49,7 @@ export class RoleSet1726843779059 implements MigrationInterface { \`authorizationId\` char(36) NULL, \`applicationFormId\` char(36) NULL, \`parentRoleSetId\` char(36) NULL, + \`baseRoleType\` varchar(128) NOT NULL, UNIQUE INDEX \`REL_b038f74c8d4eadb839e78b99ce\` (\`authorizationId\`), UNIQUE INDEX \`REL_00905b142498f63e76d38fb254\` (\`applicationFormId\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB` @@ -123,7 +124,7 @@ export class RoleSet1726843779059 implements MigrationInterface { ); await queryRunner.query( - `INSERT INTO role_set (id, version, authorizationId, applicationFormId) VALUES ('${roleSetID}', 1, '${roleSetAuthID}', '${community.applicationFormId}')` + `INSERT INTO role_set (id, version, authorizationId, applicationFormId, baseRoleType) VALUES ('${roleSetID}', 1, '${roleSetAuthID}', '${community.applicationFormId}', 'member')` ); } From 1d6c3047e58e7121b5b0c1b53d163c9030518b0d Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Sun, 22 Sep 2024 09:57:57 +0200 Subject: [PATCH 19/78] moved my roles / membership status to roleset as that is more logical --- .../role-set/dto/role.set.dto.create.ts | 2 + .../role-set/role.set.resolver.fields.ts | 46 +++++++- .../access/role-set/role.set.service.ts | 5 +- .../community/community.resolver.fields.ts | 49 +-------- .../community/community/community.service.ts | 8 +- .../account.host/account.host.service.ts | 19 ++-- src/domain/space/account/account.service.ts | 27 ++++- src/domain/space/space/space.service.ts | 104 +++++++++++------- .../admin.communication.service.ts | 18 ++- .../api/conversion/conversion.service.ts | 11 +- 10 files changed, 167 insertions(+), 122 deletions(-) diff --git a/src/domain/access/role-set/dto/role.set.dto.create.ts b/src/domain/access/role-set/dto/role.set.dto.create.ts index fc4b43b740..b74ea18a48 100644 --- a/src/domain/access/role-set/dto/role.set.dto.create.ts +++ b/src/domain/access/role-set/dto/role.set.dto.create.ts @@ -1,9 +1,11 @@ import { CreateRoleInput } from '@domain/access/role/dto/role.dto.create'; import { CreateFormInput } from '@domain/common/form/dto/form.dto.create'; import { IRoleSet } from '../role.set.interface'; +import { CommunityRoleType } from '@common/enums/community.role'; export class CreateRoleSetInput { parentRoleSet?: IRoleSet; roles!: CreateRoleInput[]; applicationForm!: CreateFormInput; + baseRoleType!: CommunityRoleType; } diff --git a/src/domain/access/role-set/role.set.resolver.fields.ts b/src/domain/access/role-set/role.set.resolver.fields.ts index 89f5353ba3..cf94475c39 100644 --- a/src/domain/access/role-set/role.set.resolver.fields.ts +++ b/src/domain/access/role-set/role.set.resolver.fields.ts @@ -1,7 +1,10 @@ import { GraphqlGuard } from '@core/authorization'; import { UseGuards } from '@nestjs/common'; import { Args, Float, Parent, ResolveField, Resolver } from '@nestjs/graphql'; -import { AuthorizationAgentPrivilege } from '@src/common/decorators'; +import { + AuthorizationAgentPrivilege, + CurrentUser, +} from '@src/common/decorators'; import { RoleSetService } from './role.set.service'; import { IForm } from '@domain/common/form/form.interface'; import { IRoleSet } from './role.set.interface'; @@ -20,6 +23,9 @@ import { IVirtualContributor } from '@domain/community/virtual-contributor/virtu import { IInvitation } from '../invitation/invitation.interface'; import { IPlatformInvitation } from '@platform/invitation/platform.invitation.interface'; import { IRole } from '../role/role.interface'; +import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; +import { AgentInfo } from '@core/authentication.agent.info/agent.info'; +import { CommunityRoleImplicit } from '@common/enums/community.role.implicit'; @Resolver(() => IRoleSet) export class RoleSetResolverFields { @@ -227,4 +233,42 @@ export class RoleSetResolverFields { ): Promise { return await this.roleSetService.getRoleDefinition(roleSet, role); } + + @UseGuards(GraphqlGuard) + @ResolveField('myMembershipStatus', () => CommunityMembershipStatus, { + nullable: true, + description: 'The membership status of the currently logged in user.', + }) + async myMembershipStatus( + @CurrentUser() agentInfo: AgentInfo, + @Parent() roleSet: IRoleSet + ): Promise { + return this.roleSetService.getMembershipStatus(agentInfo, roleSet); + } + + @UseGuards(GraphqlGuard) + @ResolveField('myRoles', () => [CommunityRoleType], { + nullable: false, + description: + 'The roles on this community for the currently logged in user.', + }) + async myRoles( + @CurrentUser() agentInfo: AgentInfo, + @Parent() roleSet: IRoleSet + ): Promise { + return this.roleSetService.getRolesForAgentInfo(agentInfo, roleSet); + } + + @UseGuards(GraphqlGuard) + @ResolveField('myRolesImplicit', () => [CommunityRoleImplicit], { + nullable: false, + description: + 'The implicit roles on this community for the currently logged in user.', + }) + async myRolesImplicit( + @CurrentUser() agentInfo: AgentInfo, + @Parent() roleSet: IRoleSet + ): Promise { + return this.roleSetService.getCommunityImplicitRoles(agentInfo, roleSet); + } } diff --git a/src/domain/access/role-set/role.set.service.ts b/src/domain/access/role-set/role.set.service.ts index f4d59796f0..f2c897a73d 100644 --- a/src/domain/access/role-set/role.set.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -86,6 +86,7 @@ export class RoleSetService { roleSet.applications = []; roleSet.invitations = []; roleSet.platformInvitations = []; + roleSet.baseRoleType = roleSetData.baseRoleType; roleSet.parentRoleSet = roleSetData.parentRoleSet; @@ -230,7 +231,7 @@ export class RoleSetService { return roleSet; } - public async removeAllCommunityRoles(roleSet: IRoleSet) { + public async removeAllRoleAssignments(roleSet: IRoleSet) { // Remove all issued role credentials for contributors for (const roleType of Object.values(CommunityRoleType)) { const users = await this.getUsersWithRole(roleSet, roleType); @@ -253,7 +254,7 @@ export class RoleSetService { } } - async getCommunityRoles( + async getRolesForAgentInfo( agentInfo: AgentInfo, roleSet: IRoleSet ): Promise { diff --git a/src/domain/community/community/community.resolver.fields.ts b/src/domain/community/community/community.resolver.fields.ts index 4fff486615..f5b1997b6b 100644 --- a/src/domain/community/community/community.resolver.fields.ts +++ b/src/domain/community/community/community.resolver.fields.ts @@ -1,11 +1,7 @@ import { GraphqlGuard } from '@core/authorization'; import { UseGuards } from '@nestjs/common'; import { Parent, ResolveField, Resolver, Args } from '@nestjs/graphql'; -import { - AuthorizationAgentPrivilege, - CurrentUser, - Profiling, -} from '@src/common/decorators'; +import { AuthorizationAgentPrivilege, Profiling } from '@src/common/decorators'; import { Community, ICommunity } from '@domain/community/community'; import { CommunityService } from './community.service'; import { IUserGroup } from '@domain/community/user-group'; @@ -14,12 +10,7 @@ import { ICommunication } from '@domain/communication/communication/communicatio import { UUID } from '@domain/common/scalars/scalar.uuid'; import { ICommunityGuidelines } from '../community-guidelines/community.guidelines.interface'; import { IRoleSet } from '@domain/access/role-set'; -import { AgentInfo } from '@core/authentication.agent.info/agent.info'; -import { CommunityRoleImplicit } from '@common/enums/community.role.implicit'; import { RoleSetService } from '@domain/access/role-set/role.set.service'; -import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; -import { CommunityRoleType } from '@common/enums/community.role'; - @Resolver(() => ICommunity) export class CommunityResolverFields { constructor( @@ -84,42 +75,4 @@ export class CommunityResolverFields { ): Promise { return await this.communityService.getCommunityGuidelines(community); } - - @UseGuards(GraphqlGuard) - @ResolveField('myMembershipStatus', () => CommunityMembershipStatus, { - nullable: true, - description: 'The membership status of the currently logged in user.', - }) - async myMembershipStatus( - @CurrentUser() agentInfo: AgentInfo, - @Parent() community: ICommunity - ): Promise { - return this.roleSetService.getMembershipStatus(agentInfo, community); - } - - @UseGuards(GraphqlGuard) - @ResolveField('myRoles', () => [CommunityRoleType], { - nullable: false, - description: - 'The roles on this community for the currently logged in user.', - }) - async myRoles( - @CurrentUser() agentInfo: AgentInfo, - @Parent() community: ICommunity - ): Promise { - return this.roleSetService.getCommunityRoles(agentInfo, community); - } - - @UseGuards(GraphqlGuard) - @ResolveField('myRolesImplicit', () => [CommunityRoleImplicit], { - nullable: false, - description: - 'The implicit roles on this community for the currently logged in user.', - }) - async myRolesImplicit( - @CurrentUser() agentInfo: AgentInfo, - @Parent() community: ICommunity - ): Promise { - return this.roleSetService.getCommunityImplicitRoles(agentInfo, community); - } } diff --git a/src/domain/community/community/community.service.ts b/src/domain/community/community/community.service.ts index 1140053eda..b6163c08a1 100644 --- a/src/domain/community/community/community.service.ts +++ b/src/domain/community/community/community.service.ts @@ -114,7 +114,7 @@ export class CommunityService { // Loads the group into the Community entity if not already present async getUserGroup( - community: IRoleSet, + community: ICommunity, groupID: string ): Promise { const communityWithGroups = await this.getCommunityOrFail(community.id, { @@ -178,6 +178,8 @@ export class CommunityService { ); } + await this.roleSetService.removeAllRoleAssignments(community.roleSet); + // Remove all groups for (const group of community.groups) { await this.userGroupService.removeUserGroup({ @@ -202,7 +204,7 @@ export class CommunityService { return true; } - async save(community: IRoleSet): Promise { + async save(community: ICommunity): Promise { return await this.communityRepository.save(community); } @@ -343,7 +345,7 @@ export class CommunityService { } public async getLevelZeroSpaceIdForCommunity( - community: IRoleSet + community: ICommunity ): Promise { return await this.communityResolverService.getLevelZeroSpaceIdForCommunity( community.id diff --git a/src/domain/space/account.host/account.host.service.ts b/src/domain/space/account.host/account.host.service.ts index d96fa8d992..03fa807d0a 100644 --- a/src/domain/space/account.host/account.host.service.ts +++ b/src/domain/space/account.host/account.host.service.ts @@ -23,8 +23,8 @@ import { LicensingService } from '@platform/licensing/licensing.service'; import { StorageAggregatorType } from '@common/enums/storage.aggregator.type'; import { AgentType } from '@common/enums/agent.type'; import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type'; -import { ISpace } from '../space/space.interface'; import { AccountType } from '@common/enums/account.type'; +import { IAgent } from '@domain/agent/agent/agent.interface'; @Injectable() export class AccountHostService { @@ -82,16 +82,11 @@ export class AccountHostService { } public async assignLicensePlansToSpace( - space: ISpace, + spaceAgent: IAgent, + spaceID: string, type: AccountType, licensePlanID?: string - ): Promise { - if (!space.agent) { - throw new RelationshipNotFoundException( - `Space ${space.id} has no agent`, - LogContext.ACCOUNT - ); - } + ): Promise { const licensingFramework = await this.licensingService.getDefaultLicensingOrFail(); const licensePlansToAssign: ILicensePlan[] = []; @@ -122,14 +117,14 @@ export class AccountHostService { } } - const spaceAgent = space.agent; for (const licensePlan of licensePlansToAssign) { - space.agent = await this.licenseIssuerService.assignLicensePlan( + await this.licenseIssuerService.assignLicensePlan( spaceAgent, licensePlan, - space.id + spaceID ); } + return await this.agentService.getAgentOrFail(spaceAgent.id); } public async getHost(account: IAccount): Promise { diff --git a/src/domain/space/account/account.service.ts b/src/domain/space/account/account.service.ts index e99d6001bf..c2bf214646 100644 --- a/src/domain/space/account/account.service.ts +++ b/src/domain/space/account/account.service.ts @@ -1,6 +1,7 @@ import { LogContext } from '@common/enums'; import { EntityNotFoundException, + EntityNotInitializedException, RelationshipNotFoundException, ValidationException, } from '@common/exceptions'; @@ -101,16 +102,30 @@ export class AccountService { space = await this.spaceService.save(space); - if (agentInfo) { - await this.spaceService.assignUserToRoles(space, agentInfo); - } - const spaceWithAgent = await this.spaceService.getSpaceOrFail(space.id, { + space = await this.spaceService.getSpaceOrFail(space.id, { relations: { + community: { + roleSet: true, + }, agent: true, }, }); - await this.accountHostService.assignLicensePlansToSpace( - spaceWithAgent, + if (!space.agent || !space.community || !space.community.roleSet) { + throw new EntityNotInitializedException( + `Unable to load space ${space.id} with required entities for creating space`, + LogContext.SPACES + ); + } + const spaceAgent = space.agent; + const roleSet = space.community.roleSet; + + if (agentInfo) { + await this.spaceService.assignUserToRoles(roleSet, agentInfo); + } + + space.agent = await this.accountHostService.assignLicensePlansToSpace( + spaceAgent, + space.id, account.type, spaceData.licensePlanID ); diff --git a/src/domain/space/space/space.service.ts b/src/domain/space/space/space.service.ts index 1c091dc84f..75fd5ae31a 100644 --- a/src/domain/space/space/space.service.ts +++ b/src/domain/space/space/space.service.ts @@ -79,6 +79,7 @@ import { TemplateService } from '@domain/template/template/template.service'; import { templatesSetDefaults } from '../space.defaults/definitions/space.defaults.templates'; import { InputCreatorService } from '@services/api/input-creator/input.creator.service'; import { RoleSetService } from '@domain/access/role-set/role.set.service'; +import { IRoleSet } from '@domain/access/role-set/role.set.interface'; @Injectable() export class SpaceService { @@ -168,6 +169,7 @@ export class SpaceService { roleSetData: { roles: roleSetRolesData, applicationForm: applicationFormData, + baseRoleType: CommunityRoleType.MEMBER, }, guidelines: { // TODO: get this from defaults service @@ -942,15 +944,27 @@ export class SpaceService { subspace = await this.addSubspaceToSpace(space, subspace); subspace = await this.save(subspace); + subspace = await this.getSpaceOrFail(subspace.id, { + relations: { + community: { + roleSet: true, + }, + }, + }); + // Before assigning roles in the subspace check that the user is a member if (agentInfo) { + if (!subspace.community || !subspace.community.roleSet) { + throw new EntityNotInitializedException( + `unable to load community with role manager: ${subspace.id}`, + LogContext.SPACES + ); + } + const roleSet = subspace.community.roleSet; const agent = await this.agentService.getAgentOrFail(agentInfo?.agentID); - const isMember = await this.roleSetService.isMember( - agent, - space.community - ); + const isMember = await this.roleSetService.isMember(agent, roleSet); if (isMember) { - await this.assignUserToRoles(subspace, agentInfo); + await this.assignUserToRoles(roleSet, agentInfo); } } @@ -994,7 +1008,7 @@ export class SpaceService { role: CommunityRoleType, type: CommunityContributorType ) { - if (!space.community) { + if (!space.community || !space.community.roleSet) { throw new EntityNotInitializedException( `Community not initialised on Space for assigning contributor to role: ${space.id}`, LogContext.SPACES @@ -1008,7 +1022,7 @@ export class SpaceService { } await this.roleSetService.assignContributorAgentToRole( - space.community, + space.community.roleSet, role, contributor.agent, type @@ -1030,35 +1044,27 @@ export class SpaceService { return levelZeroSpace.agent; } - public async assignUserToRoles(space: ISpace, agentInfo: AgentInfo) { - if (!space.community) { - throw new EntityNotInitializedException( - `Community not initialised on Space: ${space.id}`, - LogContext.SPACES - ); - } - if (agentInfo) { - await this.roleSetService.assignUserToRole( - space.community, - CommunityRoleType.MEMBER, - agentInfo.userID, - agentInfo - ); + public async assignUserToRoles(roleSet: IRoleSet, agentInfo: AgentInfo) { + await this.roleSetService.assignUserToRole( + roleSet, + CommunityRoleType.MEMBER, + agentInfo.userID, + agentInfo + ); - await this.roleSetService.assignUserToRole( - space.community, - CommunityRoleType.LEAD, - agentInfo.userID, - agentInfo - ); + await this.roleSetService.assignUserToRole( + roleSet, + CommunityRoleType.LEAD, + agentInfo.userID, + agentInfo + ); - await this.roleSetService.assignUserToRole( - space.community, - CommunityRoleType.ADMIN, - agentInfo.userID, - agentInfo - ); - } + await this.roleSetService.assignUserToRole( + roleSet, + CommunityRoleType.ADMIN, + agentInfo.userID, + agentInfo + ); } public async update(spaceData: UpdateSpaceInput): Promise { @@ -1107,6 +1113,7 @@ export class SpaceService { if ( !space.collaboration || !space.community || + !space.community.roleSet || !space.context || !space.agent || !space.profile || @@ -1121,7 +1128,6 @@ export class SpaceService { await this.contextService.removeContext(space.context.id); await this.collaborationService.deleteCollaboration(space.collaboration.id); - await this.roleSetService.removeAllCommunityRoles(space.community); await this.communityService.removeCommunity(space.community.id); await this.profileService.deleteProfile(space.profile.id); await this.agentService.deleteAgent(space.agent.id); @@ -1259,6 +1265,25 @@ export class SpaceService { return community; } + public async getCommunityRoleSet(spaceId: string): Promise { + const subspaceWithCommunityRoleSet = await this.getSpaceOrFail(spaceId, { + relations: { + community: { + roleSet: true, + }, + }, + }); + const community = subspaceWithCommunityRoleSet.community; + if (!community || !community.roleSet) { + throw new RelationshipNotFoundException( + `Unable to load community with RoleSet for space ${spaceId} `, + LogContext.COMMUNITY + ); + } + + return community.roleSet; + } + async getLibraryOrFail(rootSpaceID: string): Promise { const levelZeroSpaceWithLibrary = await this.getSpaceOrFail(rootSpaceID, { relations: { @@ -1424,11 +1449,6 @@ export class SpaceService { return agent; } - public async getMembersCount(space: ISpace): Promise { - const community = await this.getCommunity(space.id); - return await this.roleSetService.getMembersCount(community); - } - public async getPostsCount(space: ISpace): Promise { const collaboration = await this.getCollaborationOrFail(space.id); @@ -1467,10 +1487,10 @@ export class SpaceService { subspacesTopic.id = `subspaces-${space.id}`; metrics.push(subspacesTopic); - const community = await this.getCommunity(space.id); + const roleSet = await this.getCommunityRoleSet(space.id); // Members - const membersCount = await this.roleSetService.getMembersCount(community); + const membersCount = await this.roleSetService.getMembersCount(roleSet); const membersTopic = new NVP('members', membersCount.toString()); membersTopic.id = `members-${space.id}`; metrics.push(membersTopic); diff --git a/src/platform/admin/communication/admin.communication.service.ts b/src/platform/admin/communication/admin.communication.service.ts index a21f91fbf7..453730ae3c 100644 --- a/src/platform/admin/communication/admin.communication.service.ts +++ b/src/platform/admin/communication/admin.communication.service.ts @@ -35,10 +35,15 @@ export class AdminCommunicationService { LogContext.COMMUNICATION ); const community = await this.communityService.getCommunityOrFail( - communicationData.communityID + communicationData.communityID, + { + relations: { + roleSet: true, + }, + } ); const communityMembers = await this.roleSetService.getUsersWithRole( - community, + community.roleSet, CommunityRoleType.MEMBER ); const communication = await this.communityService.getCommunication( @@ -103,13 +108,18 @@ export class AdminCommunicationService { LogContext.COMMUNICATION ); const community = await this.communityService.getCommunityOrFail( - communicationData.communityID + communicationData.communityID, + { + relations: { + roleSet: true, + }, + } ); const communication = await this.communityService.getCommunication( community.id ); const communityMembers = await this.roleSetService.getUsersWithRole( - community, + community.roleSet, CommunityRoleType.MEMBER ); for (const communityMember of communityMembers) { diff --git a/src/services/api/conversion/conversion.service.ts b/src/services/api/conversion/conversion.service.ts index 933585d387..4950e276b7 100644 --- a/src/services/api/conversion/conversion.service.ts +++ b/src/services/api/conversion/conversion.service.ts @@ -47,7 +47,9 @@ export class ConversionService { conversionData.subspaceID, { relations: { - community: true, + community: { + roleSet: true, + }, context: true, profile: true, collaboration: { @@ -65,6 +67,7 @@ export class ConversionService { ); if ( !subspace.community || + !subspace.community.roleSet || !subspace.context || !subspace.profile || !subspace.collaboration || @@ -84,7 +87,7 @@ export class ConversionService { // check the community is in a fit state const challengeCommunityLeadOrgs = await this.roleSetService.getOrganizationsWithRole( - subspace.community, + subspace.community.roleSet, CommunityRoleType.LEAD ); if (challengeCommunityLeadOrgs.length !== 1) { @@ -196,7 +199,7 @@ export class ConversionService { // Assign users to roles in new space await this.assignContributors( - space.community, + spaceRoleSet, userMembers, userLeads, orgMembers @@ -395,7 +398,7 @@ export class ConversionService { // Assign users to roles in new challenge await this.assignContributors( - challengeCommunityUpdated, + challengeCommunityUpdated.roleSet, userMembers, userLeads, orgMembers, From bac4ae40256f9e78e3767f69a4c35cdc21339f23 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Sun, 22 Sep 2024 14:42:57 +0200 Subject: [PATCH 20/78] added back on communication addition / removal on role assignment --- src/domain/access/role-set/role.set.module.ts | 2 + .../access/role-set/role.set.service.ts | 31 +++++++----- .../community.communication.module.ts | 10 ++++ .../community.communication.service.ts | 48 +++++++++++++++++++ .../community/community.resolver.fields.ts | 8 +--- .../community/community/community.service.ts | 38 --------------- .../community.resolver.service.ts | 21 ++++++++ 7 files changed, 103 insertions(+), 55 deletions(-) create mode 100644 src/domain/community/community-communication/community.communication.module.ts create mode 100644 src/domain/community/community-communication/community.communication.service.ts diff --git a/src/domain/access/role-set/role.set.module.ts b/src/domain/access/role-set/role.set.module.ts index 1e7bc9580c..2970114d8c 100644 --- a/src/domain/access/role-set/role.set.module.ts +++ b/src/domain/access/role-set/role.set.module.ts @@ -27,6 +27,7 @@ import { RoleSetInvitationLifecycleOptionsProvider } from './role.set.lifecycle. import { ContributionReporterModule } from '@services/external/elasticsearch/contribution-reporter'; import { ActivityAdapterModule } from '@services/adapters/activity-adapter/activity.adapter.module'; import { LifecycleModule } from '@domain/common/lifecycle/lifecycle.module'; +import { CommunityCommunicationModule } from '@domain/community/community-communication/community.communication.module'; @Module({ imports: [ @@ -49,6 +50,7 @@ import { LifecycleModule } from '@domain/common/lifecycle/lifecycle.module'; ContributionReporterModule, ActivityAdapterModule, LifecycleModule, + CommunityCommunicationModule, TypeOrmModule.forFeature([RoleSet]), ], providers: [ diff --git a/src/domain/access/role-set/role.set.service.ts b/src/domain/access/role-set/role.set.service.ts index f2c897a73d..fbe59ddfbc 100644 --- a/src/domain/access/role-set/role.set.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -54,6 +54,7 @@ import { ContributorService } from '@domain/community/contributor/contributor.se import { RoleSetEventsService } from './role.set.service.events'; import { AiServerAdapter } from '@services/adapters/ai-server-adapter/ai.server.adapter'; import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; +import { CommunityCommunicationService } from '@domain/community/community-communication/community.communication.service'; @Injectable() export class RoleSetService { @@ -72,6 +73,7 @@ export class RoleSetService { private communityResolverService: CommunityResolverService, private roleSetEventsService: RoleSetEventsService, private aiServerAdapter: AiServerAdapter, + private communityCommunicationService: CommunityCommunicationService, @InjectRepository(RoleSet) private roleSetRepository: Repository, @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService @@ -533,11 +535,14 @@ export class RoleSetService { LogContext.ROLES ); if (role === CommunityRoleType.MEMBER) { - // TODO: FIX ME - need callback depending on Type of RoleSet - - // const community = - // await this.communityResolverService.getCommunityForRoleSet(roleSet.id); - // this.addMemberToCommunication(contributor, roleSet); + const communication = + await this.communityResolverService.getCommunicationForRoleSet( + roleSet.id + ); + this.communityCommunicationService.addMemberToCommunication( + communication, + contributor + ); if (agentInfo) { await this.roleSetEventsService.registerCommunityNewMemberActivity( @@ -688,13 +693,17 @@ export class RoleSetService { } } - // TODO: FIX ME - // if (roleType === CommunityRoleType.MEMBER) { - // const community = - // await this.communityResolverService.getCommunityForRoleSet(roleSet.id); + if (roleType === CommunityRoleType.MEMBER) { + const communication = + await this.communityResolverService.getCommunicationForRoleSet( + roleSet.id + ); - // await this.removeMemberFromCommunication(community, user); - // } + await this.communityCommunicationService.removeMemberFromCommunication( + communication, + user + ); + } return user; } diff --git a/src/domain/community/community-communication/community.communication.module.ts b/src/domain/community/community-communication/community.communication.module.ts new file mode 100644 index 0000000000..57228c4cb9 --- /dev/null +++ b/src/domain/community/community-communication/community.communication.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { CommunityCommunicationService } from './community.communication.service'; +import { CommunicationModule } from '@domain/communication/communication/communication.module'; + +@Module({ + imports: [CommunicationModule], + providers: [CommunityCommunicationService], + exports: [CommunityCommunicationService], +}) +export class CommunityCommunicationModule {} diff --git a/src/domain/community/community-communication/community.communication.service.ts b/src/domain/community/community-communication/community.communication.service.ts new file mode 100644 index 0000000000..e117596c42 --- /dev/null +++ b/src/domain/community/community-communication/community.communication.service.ts @@ -0,0 +1,48 @@ +import { Inject, Injectable, LoggerService } from '@nestjs/common'; +import { LogContext } from '@common/enums'; +import { IUser } from '../user/user.interface'; +import { CommunicationService } from '@domain/communication/communication/communication.service'; +import { IContributor } from '../contributor/contributor.interface'; +import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston/dist/winston.constants'; +import { ICommunication } from '@domain/communication/communication'; + +@Injectable() +export class CommunityCommunicationService { + constructor( + private communicationService: CommunicationService, + @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService + ) {} + + public async addMemberToCommunication( + communication: ICommunication, + contributor: IContributor + ): Promise { + this.communicationService + .addContributorToCommunications( + communication, + contributor.communicationID + ) + .catch(error => + this.logger.error( + `Unable to add user to community messaging (${communication.id}): ${error}`, + error?.stack, + LogContext.COMMUNICATION + ) + ); + } + + public async removeMemberFromCommunication( + communication: ICommunication, + user: IUser + ): Promise { + this.communicationService + .removeUserFromCommunications(communication, user) + .catch(error => + this.logger.error( + `Unable remove user from community messaging (${communication.id}): ${error}`, + error?.stack, + LogContext.COMMUNICATION + ) + ); + } +} diff --git a/src/domain/community/community/community.resolver.fields.ts b/src/domain/community/community/community.resolver.fields.ts index f5b1997b6b..aaf6b7e215 100644 --- a/src/domain/community/community/community.resolver.fields.ts +++ b/src/domain/community/community/community.resolver.fields.ts @@ -10,13 +10,9 @@ import { ICommunication } from '@domain/communication/communication/communicatio import { UUID } from '@domain/common/scalars/scalar.uuid'; import { ICommunityGuidelines } from '../community-guidelines/community.guidelines.interface'; import { IRoleSet } from '@domain/access/role-set'; -import { RoleSetService } from '@domain/access/role-set/role.set.service'; @Resolver(() => ICommunity) export class CommunityResolverFields { - constructor( - private communityService: CommunityService, - private roleSetService: RoleSetService - ) {} + constructor(private communityService: CommunityService) {} @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) @UseGuards(GraphqlGuard) @@ -61,7 +57,7 @@ export class CommunityResolverFields { nullable: false, description: 'The RoleSet for this Community.', }) - async policy(@Parent() community: Community): Promise { + async roleSet(@Parent() community: Community): Promise { return this.communityService.getRoleSet(community); } diff --git a/src/domain/community/community/community.service.ts b/src/domain/community/community/community.service.ts index b6163c08a1..4ca27aab40 100644 --- a/src/domain/community/community/community.service.ts +++ b/src/domain/community/community/community.service.ts @@ -22,8 +22,6 @@ import { CommunityGuidelinesService } from '../community-guidelines/community.gu import { IStorageAggregator } from '@domain/storage/storage-aggregator/storage.aggregator.interface'; import { CreateCommunityInput } from './dto/community.dto.create'; import { ICommunityGuidelines } from '../community-guidelines/community.guidelines.interface'; -import { IContributor } from '../contributor/contributor.interface'; -import { IUser } from '../user/user.interface'; import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type'; import { RoleSetService } from '@domain/access/role-set/role.set.service'; import { IRoleSet } from '@domain/access/role-set'; @@ -247,42 +245,6 @@ export class CommunityService { ); } - public async addMemberToCommunication( - contributor: IContributor, - community: ICommunity - ): Promise { - // register the user for the community rooms - const communication = await this.getCommunication(community.id); - this.communicationService - .addContributorToCommunications( - communication, - contributor.communicationID - ) - .catch(error => - this.logger.error( - `Unable to add user to community messaging (${community.id}): ${error}`, - error?.stack, - LogContext.COMMUNICATION - ) - ); - } - - public async removeMemberFromCommunication( - community: ICommunity, - user: IUser - ): Promise { - const communication = await this.getCommunication(community.id); - this.communicationService - .removeUserFromCommunications(communication, user) - .catch(error => - this.logger.error( - `Unable remove user from community messaging (${community.id}): ${error}`, - error?.stack, - LogContext.COMMUNICATION - ) - ); - } - public async getRoleSet(community: ICommunity): Promise { const communityWithRoleSet = await this.getCommunityOrFail(community.id, { relations: { roleSet: true }, diff --git a/src/services/infrastructure/entity-resolver/community.resolver.service.ts b/src/services/infrastructure/entity-resolver/community.resolver.service.ts index 5a456e0396..f352a5d235 100644 --- a/src/services/infrastructure/entity-resolver/community.resolver.service.ts +++ b/src/services/infrastructure/entity-resolver/community.resolver.service.ts @@ -11,6 +11,7 @@ import { RoomType } from '@common/enums/room.type'; import { VirtualContributor } from '@domain/community/virtual-contributor/virtual.contributor.entity'; import { IAgent } from '@domain/agent'; import { IAccount } from '@domain/space/account/account.interface'; +import { ICommunication } from '@domain/communication/communication/communication.interface'; @Injectable() export class CommunityResolverService { @@ -59,6 +60,26 @@ export class CommunityResolverService { return community; } + async getCommunicationForRoleSet(roleSetID: string): Promise { + const community = await this.entityManager.findOne(Community, { + where: { + roleSet: { + id: roleSetID, + }, + }, + relations: { + communication: true, + }, + }); + if (!community || !community.communication) { + throw new EntityNotFoundException( + `Unable to find Community for given RoleSet id: ${roleSetID}`, + LogContext.COMMUNITY + ); + } + return community.communication; + } + public async getLevelZeroSpaceIdForCollaboration( collaborationID: string ): Promise { From d4e00a8529203ec304b1532f6e6d230958068aa0 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Sun, 22 Sep 2024 14:47:05 +0200 Subject: [PATCH 21/78] added back in triggering of new member events --- .../role-set/role.set.service.events.ts | 17 ++++++++++------- .../access/role-set/role.set.service.ts | 19 +++++++------------ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/domain/access/role-set/role.set.service.events.ts b/src/domain/access/role-set/role.set.service.events.ts index d499291518..7740bc596c 100644 --- a/src/domain/access/role-set/role.set.service.events.ts +++ b/src/domain/access/role-set/role.set.service.events.ts @@ -37,13 +37,19 @@ export class RoleSetEventsService { public async processCommunityNewMemberEvents( roleSet: IRoleSet, - levelZeroSpaceID: string, - displayName: string, agentInfo: AgentInfo, newContributor: IContributor ) { const community = await this.communityResolverService.getCommunityForRoleSet(roleSet.id); + const space = await this.communityResolverService.getSpaceForRoleSetOrFail( + roleSet.id + ); + const levelZeroSpaceID = space.levelZeroSpaceID; + const communityDisplayName = + await this.communityResolverService.getDisplayNameForRoleSetOrFail( + roleSet.id + ); // TODO: community just needs to know the level, not the type // Send the notification const notificationInput: NotificationInputCommunityNewMember = { @@ -54,15 +60,12 @@ export class RoleSetEventsService { await this.notificationAdapter.communityNewMember(notificationInput); // Record the contribution events - const space = await this.communityResolverService.getSpaceForRoleSetOrFail( - roleSet.id - ); switch (space.type) { case SpaceType.SPACE: this.contributionReporter.spaceJoined( { id: community.parentID, - name: displayName, + name: communityDisplayName, space: levelZeroSpaceID, }, { @@ -75,7 +78,7 @@ export class RoleSetEventsService { this.contributionReporter.subspaceJoined( { id: community.parentID, - name: displayName, + name: communityDisplayName, space: levelZeroSpaceID, }, { diff --git a/src/domain/access/role-set/role.set.service.ts b/src/domain/access/role-set/role.set.service.ts index fbe59ddfbc..2cb25894c1 100644 --- a/src/domain/access/role-set/role.set.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -551,18 +551,13 @@ export class RoleSetService { agentInfo ); - // if (triggerNewMemberEvents) { - // const levelZeroSpaceID = - // await this.getLevelZeroSpaceIdForCommunity(roleSet); - // const displayName = await this.getDisplayName(roleSet); - // await this.roleSetEventsService.processCommunityNewMemberEvents( - // roleSet, - // levelZeroSpaceID, - // displayName, - // agentInfo, - // contributor - // ); - // } + if (triggerNewMemberEvents) { + await this.roleSetEventsService.processCommunityNewMemberEvents( + roleSet, + agentInfo, + contributor + ); + } } } } From 8abce79301ac6d930ef056a7f71347759c3c3cce Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Sun, 22 Sep 2024 20:06:06 +0200 Subject: [PATCH 22/78] moved join to be on RoleSet; renamed base role to entry role --- .../validation/handlers/base/base.handler.ts | 10 ++-- ...le.ts => role.set.dto.entry.role.apply.ts} | 2 +- ...e.ts => role.set.dto.entry.role.invite.ts} | 2 +- ...oin.ts => role.set.dto.entry.role.join.ts} | 2 +- src/domain/access/role-set/role.set.entity.ts | 2 +- .../access/role-set/role.set.interface.ts | 4 +- .../role-set/role.set.resolver.mutations.ts | 54 ++++++++++++++++--- .../access/role-set/role.set.service.ts | 8 +-- .../community/community.resolver.mutations.ts | 52 +----------------- .../community.dto.update.application.form.ts | 16 ------ src/migrations/1726843779059-roleSet.ts | 4 +- 11 files changed, 66 insertions(+), 90 deletions(-) rename src/domain/access/role-set/dto/{role.set.dto.apply.for.base.role.ts => role.set.dto.entry.role.apply.ts} (92%) rename src/domain/access/role-set/dto/{role.set.dto.invite.for.base.role.ts => role.set.dto.entry.role.invite.ts} (93%) rename src/domain/access/role-set/dto/{role.set.dto.join.ts => role.set.dto.entry.role.join.ts} (86%) delete mode 100644 src/domain/community/community/dto/community.dto.update.application.form.ts diff --git a/src/core/validation/handlers/base/base.handler.ts b/src/core/validation/handlers/base/base.handler.ts index 255e42a1b8..99a8e7590d 100644 --- a/src/core/validation/handlers/base/base.handler.ts +++ b/src/core/validation/handlers/base/base.handler.ts @@ -35,7 +35,6 @@ import { SendMessageOnCalloutInput } from '@domain/collaboration/callout/dto/cal import { CreateCalloutOnCollaborationInput } from '@domain/collaboration/collaboration/dto/collaboration.dto.create.callout'; import { CreateCalendarEventOnCalendarInput } from '@domain/timeline/calendar/dto/calendar.dto.create.event'; import { UpdateCalendarEventInput } from '@domain/timeline/event'; -import { UpdateCommunityApplicationFormInput } from '@domain/community/community/dto/community.dto.update.application.form'; import { CreateTemplateOnTemplatesSetInput } from '@domain/template/templates-set/dto/templates.set.dto.create.template'; import { UpdateTemplateInput } from '@domain/template/template/dto/template.dto.update'; import { CreateDocumentInput } from '@domain/storage/document/dto/document.dto.create'; @@ -72,8 +71,8 @@ import { UpdateCommunityGuidelinesInput } from '@domain/community/community-guid import { ForumCreateDiscussionInput } from '@platform/forum/dto/forum.dto.create.discussion'; import { CreateCollaborationOnSpaceInput } from '@domain/space/space/dto/space.dto.create.collaboration'; import { InviteNewContributorForRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.platform.invitation.community'; -import { InviteForBaseRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.invite.for.base.role'; -import { ApplyForBaseRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.apply.for.base.role'; +import { ApplyForEntryRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.entry.role.apply'; +import { InviteForEntryRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.entry.role.invite'; export class BaseHandler extends AbstractHandler { public async handle( @@ -112,7 +111,6 @@ export class BaseHandler extends AbstractHandler { UpdateCalloutContributionDefaultsInput, UpdateCalloutContributionPolicyInput, UpdateTemplateInput, - UpdateCommunityApplicationFormInput, UpdateCommunityGuidelinesInput, UpdateSpaceInput, UpdateSpaceSettingsEntityInput, @@ -130,8 +128,8 @@ export class BaseHandler extends AbstractHandler { UpdateSpaceSettingsEntityInput, UpdateSpaceSettingsInput, VisualUploadImageInput, - ApplyForBaseRoleOnRoleSetInput, - InviteForBaseRoleOnRoleSetInput, + ApplyForEntryRoleOnRoleSetInput, + InviteForEntryRoleOnRoleSetInput, InviteNewContributorForRoleOnRoleSetInput, ForumCreateDiscussionInput, SendMessageOnCalloutInput, diff --git a/src/domain/access/role-set/dto/role.set.dto.apply.for.base.role.ts b/src/domain/access/role-set/dto/role.set.dto.entry.role.apply.ts similarity index 92% rename from src/domain/access/role-set/dto/role.set.dto.apply.for.base.role.ts rename to src/domain/access/role-set/dto/role.set.dto.entry.role.apply.ts index d5fb66d9e3..fe8f6db654 100644 --- a/src/domain/access/role-set/dto/role.set.dto.apply.for.base.role.ts +++ b/src/domain/access/role-set/dto/role.set.dto.entry.role.apply.ts @@ -6,7 +6,7 @@ import { Type } from 'class-transformer'; import { UUID_LENGTH } from '@common/constants'; @InputType() -export class ApplyForBaseRoleOnRoleSetInput { +export class ApplyForEntryRoleOnRoleSetInput { @Field(() => UUID, { nullable: false }) @MaxLength(UUID_LENGTH) roleSetID!: string; diff --git a/src/domain/access/role-set/dto/role.set.dto.invite.for.base.role.ts b/src/domain/access/role-set/dto/role.set.dto.entry.role.invite.ts similarity index 93% rename from src/domain/access/role-set/dto/role.set.dto.invite.for.base.role.ts rename to src/domain/access/role-set/dto/role.set.dto.entry.role.invite.ts index 6dd2d7c8dd..d6f89354ac 100644 --- a/src/domain/access/role-set/dto/role.set.dto.invite.for.base.role.ts +++ b/src/domain/access/role-set/dto/role.set.dto.entry.role.invite.ts @@ -4,7 +4,7 @@ import { IsOptional, MaxLength } from 'class-validator'; import { MID_TEXT_LENGTH, UUID_LENGTH } from '@common/constants'; @InputType() -export class InviteForBaseRoleOnRoleSetInput { +export class InviteForEntryRoleOnRoleSetInput { @Field(() => UUID, { nullable: false }) @MaxLength(UUID_LENGTH) roleSetID!: string; diff --git a/src/domain/access/role-set/dto/role.set.dto.join.ts b/src/domain/access/role-set/dto/role.set.dto.entry.role.join.ts similarity index 86% rename from src/domain/access/role-set/dto/role.set.dto.join.ts rename to src/domain/access/role-set/dto/role.set.dto.entry.role.join.ts index 6dcf0aa937..e686f89ace 100644 --- a/src/domain/access/role-set/dto/role.set.dto.join.ts +++ b/src/domain/access/role-set/dto/role.set.dto.entry.role.join.ts @@ -4,7 +4,7 @@ import { MaxLength } from 'class-validator'; import { UUID_LENGTH } from '@common/constants'; @InputType() -export class JoinAsBaseRoleOnRoleSetInput { +export class JoinAsEntryRoleOnRoleSetInput { @Field(() => UUID, { nullable: false }) @MaxLength(UUID_LENGTH) roleSetID!: string; diff --git a/src/domain/access/role-set/role.set.entity.ts b/src/domain/access/role-set/role.set.entity.ts index 598fdcf01e..e20a85a739 100644 --- a/src/domain/access/role-set/role.set.entity.ts +++ b/src/domain/access/role-set/role.set.entity.ts @@ -37,7 +37,7 @@ export class RoleSet roles?: Role[]; @Column('varchar', { length: ENUM_LENGTH, nullable: false }) - baseRoleType!: CommunityRoleType; + entryRoleType!: CommunityRoleType; @OneToMany(() => Application, application => application.roleSet, { eager: false, diff --git a/src/domain/access/role-set/role.set.interface.ts b/src/domain/access/role-set/role.set.interface.ts index dceb2fb6a4..a5c80dd283 100644 --- a/src/domain/access/role-set/role.set.interface.ts +++ b/src/domain/access/role-set/role.set.interface.ts @@ -14,9 +14,9 @@ export abstract class IRoleSet extends IAuthorizable { @Field(() => CommunityRoleType, { nullable: false, description: - 'The CommunityRole that acts as the base for the RoleSet, so other roles potentially require it.', + 'The CommunityRole that acts as the entry Role for the RoleSet, so other roles potentially require it.', }) - baseRoleType!: CommunityRoleType; + entryRoleType!: CommunityRoleType; applications?: IApplication[]; invitations?: IInvitation[]; diff --git a/src/domain/access/role-set/role.set.resolver.mutations.ts b/src/domain/access/role-set/role.set.resolver.mutations.ts index f43c671d07..2ec50d9e03 100644 --- a/src/domain/access/role-set/role.set.resolver.mutations.ts +++ b/src/domain/access/role-set/role.set.resolver.mutations.ts @@ -39,9 +39,9 @@ import { AssignRoleOnRoleSetToVirtualContributorInput } from './dto/role.set.dto import { RemoveRoleOnRoleSetFromUserInput } from './dto/role.set.dto.role.remove.user'; import { RemoveRoleOnRoleSetFromOrganizationInput } from './dto/role.set.dto.role.remove.organization'; import { RemoveRoleOnRoleSetFromVirtualContributorInput } from './dto/role.set.dto.role.remove.virtual'; -import { ApplyForBaseRoleOnRoleSetInput } from './dto/role.set.dto.apply.for.base.role'; +import { ApplyForEntryRoleOnRoleSetInput as ApplyForEntryRoleOnRoleSetInput } from './dto/role.set.dto.entry.role.apply'; import { NotificationInputCommunityApplication } from '@services/adapters/notification-adapter/dto/notification.dto.input.community.application'; -import { InviteForBaseRoleOnRoleSetInput } from './dto/role.set.dto.invite.for.base.role'; +import { InviteForEntryRoleOnRoleSetInput } from './dto/role.set.dto.entry.role.invite'; import { RoleSetInvitationException } from '@common/exceptions/role.set.invitation.exception'; import { IContributor } from '@domain/community/contributor/contributor.interface'; import { EntityNotInitializedException } from '@common/exceptions/entity.not.initialized.exception'; @@ -52,6 +52,8 @@ import { IPlatformInvitation } from '@platform/invitation/platform.invitation.in import { InviteNewContributorForRoleOnRoleSetInput } from './dto/role.set.dto.platform.invitation.community'; import { NotificationInputCommunityInvitation } from '@services/adapters/notification-adapter/dto/notification.dto.input.community.invitation'; import { RoleSetAuthorizationService } from './role.set.service.authorization'; +import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; +import { JoinAsEntryRoleOnRoleSetInput } from './dto/role.set.dto.entry.role.join'; @Resolver() export class RoleSetResolverMutations { @@ -304,14 +306,54 @@ export class RoleSetResolverMutations { roleData.contributorID ); } + @UseGuards(GraphqlGuard) + @Mutation(() => IRoleSet, { + description: + 'Join the specified RoleSet using the base role, without going through an approval process.', + }) + async joinRoleSet( + @CurrentUser() agentInfo: AgentInfo, + @Args('joinData') joiningData: JoinAsEntryRoleOnRoleSetInput + ): Promise { + const roleSet = await this.roleSetService.getRoleSetOrFail( + joiningData.roleSetID + ); + const membershipStatus = await this.roleSetService.getMembershipStatus( + agentInfo, + roleSet + ); + if (membershipStatus === CommunityMembershipStatus.INVITATION_PENDING) { + throw new RoleSetMembershipException( + `Unable to join RoleSet (${roleSet.id}): invitation to join is pending.`, + LogContext.COMMUNITY + ); + } + + await this.authorizationService.grantAccessOrFail( + agentInfo, + roleSet.authorization, + AuthorizationPrivilege.COMMUNITY_JOIN, + `join community: ${roleSet.id}` + ); + + await this.roleSetService.assignUserToRole( + roleSet, + CommunityRoleType.MEMBER, + agentInfo.userID, + agentInfo, + true + ); + + return roleSet; + } @UseGuards(GraphqlGuard) @Mutation(() => IApplication, { - description: 'Apply to join the specified Community as a member.', + description: 'Apply to join the specified RoleSet in the entry Role.', }) - async applyForBaseRoleOnRoleSet( + async applyForEntryRoleOnRoleSet( @CurrentUser() agentInfo: AgentInfo, - @Args('applicationData') applicationData: ApplyForBaseRoleOnRoleSetInput + @Args('applicationData') applicationData: ApplyForEntryRoleOnRoleSetInput ): Promise { const roleSet = await this.roleSetService.getRoleSetOrFail( applicationData.roleSetID, @@ -382,7 +424,7 @@ export class RoleSetResolverMutations { async inviteContributorsForRoleSetMembership( @CurrentUser() agentInfo: AgentInfo, @Args('invitationData') - invitationData: InviteForBaseRoleOnRoleSetInput + invitationData: InviteForEntryRoleOnRoleSetInput ): Promise { const roleSet = await this.roleSetService.getRoleSetOrFail( invitationData.roleSetID, diff --git a/src/domain/access/role-set/role.set.service.ts b/src/domain/access/role-set/role.set.service.ts index 2cb25894c1..adbb177648 100644 --- a/src/domain/access/role-set/role.set.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -88,7 +88,7 @@ export class RoleSetService { roleSet.applications = []; roleSet.invitations = []; roleSet.platformInvitations = []; - roleSet.baseRoleType = roleSetData.baseRoleType; + roleSet.entryRoleType = roleSetData.baseRoleType; roleSet.parentRoleSet = roleSetData.parentRoleSet; @@ -1367,11 +1367,11 @@ export class RoleSetService { public async getBaseRoleDefinition(roleSet: IRoleSet): Promise { const roleDefinitions = await this.getRoleDefinitions(roleSet); const baseRole = roleDefinitions.find( - rd => rd.type === roleSet.baseRoleType + rd => rd.type === roleSet.entryRoleType ); if (!baseRole) { throw new RelationshipNotFoundException( - `Unable to find BaseRole of type ${roleSet.baseRoleType} for RoleSet: ${roleSet.id}`, + `Unable to find BaseRole of type ${roleSet.entryRoleType} for RoleSet: ${roleSet.id}`, LogContext.COMMUNITY ); } @@ -1382,7 +1382,7 @@ export class RoleSetService { roleSet: IRoleSet, roleType: CommunityRoleType ): Promise { - const isBaseRole = roleSet.baseRoleType === roleType; + const isBaseRole = roleSet.entryRoleType === roleType; return isBaseRole; } } diff --git a/src/domain/community/community/community.resolver.mutations.ts b/src/domain/community/community/community.resolver.mutations.ts index d8c1b71d04..d2d53626af 100644 --- a/src/domain/community/community/community.resolver.mutations.ts +++ b/src/domain/community/community/community.resolver.mutations.ts @@ -5,7 +5,7 @@ import { CommunityService } from './community.service'; import { CurrentUser, Profiling } from '@src/common/decorators'; import { GraphqlGuard } from '@core/authorization'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; -import { AuthorizationPrivilege, LogContext } from '@common/enums'; +import { AuthorizationPrivilege } from '@common/enums'; import { AuthorizationService } from '@core/authorization/authorization.service'; import { UserGroupAuthorizationService } from '../user-group/user-group.service.authorization'; import { AgentService } from '@domain/agent/agent/agent.service'; @@ -14,12 +14,6 @@ import { AuthorizationPolicyService } from '@domain/common/authorization-policy/ import { AgentBeginVerifiedCredentialOfferOutput } from '@domain/agent/agent/dto/agent.dto.verified.credential.offer.begin.output'; import { AlkemioUserClaim } from '@services/external/trust-registry/trust.registry.claim/claim.alkemio.user'; import { CommunityMemberClaim } from '@services/external/trust-registry/trust.registry.claim/claim.community.member'; -import { IRoleSet } from '@domain/access/role-set/role.set.interface'; -import { JoinAsBaseRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.join'; -import { RoleSetService } from '@domain/access/role-set/role.set.service'; -import { RoleSetMembershipException } from '@common/exceptions/role.set.membership.exception'; -import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; -import { CommunityRoleType } from '@common/enums/community.role'; @Resolver() export class CommunityResolverMutations { @@ -28,8 +22,7 @@ export class CommunityResolverMutations { private authorizationPolicyService: AuthorizationPolicyService, private userGroupAuthorizationService: UserGroupAuthorizationService, private communityService: CommunityService, - private agentService: AgentService, - private roleSetService: RoleSetService + private agentService: AgentService ) {} @UseGuards(GraphqlGuard) @@ -96,45 +89,4 @@ export class CommunityResolverMutations { ] ); } - - @UseGuards(GraphqlGuard) - @Mutation(() => IRoleSet, { - description: - 'Join the specified Community as a member, without going through an approval process.', - }) - async joinCommunity( - @CurrentUser() agentInfo: AgentInfo, - @Args('joinCommunityData') joiningData: JoinAsBaseRoleOnRoleSetInput - ): Promise { - const roleSet = await this.roleSetService.getRoleSetOrFail( - joiningData.roleSetID - ); - const membershipStatus = await this.roleSetService.getMembershipStatus( - agentInfo, - roleSet - ); - if (membershipStatus === CommunityMembershipStatus.INVITATION_PENDING) { - throw new RoleSetMembershipException( - `Unable to join Community (${roleSet.id}): invitation to join is pending.`, - LogContext.COMMUNITY - ); - } - - await this.authorizationService.grantAccessOrFail( - agentInfo, - roleSet.authorization, - AuthorizationPrivilege.COMMUNITY_JOIN, - `join community: ${roleSet.id}` - ); - - await this.roleSetService.assignUserToRole( - roleSet, - CommunityRoleType.MEMBER, - agentInfo.userID, - agentInfo, - true - ); - - return roleSet; - } } diff --git a/src/domain/community/community/dto/community.dto.update.application.form.ts b/src/domain/community/community/dto/community.dto.update.application.form.ts deleted file mode 100644 index b609247aec..0000000000 --- a/src/domain/community/community/dto/community.dto.update.application.form.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { InputType, Field } from '@nestjs/graphql'; -import { ValidateNested } from 'class-validator'; -import { Type } from 'class-transformer'; -import { UpdateFormInput } from '@domain/common/form/dto/form.dto.update'; -import { UUID } from '@domain/common/scalars'; - -@InputType() -export class UpdateCommunityApplicationFormInput { - @Field(() => UUID, { nullable: false }) - communityID!: string; - - @Field(() => UpdateFormInput, { nullable: false }) - @ValidateNested() - @Type(() => UpdateFormInput) - formData!: UpdateFormInput; -} diff --git a/src/migrations/1726843779059-roleSet.ts b/src/migrations/1726843779059-roleSet.ts index 9bf94dc04a..e6b4ef9419 100644 --- a/src/migrations/1726843779059-roleSet.ts +++ b/src/migrations/1726843779059-roleSet.ts @@ -49,7 +49,7 @@ export class RoleSet1726843779059 implements MigrationInterface { \`authorizationId\` char(36) NULL, \`applicationFormId\` char(36) NULL, \`parentRoleSetId\` char(36) NULL, - \`baseRoleType\` varchar(128) NOT NULL, + \`entryRoleType\` varchar(128) NOT NULL, UNIQUE INDEX \`REL_b038f74c8d4eadb839e78b99ce\` (\`authorizationId\`), UNIQUE INDEX \`REL_00905b142498f63e76d38fb254\` (\`applicationFormId\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB` @@ -124,7 +124,7 @@ export class RoleSet1726843779059 implements MigrationInterface { ); await queryRunner.query( - `INSERT INTO role_set (id, version, authorizationId, applicationFormId, baseRoleType) VALUES ('${roleSetID}', 1, '${roleSetAuthID}', '${community.applicationFormId}', 'member')` + `INSERT INTO role_set (id, version, authorizationId, applicationFormId, entryRoleType) VALUES ('${roleSetID}', 1, '${roleSetAuthID}', '${community.applicationFormId}', 'member')` ); } From 58093f2ff3b0022dc8179c65a2576d0463dc4c9d Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Mon, 23 Sep 2024 09:23:14 +0200 Subject: [PATCH 23/78] tidied up namings after chat with Carlos --- .../access/role-set/dto/role.set.dto.create.ts | 2 +- .../role-set/role.set.resolver.mutations.ts | 2 +- src/domain/access/role-set/role.set.service.ts | 18 +++++++++--------- src/domain/access/role/dto/role.dto.create.ts | 4 ++-- src/domain/access/role/role.entity.ts | 4 ++-- src/domain/access/role/role.interface.ts | 6 +++--- .../definitions/space.community.roles.ts | 12 ++++++------ .../definitions/subspace.community.roles.ts | 12 ++++++------ src/domain/space/space/space.service.ts | 2 +- src/migrations/1726843779059-roleSet.ts | 14 +++++++------- 10 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/domain/access/role-set/dto/role.set.dto.create.ts b/src/domain/access/role-set/dto/role.set.dto.create.ts index b74ea18a48..26f680c180 100644 --- a/src/domain/access/role-set/dto/role.set.dto.create.ts +++ b/src/domain/access/role-set/dto/role.set.dto.create.ts @@ -7,5 +7,5 @@ export class CreateRoleSetInput { parentRoleSet?: IRoleSet; roles!: CreateRoleInput[]; applicationForm!: CreateFormInput; - baseRoleType!: CommunityRoleType; + entryRoleType!: CommunityRoleType; } diff --git a/src/domain/access/role-set/role.set.resolver.mutations.ts b/src/domain/access/role-set/role.set.resolver.mutations.ts index 2ec50d9e03..0c276d6690 100644 --- a/src/domain/access/role-set/role.set.resolver.mutations.ts +++ b/src/domain/access/role-set/role.set.resolver.mutations.ts @@ -309,7 +309,7 @@ export class RoleSetResolverMutations { @UseGuards(GraphqlGuard) @Mutation(() => IRoleSet, { description: - 'Join the specified RoleSet using the base role, without going through an approval process.', + 'Join the specified RoleSet using the entry Role, without going through an approval process.', }) async joinRoleSet( @CurrentUser() agentInfo: AgentInfo, diff --git a/src/domain/access/role-set/role.set.service.ts b/src/domain/access/role-set/role.set.service.ts index adbb177648..3400c22dfd 100644 --- a/src/domain/access/role-set/role.set.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -88,7 +88,7 @@ export class RoleSetService { roleSet.applications = []; roleSet.invitations = []; roleSet.platformInvitations = []; - roleSet.entryRoleType = roleSetData.baseRoleType; + roleSet.entryRoleType = roleSetData.entryRoleType; roleSet.parentRoleSet = roleSetData.parentRoleSet; @@ -1364,25 +1364,25 @@ export class RoleSetService { return role; } - public async getBaseRoleDefinition(roleSet: IRoleSet): Promise { + public async getEntryRoleDefinition(roleSet: IRoleSet): Promise { const roleDefinitions = await this.getRoleDefinitions(roleSet); - const baseRole = roleDefinitions.find( + const entryRole = roleDefinitions.find( rd => rd.type === roleSet.entryRoleType ); - if (!baseRole) { + if (!entryRole) { throw new RelationshipNotFoundException( - `Unable to find BaseRole of type ${roleSet.entryRoleType} for RoleSet: ${roleSet.id}`, + `Unable to find entry level Role of type ${roleSet.entryRoleType} for RoleSet: ${roleSet.id}`, LogContext.COMMUNITY ); } - return baseRole; + return entryRole; } - public async isBaseRole( + public async isEntryRole( roleSet: IRoleSet, roleType: CommunityRoleType ): Promise { - const isBaseRole = roleSet.entryRoleType === roleType; - return isBaseRole; + const isEntryRole = roleSet.entryRoleType === roleType; + return isEntryRole; } } diff --git a/src/domain/access/role/dto/role.dto.create.ts b/src/domain/access/role/dto/role.dto.create.ts index 4585ff2c69..863efe71de 100644 --- a/src/domain/access/role/dto/role.dto.create.ts +++ b/src/domain/access/role/dto/role.dto.create.ts @@ -4,8 +4,8 @@ import { IContributorRolePolicy } from '../contributor.role.policy.interface'; export class CreateRoleInput { type!: CommunityRoleType; - requireBaseRole!: boolean; - requireParentRole!: boolean; + requireEntryRole!: boolean; + requiresSameRoleInParentRoleSet!: boolean; credentialData!: ICredentialDefinition; parentCredentialsData!: ICredentialDefinition[]; userPolicyData!: IContributorRolePolicy; diff --git a/src/domain/access/role/role.entity.ts b/src/domain/access/role/role.entity.ts index 3745370a33..9d476216b9 100644 --- a/src/domain/access/role/role.entity.ts +++ b/src/domain/access/role/role.entity.ts @@ -24,10 +24,10 @@ export class Role extends BaseAlkemioEntity implements IRole { parentCredentials!: string; @Column('boolean', { nullable: false }) - requiresBaseRole!: boolean; + requiresEntryRole!: boolean; @Column('boolean', { nullable: false }) - requiresParentRole!: boolean; + requiresSameRoleInParentRoleSet!: boolean; @Column('text', { nullable: false }) userPolicy!: string; diff --git a/src/domain/access/role/role.interface.ts b/src/domain/access/role/role.interface.ts index 1fb26727a3..de0bdc6744 100644 --- a/src/domain/access/role/role.interface.ts +++ b/src/domain/access/role/role.interface.ts @@ -13,16 +13,16 @@ export abstract class IRole extends IBaseAlkemio { @Field(() => Boolean, { nullable: false, description: - 'Flag to indicate if this Role requires the Base role to be held.', + 'Flag to indicate if this Role requires the entry level role to be held.', }) - requiresBaseRole!: boolean; + requiresEntryRole!: boolean; @Field(() => Boolean, { nullable: false, description: 'Flag to indicate if this Role requires having the same role in the Parent RoleSet.', }) - requiresParentRole!: boolean; + requiresSameRoleInParentRoleSet!: boolean; credential!: string; diff --git a/src/domain/space/space.defaults/definitions/space.community.roles.ts b/src/domain/space/space.defaults/definitions/space.community.roles.ts index a160b9b660..92b833a9e3 100644 --- a/src/domain/space/space.defaults/definitions/space.community.roles.ts +++ b/src/domain/space/space.defaults/definitions/space.community.roles.ts @@ -5,8 +5,8 @@ import { CreateRoleInput } from '@domain/access/role/dto/role.dto.create'; export const spaceCommunityRoles: CreateRoleInput[] = [ { type: CommunityRoleType.MEMBER, - requireBaseRole: false, - requireParentRole: true, + requireEntryRole: false, + requiresSameRoleInParentRoleSet: true, credentialData: { type: AuthorizationCredential.SPACE_MEMBER, resourceID: '', @@ -30,8 +30,8 @@ export const spaceCommunityRoles: CreateRoleInput[] = [ }, { type: CommunityRoleType.LEAD, - requireBaseRole: true, - requireParentRole: false, + requireEntryRole: true, + requiresSameRoleInParentRoleSet: false, credentialData: { type: AuthorizationCredential.SPACE_LEAD, resourceID: '', @@ -55,8 +55,8 @@ export const spaceCommunityRoles: CreateRoleInput[] = [ }, { type: CommunityRoleType.ADMIN, - requireBaseRole: true, - requireParentRole: false, + requireEntryRole: true, + requiresSameRoleInParentRoleSet: false, credentialData: { type: AuthorizationCredential.SPACE_ADMIN, resourceID: '', diff --git a/src/domain/space/space.defaults/definitions/subspace.community.roles.ts b/src/domain/space/space.defaults/definitions/subspace.community.roles.ts index 218e3827c6..782f6765e6 100644 --- a/src/domain/space/space.defaults/definitions/subspace.community.roles.ts +++ b/src/domain/space/space.defaults/definitions/subspace.community.roles.ts @@ -5,8 +5,8 @@ import { CreateRoleInput } from '@domain/access/role/dto/role.dto.create'; export const subspaceCommunityRoles: CreateRoleInput[] = [ { type: CommunityRoleType.MEMBER, - requireBaseRole: false, - requireParentRole: true, + requireEntryRole: false, + requiresSameRoleInParentRoleSet: true, credentialData: { type: AuthorizationCredential.SPACE_MEMBER, resourceID: '', @@ -30,8 +30,8 @@ export const subspaceCommunityRoles: CreateRoleInput[] = [ }, { type: CommunityRoleType.LEAD, - requireBaseRole: true, - requireParentRole: false, + requireEntryRole: true, + requiresSameRoleInParentRoleSet: false, credentialData: { type: AuthorizationCredential.SPACE_LEAD, resourceID: '', @@ -55,8 +55,8 @@ export const subspaceCommunityRoles: CreateRoleInput[] = [ }, { type: CommunityRoleType.ADMIN, - requireBaseRole: true, - requireParentRole: false, + requireEntryRole: true, + requiresSameRoleInParentRoleSet: false, credentialData: { type: AuthorizationCredential.SPACE_ADMIN, resourceID: '', diff --git a/src/domain/space/space/space.service.ts b/src/domain/space/space/space.service.ts index 75fd5ae31a..5ee1b66f83 100644 --- a/src/domain/space/space/space.service.ts +++ b/src/domain/space/space/space.service.ts @@ -169,7 +169,7 @@ export class SpaceService { roleSetData: { roles: roleSetRolesData, applicationForm: applicationFormData, - baseRoleType: CommunityRoleType.MEMBER, + entryRoleType: CommunityRoleType.MEMBER, }, guidelines: { // TODO: get this from defaults service diff --git a/src/migrations/1726843779059-roleSet.ts b/src/migrations/1726843779059-roleSet.ts index e6b4ef9419..a96eda418b 100644 --- a/src/migrations/1726843779059-roleSet.ts +++ b/src/migrations/1726843779059-roleSet.ts @@ -33,8 +33,8 @@ export class RoleSet1726843779059 implements MigrationInterface { \`version\` int NOT NULL, \`type\` varchar(128) NOT NULL, \`credential\` text NOT NULL, \`parentCredentials\` text NOT NULL, - \`requiresBaseRole\` tinyint NOT NULL, - \`requiresParentRole\` tinyint NOT NULL, + \`requiresEntryRole\` tinyint NOT NULL, + \`requiresSameRoleInParentRoleSet\` tinyint NOT NULL, \`userPolicy\` text NOT NULL, \`organizationPolicy\` text NOT NULL, \`virtualContributorPolicy\` text NOT NULL, @@ -294,8 +294,8 @@ export class RoleSet1726843779059 implements MigrationInterface { roleSetID: string, vcMin: number, vcMax: number, - requiresBaseRole: boolean, - requiresParentRole: boolean + requiresEntryRole: boolean, + requiresSameRoleInParentRoleSet: boolean ) { const roleID = randomUUID(); const communityRolePolicy: CommunityPolicy = JSON.parse(communityPolicyStr); @@ -314,13 +314,13 @@ export class RoleSet1726843779059 implements MigrationInterface { // Create the role for the member role await queryRunner.query(` - INSERT INTO role (id, version, type, credential, parentCredentials, requiresBaseRole, requiresParentRole, userPolicy, organizationPolicy, virtualContributorPolicy, roleSetId) VALUES + INSERT INTO role (id, version, type, credential, parentCredentials, requiresEntryRole, requiresSameRoleInParentRoleSet, userPolicy, organizationPolicy, virtualContributorPolicy, roleSetId) VALUES ('${roleID}', 1, '${type}', '${JSON.stringify(communityRolePolicy.credential)}', '${JSON.stringify(communityRolePolicy.parentCredentials)}', - ${requiresBaseRole}, - ${requiresParentRole}, + ${requiresEntryRole}, + ${requiresSameRoleInParentRoleSet}, '${JSON.stringify(userPolicy)}', '${JSON.stringify(organizationPolicy)}', '${JSON.stringify(vcPolicy)}', From 9ef4ffb7f3bb7d690a798f65b475fb8145ea10e6 Mon Sep 17 00:00:00 2001 From: Carlos Cano Date: Tue, 24 Sep 2024 11:28:15 +0200 Subject: [PATCH 24/78] Migration fix --- src/migrations/1726843779059-roleSet.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/migrations/1726843779059-roleSet.ts b/src/migrations/1726843779059-roleSet.ts index a96eda418b..76c5411de3 100644 --- a/src/migrations/1726843779059-roleSet.ts +++ b/src/migrations/1726843779059-roleSet.ts @@ -126,6 +126,10 @@ export class RoleSet1726843779059 implements MigrationInterface { await queryRunner.query( `INSERT INTO role_set (id, version, authorizationId, applicationFormId, entryRoleType) VALUES ('${roleSetID}', 1, '${roleSetAuthID}', '${community.applicationFormId}', 'member')` ); + + await queryRunner.query( + `UPDATE community SET roleSetId = '${roleSetID}' WHERE id = '${community.id}'` + ); } // Second loop makes the hierarchy linked @@ -159,12 +163,21 @@ export class RoleSet1726843779059 implements MigrationInterface { await queryRunner.query( `ALTER TABLE \`platform_invitation\` CHANGE \`communityId\` \`roleSetId\` char(36) NULL` ); + await queryRunner.query( + `UPDATE platform_invitation SET roleSetId = (SELECT roleSetId FROM community WHERE id = platform_invitation.roleSetId)` + ); await queryRunner.query( `ALTER TABLE \`application\` CHANGE \`communityId\` \`roleSetId\` char(36) NULL` ); + await queryRunner.query( + `UPDATE application SET roleSetId = (SELECT roleSetId FROM community WHERE id = application.roleSetId)` + ); await queryRunner.query( `ALTER TABLE \`invitation\` CHANGE \`communityId\` \`roleSetId\` char(36) NULL` ); + await queryRunner.query( + `UPDATE invitation SET roleSetId = (SELECT roleSetId FROM community WHERE id = invitation.roleSetId)` + ); await queryRunner.query( `ALTER TABLE \`community\` DROP COLUMN \`applicationFormId\`` @@ -179,6 +192,7 @@ export class RoleSet1726843779059 implements MigrationInterface { await queryRunner.query( `ALTER TABLE \`role\` ADD CONSTRAINT \`FK_66d695b73839e9b66ff1350d34f\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` ); + await queryRunner.query( `ALTER TABLE \`platform_invitation\` ADD CONSTRAINT \`FK_562dce4a08bb214f08107b3631e\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` ); From 6f69d7d7b7e4cc184e5a14cf26d4a375bf153c5b Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Tue, 24 Sep 2024 12:32:17 +0200 Subject: [PATCH 25/78] set roleset hierarchy --- src/domain/community/community/community.service.ts | 10 ++++++++-- src/domain/space/space/space.service.ts | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/domain/community/community/community.service.ts b/src/domain/community/community/community.service.ts index 4ca27aab40..98ba3d64cf 100644 --- a/src/domain/community/community/community.service.ts +++ b/src/domain/community/community/community.service.ts @@ -224,9 +224,14 @@ export class CommunityService { community?: ICommunity, parentCommunity?: ICommunity ): Promise { - if (!community || !parentCommunity) { + if ( + !community || + !community.roleSet || + !parentCommunity || + !parentCommunity.roleSet + ) { throw new EntityNotInitializedException( - 'Community not set', + `Unable to set the parent relationship for community with rolesets: ${community?.id} and ${parentCommunity?.id}`, LogContext.COMMUNITY ); } @@ -235,6 +240,7 @@ export class CommunityService { community.roleSet = await this.roleSetService.inheritParentCredentials( community.roleSet ); + community.parentCommunity.roleSet.parentRoleSet = parentCommunity.roleSet; return community; } diff --git a/src/domain/space/space/space.service.ts b/src/domain/space/space/space.service.ts index 5ee1b66f83..14dfe79001 100644 --- a/src/domain/space/space/space.service.ts +++ b/src/domain/space/space/space.service.ts @@ -1201,7 +1201,7 @@ export class SpaceService { return this.spaceSettingsService.getSettings(space.settingsStr); } - public async setCommunityHierarchyForSubspace( + private async setCommunityHierarchyForSubspace( parentCommunity: ICommunity, childCommunity: ICommunity | undefined ): Promise { From 0fdae8e6859f1326599e1b865450eaa95aeeb0ad Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Tue, 24 Sep 2024 20:00:09 +0200 Subject: [PATCH 26/78] fixed lookup of applications for a given roleset --- .../access/application/application.service.ts | 23 +++++++++---------- .../community.resolver.service.ts | 19 --------------- 2 files changed, 11 insertions(+), 31 deletions(-) diff --git a/src/domain/access/application/application.service.ts b/src/domain/access/application/application.service.ts index 64ff24095f..a1434cb636 100644 --- a/src/domain/access/application/application.service.ts +++ b/src/domain/access/application/application.service.ts @@ -131,19 +131,18 @@ export class ApplicationService { async findExistingApplications( userID: string, - communityID: string + roleSetID: string ): Promise { - const existingApplications = await this.applicationRepository - .createQueryBuilder('application') - .leftJoinAndSelect('application.user', 'user') - .leftJoinAndSelect('application.community', 'community') - .where('user.id = :userID') - .andWhere('community.id = :communityID') - .setParameters({ - userID: `${userID}`, - communityID: communityID, - }) - .getMany(); + const existingApplications = await this.applicationRepository.find({ + where: { + user: { id: userID }, + roleSet: { id: roleSetID }, + }, + relations: { + roleSet: true, + user: true, + }, + }); if (existingApplications.length > 0) return existingApplications; return []; } diff --git a/src/services/infrastructure/entity-resolver/community.resolver.service.ts b/src/services/infrastructure/entity-resolver/community.resolver.service.ts index f352a5d235..b57070a79b 100644 --- a/src/services/infrastructure/entity-resolver/community.resolver.service.ts +++ b/src/services/infrastructure/entity-resolver/community.resolver.service.ts @@ -437,23 +437,4 @@ export class CommunityResolverService { } } } - - public async getCommunityWithParentOrFail( - communityID: string - ): Promise { - const community = await this.communityRepository - .createQueryBuilder('community') - .leftJoinAndSelect('community.parentCommunity', 'parentCommunity') - .where('community.id = :id') - .setParameters({ id: `${communityID}` }) - .getOne(); - - if (!community) { - throw new EntityNotFoundException( - `Unable to find Community with parent: ${communityID}`, - LogContext.NOTIFICATIONS - ); - } - return community; - } } From 5dbf4c8f44ac70086bc68af562a636086426c58f Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Tue, 24 Sep 2024 21:39:13 +0200 Subject: [PATCH 27/78] fixed a couple of issues with creating space --- src/domain/access/role/dto/role.dto.create.ts | 2 +- .../definitions/space.community.roles.ts | 6 ++--- .../definitions/subspace.community.roles.ts | 6 ++--- .../notification.payload.builder.ts | 7 +++--- .../community.resolver.service.ts | 24 ++++++++++++++++++- 5 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/domain/access/role/dto/role.dto.create.ts b/src/domain/access/role/dto/role.dto.create.ts index 863efe71de..cd0e9ecb8c 100644 --- a/src/domain/access/role/dto/role.dto.create.ts +++ b/src/domain/access/role/dto/role.dto.create.ts @@ -4,7 +4,7 @@ import { IContributorRolePolicy } from '../contributor.role.policy.interface'; export class CreateRoleInput { type!: CommunityRoleType; - requireEntryRole!: boolean; + requiresEntryRole!: boolean; requiresSameRoleInParentRoleSet!: boolean; credentialData!: ICredentialDefinition; parentCredentialsData!: ICredentialDefinition[]; diff --git a/src/domain/space/space.defaults/definitions/space.community.roles.ts b/src/domain/space/space.defaults/definitions/space.community.roles.ts index 92b833a9e3..785386d8bb 100644 --- a/src/domain/space/space.defaults/definitions/space.community.roles.ts +++ b/src/domain/space/space.defaults/definitions/space.community.roles.ts @@ -5,7 +5,7 @@ import { CreateRoleInput } from '@domain/access/role/dto/role.dto.create'; export const spaceCommunityRoles: CreateRoleInput[] = [ { type: CommunityRoleType.MEMBER, - requireEntryRole: false, + requiresEntryRole: false, requiresSameRoleInParentRoleSet: true, credentialData: { type: AuthorizationCredential.SPACE_MEMBER, @@ -30,7 +30,7 @@ export const spaceCommunityRoles: CreateRoleInput[] = [ }, { type: CommunityRoleType.LEAD, - requireEntryRole: true, + requiresEntryRole: true, requiresSameRoleInParentRoleSet: false, credentialData: { type: AuthorizationCredential.SPACE_LEAD, @@ -55,7 +55,7 @@ export const spaceCommunityRoles: CreateRoleInput[] = [ }, { type: CommunityRoleType.ADMIN, - requireEntryRole: true, + requiresEntryRole: true, requiresSameRoleInParentRoleSet: false, credentialData: { type: AuthorizationCredential.SPACE_ADMIN, diff --git a/src/domain/space/space.defaults/definitions/subspace.community.roles.ts b/src/domain/space/space.defaults/definitions/subspace.community.roles.ts index 782f6765e6..c99d1292c3 100644 --- a/src/domain/space/space.defaults/definitions/subspace.community.roles.ts +++ b/src/domain/space/space.defaults/definitions/subspace.community.roles.ts @@ -5,7 +5,7 @@ import { CreateRoleInput } from '@domain/access/role/dto/role.dto.create'; export const subspaceCommunityRoles: CreateRoleInput[] = [ { type: CommunityRoleType.MEMBER, - requireEntryRole: false, + requiresEntryRole: false, requiresSameRoleInParentRoleSet: true, credentialData: { type: AuthorizationCredential.SPACE_MEMBER, @@ -30,7 +30,7 @@ export const subspaceCommunityRoles: CreateRoleInput[] = [ }, { type: CommunityRoleType.LEAD, - requireEntryRole: true, + requiresEntryRole: true, requiresSameRoleInParentRoleSet: false, credentialData: { type: AuthorizationCredential.SPACE_LEAD, @@ -55,7 +55,7 @@ export const subspaceCommunityRoles: CreateRoleInput[] = [ }, { type: CommunityRoleType.ADMIN, - requireEntryRole: true, + requiresEntryRole: true, requiresSameRoleInParentRoleSet: false, credentialData: { type: AuthorizationCredential.SPACE_ADMIN, diff --git a/src/services/adapters/notification-adapter/notification.payload.builder.ts b/src/services/adapters/notification-adapter/notification.payload.builder.ts index e1db5c2f1d..fc402b8ab4 100644 --- a/src/services/adapters/notification-adapter/notification.payload.builder.ts +++ b/src/services/adapters/notification-adapter/notification.payload.builder.ts @@ -717,9 +717,10 @@ export class NotificationPayloadBuilder { triggeredBy: string ): Promise { const basePayload = this.buildBaseEventPayload(triggeredBy); - const space = await this.communityResolverService.getSpaceForRoleSetOrFail( - community.id - ); + const space = + await this.communityResolverService.getSpaceForCommunityOrFail( + community.id + ); const url = await this.urlGeneratorService.generateUrlForProfile( space.profile ); diff --git a/src/services/infrastructure/entity-resolver/community.resolver.service.ts b/src/services/infrastructure/entity-resolver/community.resolver.service.ts index b57070a79b..125a8e6fff 100644 --- a/src/services/infrastructure/entity-resolver/community.resolver.service.ts +++ b/src/services/infrastructure/entity-resolver/community.resolver.service.ts @@ -324,7 +324,29 @@ export class CommunityResolverService { }); if (!space) { throw new EntityNotFoundException( - `Unable to find space for community: ${roleSetID}`, + `Unable to find space for roleSet: ${roleSetID}`, + LogContext.URL_GENERATOR + ); + } + return space; + } + + public async getSpaceForCommunityOrFail( + communityID: string + ): Promise { + const space = await this.entityManager.findOne(Space, { + where: { + community: { + id: communityID, + }, + }, + relations: { + profile: true, + }, + }); + if (!space) { + throw new EntityNotFoundException( + `Unable to find space for Community: ${communityID}`, LogContext.URL_GENERATOR ); } From 3e35777b51ea0a112a8243aaafe6e6fc04d582d2 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Tue, 24 Sep 2024 21:41:06 +0200 Subject: [PATCH 28/78] removed enabled on contributor role policy --- src/domain/access/role/contributor.role.policy.interface.ts | 5 ----- src/domain/access/role/contributor.role.policy.ts | 2 -- 2 files changed, 7 deletions(-) diff --git a/src/domain/access/role/contributor.role.policy.interface.ts b/src/domain/access/role/contributor.role.policy.interface.ts index 37bfa8d977..6da7c62002 100644 --- a/src/domain/access/role/contributor.role.policy.interface.ts +++ b/src/domain/access/role/contributor.role.policy.interface.ts @@ -2,11 +2,6 @@ import { Field, ObjectType } from '@nestjs/graphql'; @ObjectType('ContributorRolePolicy') export abstract class IContributorRolePolicy { - @Field(() => Boolean, { - description: 'Is this role enabled for this Contributor', - }) - enabled!: boolean; - @Field(() => Number, { description: 'Minimum number of Contributors in this role', }) diff --git a/src/domain/access/role/contributor.role.policy.ts b/src/domain/access/role/contributor.role.policy.ts index 381cb91d0e..309fe41628 100644 --- a/src/domain/access/role/contributor.role.policy.ts +++ b/src/domain/access/role/contributor.role.policy.ts @@ -3,11 +3,9 @@ import { IContributorRolePolicy } from './contributor.role.policy.interface'; export class ContributorRolePolicy implements IContributorRolePolicy { minimum: number; maximum: number; - enabled: boolean; constructor() { this.minimum = -1; this.maximum = -1; - this.enabled = false; } } From dd8c7d01b7efbddb3d5986dd7700be8e51f85f69 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Tue, 24 Sep 2024 21:41:51 +0200 Subject: [PATCH 29/78] updated defaults --- .../space.defaults/definitions/space.community.roles.ts | 9 --------- .../definitions/subspace.community.roles.ts | 9 --------- 2 files changed, 18 deletions(-) diff --git a/src/domain/space/space.defaults/definitions/space.community.roles.ts b/src/domain/space/space.defaults/definitions/space.community.roles.ts index 785386d8bb..d12430f7e2 100644 --- a/src/domain/space/space.defaults/definitions/space.community.roles.ts +++ b/src/domain/space/space.defaults/definitions/space.community.roles.ts @@ -13,17 +13,14 @@ export const spaceCommunityRoles: CreateRoleInput[] = [ }, parentCredentialsData: [], userPolicyData: { - enabled: true, minimum: 0, maximum: -1, }, organizationPolicyData: { - enabled: true, minimum: 0, maximum: -1, }, virtualContributorPolicyData: { - enabled: true, minimum: 0, maximum: -1, }, @@ -38,17 +35,14 @@ export const spaceCommunityRoles: CreateRoleInput[] = [ }, parentCredentialsData: [], userPolicyData: { - enabled: true, minimum: 0, maximum: 2, }, organizationPolicyData: { - enabled: true, minimum: 0, maximum: 2, }, virtualContributorPolicyData: { - enabled: true, minimum: 0, maximum: 1, }, @@ -63,17 +57,14 @@ export const spaceCommunityRoles: CreateRoleInput[] = [ }, parentCredentialsData: [], userPolicyData: { - enabled: true, minimum: 0, maximum: -1, }, organizationPolicyData: { - enabled: true, minimum: 0, maximum: 0, }, virtualContributorPolicyData: { - enabled: true, minimum: 0, maximum: 0, }, diff --git a/src/domain/space/space.defaults/definitions/subspace.community.roles.ts b/src/domain/space/space.defaults/definitions/subspace.community.roles.ts index c99d1292c3..73dfbe1313 100644 --- a/src/domain/space/space.defaults/definitions/subspace.community.roles.ts +++ b/src/domain/space/space.defaults/definitions/subspace.community.roles.ts @@ -13,17 +13,14 @@ export const subspaceCommunityRoles: CreateRoleInput[] = [ }, parentCredentialsData: [], userPolicyData: { - enabled: true, minimum: 0, maximum: -1, }, organizationPolicyData: { - enabled: true, minimum: 0, maximum: -1, }, virtualContributorPolicyData: { - enabled: true, minimum: 0, maximum: -1, }, @@ -38,17 +35,14 @@ export const subspaceCommunityRoles: CreateRoleInput[] = [ }, parentCredentialsData: [], userPolicyData: { - enabled: true, minimum: 0, maximum: 2, }, organizationPolicyData: { - enabled: true, minimum: 0, maximum: 9, }, virtualContributorPolicyData: { - enabled: true, minimum: 0, maximum: 1, }, @@ -63,17 +57,14 @@ export const subspaceCommunityRoles: CreateRoleInput[] = [ }, parentCredentialsData: [], userPolicyData: { - enabled: true, minimum: 0, maximum: -1, }, organizationPolicyData: { - enabled: true, minimum: 0, maximum: 0, }, virtualContributorPolicyData: { - enabled: true, minimum: 0, maximum: 0, }, From d78f77ffc9ab2d37f398574cc78a0233f616783e Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Tue, 24 Sep 2024 21:42:45 +0200 Subject: [PATCH 30/78] updefault for space level requires parent role --- .../space/space.defaults/definitions/space.community.roles.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domain/space/space.defaults/definitions/space.community.roles.ts b/src/domain/space/space.defaults/definitions/space.community.roles.ts index d12430f7e2..87a640b372 100644 --- a/src/domain/space/space.defaults/definitions/space.community.roles.ts +++ b/src/domain/space/space.defaults/definitions/space.community.roles.ts @@ -6,7 +6,7 @@ export const spaceCommunityRoles: CreateRoleInput[] = [ { type: CommunityRoleType.MEMBER, requiresEntryRole: false, - requiresSameRoleInParentRoleSet: true, + requiresSameRoleInParentRoleSet: false, // not required at Space level credentialData: { type: AuthorizationCredential.SPACE_MEMBER, resourceID: '', From 795534015a0c9a8da04ced8abc77e25d5db7fcf2 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Wed, 25 Sep 2024 07:33:22 +0200 Subject: [PATCH 31/78] fixed setting of resourceID on credentials in new RoleSet --- src/domain/access/role-set/role.set.service.ts | 2 ++ src/domain/access/role/role.service.ts | 10 ++++++++++ src/domain/space/space/space.service.ts | 10 ++-------- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/domain/access/role-set/role.set.service.ts b/src/domain/access/role-set/role.set.service.ts index 3400c22dfd..2135f2aabf 100644 --- a/src/domain/access/role-set/role.set.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -228,6 +228,8 @@ export class RoleSetService { for (const roleDefinition of roleDefinitions) { const credential = this.roleService.getCredentialForRole(roleDefinition); credential.resourceID = resourceID; + roleDefinition.credential = + this.roleService.convertCredentialToString(credential); } return roleSet; diff --git a/src/domain/access/role/role.service.ts b/src/domain/access/role/role.service.ts index 3c727412af..805bb9106e 100644 --- a/src/domain/access/role/role.service.ts +++ b/src/domain/access/role/role.service.ts @@ -57,6 +57,16 @@ export class RoleService { return result; } + public convertCredentialToString(credential: ICredentialDefinition): string { + return JSON.stringify(credential); + } + + public convertParentCredentialsToString( + parentCredentials: ICredentialDefinition[] + ): string { + return JSON.stringify(parentCredentials); + } + public getUserPolicy(role: IRole): IContributorRolePolicy { const result: IContributorRolePolicy = JSON.parse(role.userPolicy); return result; diff --git a/src/domain/space/space/space.service.ts b/src/domain/space/space/space.service.ts index 14dfe79001..a5be739f39 100644 --- a/src/domain/space/space/space.service.ts +++ b/src/domain/space/space/space.service.ts @@ -206,6 +206,7 @@ export class SpaceService { space.levelZeroSpaceID = ''; // save the collaboration and all it's template sets await this.save(space); + if (spaceData.level === SpaceLevel.SPACE) { space.levelZeroSpaceID = space.id; } @@ -239,17 +240,10 @@ export class SpaceService { agentInfo ); - // save the collaboration and all it's template sets - await this.save(space); - - /////////// Agents - space.agent = await this.agentService.createAgent({ type: AgentType.SPACE, }); - await this.save(space); - if (space.level === SpaceLevel.SPACE) { await this.addLevelZeroSpaceEntities(space); } @@ -268,7 +262,7 @@ export class SpaceService { space.id ); - return space; + return await this.save(space); } private async addDefaultTemplatesToSpaceLibrary( From c90465c4a1db4546ecb2ba7cf453b133a36cd473 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Wed, 25 Sep 2024 08:09:21 +0200 Subject: [PATCH 32/78] removed community-parentCommunity relationship; fixed issues with creating subspace partially --- .../role-set/role.set.resolver.fields.ts | 13 ++--- .../role-set/role.set.resolver.mutations.ts | 8 ++-- .../access/role-set/role.set.service.ts | 23 +++++---- .../community/community/community.entity.ts | 17 +------ .../community/community.interface.ts | 2 - .../community.service.authorization.ts | 8 ++-- .../community/community/community.service.ts | 45 ----------------- ...ls.ts => user.dto.role.set.credentials.ts} | 2 +- ...> user.dto.role.set.member.credentials.ts} | 4 +- src/domain/community/user/user.service.ts | 20 ++++---- .../space/space/space.resolver.mutations.ts | 3 +- src/domain/space/space/space.service.ts | 48 ++++++++++++------- src/migrations/1726843779059-roleSet.ts | 4 ++ test/mocks/community.service.mock.ts | 4 +- 14 files changed, 78 insertions(+), 123 deletions(-) rename src/domain/community/user/dto/{user.dto.community.credentials.ts => user.dto.role.set.credentials.ts} (87%) rename src/domain/community/user/dto/{user.dto.community.member.credentials.ts => user.dto.role.set.member.credentials.ts} (71%) diff --git a/src/domain/access/role-set/role.set.resolver.fields.ts b/src/domain/access/role-set/role.set.resolver.fields.ts index cf94475c39..3e69a971a8 100644 --- a/src/domain/access/role-set/role.set.resolver.fields.ts +++ b/src/domain/access/role-set/role.set.resolver.fields.ts @@ -26,6 +26,7 @@ import { IRole } from '../role/role.interface'; import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { CommunityRoleImplicit } from '@common/enums/community.role.implicit'; +import { RoleSetMemberCredentials } from '@domain/community/user/dto/user.dto.role.set.member.credentials'; @Resolver(() => IRoleSet) export class RoleSetResolverFields { @@ -51,22 +52,22 @@ export class RoleSetResolverFields { CommunityRoleType.MEMBER ); - const parentCommunity = await this.roleSetService.getParentRoleSet(roleSet); + const parentRoleSet = await this.roleSetService.getParentRoleSet(roleSet); - const parentCommunityMemberCredentials = parentCommunity + const parentRoleSetMemberCredential = parentRoleSet ? await this.roleSetService.getCredentialDefinitionForRole( - parentCommunity, + parentRoleSet, CommunityRoleType.MEMBER ) : undefined; - const roleSetMemberCredentials = { + const roleSetMemberCredential: RoleSetMemberCredentials = { member: roleDefinition, - parentCommunityMember: parentCommunityMemberCredentials, + parentRoleSetMember: parentRoleSetMemberCredential, }; return this.userService.getPaginatedAvailableMemberUsers( - roleSetMemberCredentials, + roleSetMemberCredential, pagination, filter ); diff --git a/src/domain/access/role-set/role.set.resolver.mutations.ts b/src/domain/access/role-set/role.set.resolver.mutations.ts index 0c276d6690..4f64034350 100644 --- a/src/domain/access/role-set/role.set.resolver.mutations.ts +++ b/src/domain/access/role-set/role.set.resolver.mutations.ts @@ -467,10 +467,10 @@ export class RoleSetResolverMutations { // Logic is that the ability to invite to a subspace requires the ability to invite to the // parent community if the user is not a member there if (roleSet.parentRoleSet) { - const parentCommunityAuthorization = roleSet.parentRoleSet.authorization; + const parentRoleSetAuthorization = roleSet.parentRoleSet.authorization; const canInviteToParent = this.authorizationService.isAccessGranted( agentInfo, - parentCommunityAuthorization, + parentRoleSetAuthorization, AuthorizationPrivilege.COMMUNITY_INVITE ); @@ -619,10 +619,10 @@ export class RoleSetResolverMutations { // Logic is that the ability to invite to a subspace requires the ability to invite to the // parent community if the user is not a member there if (roleSet.parentRoleSet) { - const parentCommunityAuthorization = roleSet.parentRoleSet.authorization; + const parentRoleSetAuthorization = roleSet.parentRoleSet.authorization; const canInviteToParent = this.authorizationService.isAccessGranted( agentInfo, - parentCommunityAuthorization, + parentRoleSetAuthorization, AuthorizationPrivilege.COMMUNITY_INVITE ); diff --git a/src/domain/access/role-set/role.set.service.ts b/src/domain/access/role-set/role.set.service.ts index 2135f2aabf..3337e813cb 100644 --- a/src/domain/access/role-set/role.set.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -1236,19 +1236,17 @@ export class RoleSetService { return result; } - public async inheritParentCredentials(roleSet: IRoleSet): Promise { - const roleSetParent = roleSet.parentRoleSet; - if (!roleSetParent) { - throw new RelationshipNotFoundException( - `Unable to inherit parent credentials for role Manager ${roleSet.id}`, - LogContext.ROLES - ); - } - const roleDefinitions = await this.getRoleDefinitions(roleSet); + public async setParentRoleSetAndCredentials( + childRoleSet: IRoleSet, + parentRoleSet: IRoleSet + ): Promise { + childRoleSet.parentRoleSet = parentRoleSet; + + const roleDefinitions = await this.getRoleDefinitions(childRoleSet); for (const roleDefinition of roleDefinitions) { const parentRoleDefinition = await this.getRoleDefinition( - roleSetParent, + parentRoleSet, roleDefinition.type ); const parentCredentials: ICredentialDefinition[] = []; @@ -1260,10 +1258,11 @@ export class RoleSetService { parentCredentials.push(parentDirectCredential); parentParentCredentials.forEach(c => parentCredentials?.push(c)); - roleDefinition.parentCredentials = JSON.stringify(parentCredentials); + roleDefinition.parentCredentials = + this.roleService.convertParentCredentialsToString(parentCredentials); } - return roleSet; + return childRoleSet; } public async getDirectParentCredentialForRole( diff --git a/src/domain/community/community/community.entity.ts b/src/domain/community/community/community.entity.ts index 313a84abe8..e3d15a3b47 100644 --- a/src/domain/community/community/community.entity.ts +++ b/src/domain/community/community/community.entity.ts @@ -1,11 +1,4 @@ -import { - Column, - Entity, - JoinColumn, - ManyToOne, - OneToMany, - OneToOne, -} from 'typeorm'; +import { Column, Entity, JoinColumn, OneToMany, OneToOne } from 'typeorm'; import { IGroupable } from '@src/common/interfaces/groupable.interface'; import { UserGroup } from '@domain/community/user-group/user-group.entity'; import { ICommunity } from '@domain/community/community/community.interface'; @@ -50,14 +43,6 @@ export class Community @JoinColumn() roleSet!: RoleSet; - // The parent community can have many child communities; the relationship is controlled by the child. - @ManyToOne(() => Community, { - eager: false, - cascade: false, - onDelete: 'SET NULL', - }) - parentCommunity?: Community; - @Column({ length: UUID_LENGTH, }) diff --git a/src/domain/community/community/community.interface.ts b/src/domain/community/community/community.interface.ts index fda92b55bb..fc1ce3839c 100644 --- a/src/domain/community/community/community.interface.ts +++ b/src/domain/community/community/community.interface.ts @@ -12,8 +12,6 @@ import { IRoleSet } from '@domain/access/role-set'; export abstract class ICommunity extends IAuthorizable { groups?: IUserGroup[]; - parentCommunity?: ICommunity; - roleSet!: IRoleSet; guidelines?: ICommunityGuidelines; diff --git a/src/domain/community/community/community.service.authorization.ts b/src/domain/community/community/community.service.authorization.ts index 9e994f4025..1612d761e3 100644 --- a/src/domain/community/community/community.service.authorization.ts +++ b/src/domain/community/community/community.service.authorization.ts @@ -340,21 +340,21 @@ export class CommunityAuthorizationService { const newRules: IAuthorizationPolicyRuleCredential[] = []; - const parentCommunityCredential = + const parentRoleSetMemberCredential = await this.roleSetService.getDirectParentCredentialForRole( roleSet, CommunityRoleType.MEMBER ); // Allow member of the parent community to Apply - if (parentCommunityCredential) { + if (parentRoleSetMemberCredential) { const membershipSettings = spaceSettings.membership; switch (membershipSettings.policy) { case CommunityMembershipPolicy.APPLICATIONS: const spaceMemberCanApply = this.authorizationPolicyService.createCredentialRule( [AuthorizationPrivilege.COMMUNITY_APPLY], - [parentCommunityCredential], + [parentRoleSetMemberCredential], CREDENTIAL_RULE_SUBSPACE_PARENT_MEMBER_APPLY ); spaceMemberCanApply.cascade = false; @@ -364,7 +364,7 @@ export class CommunityAuthorizationService { const spaceMemberCanJoin = this.authorizationPolicyService.createCredentialRule( [AuthorizationPrivilege.COMMUNITY_JOIN], - [parentCommunityCredential], + [parentRoleSetMemberCredential], CREDENTIAL_RULE_SUBSPACE_PARENT_MEMBER_JOIN ); spaceMemberCanJoin.cascade = false; diff --git a/src/domain/community/community/community.service.ts b/src/domain/community/community/community.service.ts index 98ba3d64cf..1086d9ecfe 100644 --- a/src/domain/community/community/community.service.ts +++ b/src/domain/community/community/community.service.ts @@ -206,45 +206,6 @@ export class CommunityService { return await this.communityRepository.save(community); } - async getParentCommunity( - community: ICommunity - ): Promise { - const communityWithParent = await this.getCommunityOrFail(community.id, { - relations: { parentCommunity: true }, - }); - - const parentCommunity = communityWithParent?.parentCommunity; - if (parentCommunity) { - return await this.getCommunityOrFail(parentCommunity.id); - } - return undefined; - } - - public async setParentCommunity( - community?: ICommunity, - parentCommunity?: ICommunity - ): Promise { - if ( - !community || - !community.roleSet || - !parentCommunity || - !parentCommunity.roleSet - ) { - throw new EntityNotInitializedException( - `Unable to set the parent relationship for community with rolesets: ${community?.id} and ${parentCommunity?.id}`, - LogContext.COMMUNITY - ); - } - community.parentCommunity = parentCommunity; - // Also update the communityPolicy - community.roleSet = await this.roleSetService.inheritParentCredentials( - community.roleSet - ); - community.parentCommunity.roleSet.parentRoleSet = parentCommunity.roleSet; - - return community; - } - public async getDisplayName(community: ICommunity): Promise { return await this.communityResolverService.getDisplayNameForRoleSetOrFail( community.id @@ -319,10 +280,4 @@ export class CommunityService { community.id ); } - - async isSpaceCommunity(community: ICommunity): Promise { - const parentCommunity = await this.getParentCommunity(community); - - return parentCommunity === undefined; - } } diff --git a/src/domain/community/user/dto/user.dto.community.credentials.ts b/src/domain/community/user/dto/user.dto.role.set.credentials.ts similarity index 87% rename from src/domain/community/user/dto/user.dto.community.credentials.ts rename to src/domain/community/user/dto/user.dto.role.set.credentials.ts index 646adb87fe..b2e2fd40b3 100644 --- a/src/domain/community/user/dto/user.dto.community.credentials.ts +++ b/src/domain/community/user/dto/user.dto.role.set.credentials.ts @@ -2,7 +2,7 @@ import { Field, InputType } from '@nestjs/graphql'; import { CredentialDefinition } from '@domain/agent/credential/credential.definition'; @InputType() -export class CommunityCredentials { +export class RoleSetCredentials { @Field() member!: CredentialDefinition; diff --git a/src/domain/community/user/dto/user.dto.community.member.credentials.ts b/src/domain/community/user/dto/user.dto.role.set.member.credentials.ts similarity index 71% rename from src/domain/community/user/dto/user.dto.community.member.credentials.ts rename to src/domain/community/user/dto/user.dto.role.set.member.credentials.ts index 96b3ff16cc..663a04e549 100644 --- a/src/domain/community/user/dto/user.dto.community.member.credentials.ts +++ b/src/domain/community/user/dto/user.dto.role.set.member.credentials.ts @@ -2,10 +2,10 @@ import { Field, InputType } from '@nestjs/graphql'; import { CredentialDefinition } from '@domain/agent/credential/credential.definition'; @InputType() -export class CommunityMemberCredentials { +export class RoleSetMemberCredentials { @Field() member!: CredentialDefinition; @Field({ nullable: true }) - parentCommunityMember?: CredentialDefinition; + parentRoleSetMember?: CredentialDefinition; } diff --git a/src/domain/community/user/user.service.ts b/src/domain/community/user/user.service.ts index 943641637a..87b501f5b2 100644 --- a/src/domain/community/user/user.service.ts +++ b/src/domain/community/user/user.service.ts @@ -45,8 +45,8 @@ import { IPaginatedType } from '@core/pagination/paginated.type'; import { CreateProfileInput } from '@domain/common/profile/dto/profile.dto.create'; import { validateEmail } from '@common/utils'; import { AgentInfoMetadata } from '@core/authentication.agent.info/agent.info.metadata'; -import { CommunityCredentials } from './dto/user.dto.community.credentials'; -import { CommunityMemberCredentials } from './dto/user.dto.community.member.credentials'; +import { RoleSetCredentials } from './dto/user.dto.role.set.credentials'; +import { RoleSetMemberCredentials } from './dto/user.dto.role.set.member.credentials'; import { TagsetReservedName } from '@common/enums/tagset.reserved.name'; import { userDefaults } from './user.defaults'; import { UsersQueryArgs } from './dto/users.query.args'; @@ -590,7 +590,7 @@ export class UserService { } public async getPaginatedAvailableMemberUsers( - communityCredentials: CommunityMemberCredentials, + communityCredentials: RoleSetMemberCredentials, paginationArgs: PaginationArgs, filter?: UserFilterInput ): Promise> { @@ -599,15 +599,15 @@ export class UserService { ); const qb = this.userRepository.createQueryBuilder('user').select(); - if (communityCredentials.parentCommunityMember) { + if (communityCredentials.parentRoleSetMember) { qb.leftJoin('user.agent', 'agent') .leftJoin('agent.credentials', 'credential') .addSelect(['credential.type', 'credential.resourceID']) .where('credential.type = :type') .andWhere('credential.resourceID = :resourceID') .setParameters({ - type: communityCredentials.parentCommunityMember.type, - resourceID: communityCredentials.parentCommunityMember.resourceID, + type: communityCredentials.parentRoleSetMember.type, + resourceID: communityCredentials.parentRoleSetMember.resourceID, }); } @@ -630,12 +630,12 @@ export class UserService { } public async getPaginatedAvailableLeadUsers( - communityCredentials: CommunityCredentials, + roleSetCredentials: RoleSetCredentials, paginationArgs: PaginationArgs, filter?: UserFilterInput ): Promise> { const currentLeadUsers = await this.usersWithCredentials( - communityCredentials.lead + roleSetCredentials.lead ); const qb = this.userRepository .createQueryBuilder('user') @@ -646,8 +646,8 @@ export class UserService { .where('credential.type = :type') .andWhere('credential.resourceID = :resourceID') .setParameters({ - type: communityCredentials.member.type, - resourceID: communityCredentials.member.resourceID, + type: roleSetCredentials.member.type, + resourceID: roleSetCredentials.member.resourceID, }); if (currentLeadUsers.length > 0) { diff --git a/src/domain/space/space/space.resolver.mutations.ts b/src/domain/space/space/space.resolver.mutations.ts index ae2b03e5fa..147d6fc750 100644 --- a/src/domain/space/space/space.resolver.mutations.ts +++ b/src/domain/space/space/space.resolver.mutations.ts @@ -187,13 +187,12 @@ export class SpaceResolverMutations { `subspace create in: ${space.id}` ); - let subspace = await this.spaceService.createSubspace( + const subspace = await this.spaceService.createSubspace( subspaceData, agentInfo ); // Save here so can reuse it later without another load const displayName = subspace.profile.displayName; - subspace = await this.spaceService.save(subspace); const updatedAuthorizations = await this.spaceAuthorizationService.applyAuthorizationPolicy(subspace); diff --git a/src/domain/space/space/space.service.ts b/src/domain/space/space/space.service.ts index a5be739f39..9427a31516 100644 --- a/src/domain/space/space/space.service.ts +++ b/src/domain/space/space/space.service.ts @@ -895,7 +895,9 @@ export class SpaceService { const space = await this.getSpaceOrFail(subspaceData.spaceID, { relations: { storageAggregator: true, - community: true, + community: { + roleSet: true, + }, }, }); @@ -940,6 +942,7 @@ export class SpaceService { subspace = await this.getSpaceOrFail(subspace.id, { relations: { + profile: true, community: { roleSet: true, }, @@ -950,13 +953,14 @@ export class SpaceService { if (agentInfo) { if (!subspace.community || !subspace.community.roleSet) { throw new EntityNotInitializedException( - `unable to load community with role manager: ${subspace.id}`, + `unable to load community with role set: ${subspace.id}`, LogContext.SPACES ); } const roleSet = subspace.community.roleSet; + const parentRoleSet = space.community.roleSet; const agent = await this.agentService.getAgentOrFail(agentInfo?.agentID); - const isMember = await this.roleSetService.isMember(agent, roleSet); + const isMember = await this.roleSetService.isMember(agent, parentRoleSet); if (isMember) { await this.assignUserToRoles(roleSet, agentInfo); } @@ -969,9 +973,14 @@ export class SpaceService { space: ISpace, subspace: ISpace ): Promise { - if (!space.community) { + if ( + !space.community || + !subspace.community || + !space.community.roleSet || + !subspace.community.roleSet + ) { throw new ValidationException( - `Unable to add Subspace to space, missing relations: ${space.id}`, + `Unable to add Subspace to space, missing community or roleset relations: ${space.id}`, LogContext.SPACES ); } @@ -981,7 +990,7 @@ export class SpaceService { subspace.levelZeroSpaceID = space.levelZeroSpaceID; // Finally set the community relationship - subspace.community = await this.setCommunityHierarchyForSubspace( + subspace.community = await this.setRoleSetHierarchyForSubspace( space.community, subspace.community ); @@ -1195,21 +1204,28 @@ export class SpaceService { return this.spaceSettingsService.getSettings(space.settingsStr); } - private async setCommunityHierarchyForSubspace( + private async setRoleSetHierarchyForSubspace( parentCommunity: ICommunity, childCommunity: ICommunity | undefined ): Promise { - if (!childCommunity) { - throw new RelationshipNotFoundException( - `Unable to set subspace community relationship, child community not provied: ${parentCommunity.id}`, - LogContext.SPACES + if ( + !childCommunity || + !childCommunity.roleSet || + !parentCommunity || + !parentCommunity.roleSet + ) { + throw new EntityNotInitializedException( + `Unable to set the parent relationship for rolesets: ${childCommunity?.id} and ${parentCommunity?.id}`, + LogContext.COMMUNITY ); } - // Finally set the community relationship - return await this.communityService.setParentCommunity( - childCommunity, - parentCommunity - ); + + childCommunity.roleSet = + await this.roleSetService.setParentRoleSetAndCredentials( + childCommunity.roleSet, + parentCommunity.roleSet + ); + return childCommunity; } public async updateSettings( diff --git a/src/migrations/1726843779059-roleSet.ts b/src/migrations/1726843779059-roleSet.ts index 76c5411de3..25b3fe263f 100644 --- a/src/migrations/1726843779059-roleSet.ts +++ b/src/migrations/1726843779059-roleSet.ts @@ -219,6 +219,10 @@ export class RoleSet1726843779059 implements MigrationInterface { ); await queryRunner.query(`DROP TABLE \`community_policy\``); + + await queryRunner.query( + `ALTER TABLE \`community\` DROP COLUMN \`parentCommunityId\`` + ); } public async down(queryRunner: QueryRunner): Promise { diff --git a/test/mocks/community.service.mock.ts b/test/mocks/community.service.mock.ts index 6b1843a12e..f48baa377b 100644 --- a/test/mocks/community.service.mock.ts +++ b/test/mocks/community.service.mock.ts @@ -5,7 +5,5 @@ import { PublicPart } from '../utils/public-part'; export const MockCommunityService: ValueProvider> = { provide: CommunityService, - useValue: { - isSpaceCommunity: jest.fn(), - }, + useValue: {}, }; From bd77ca32dbd2702239c7d23f5c69c0cf0eea5334 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Wed, 25 Sep 2024 09:55:14 +0200 Subject: [PATCH 33/78] tidied up authorization between community + roleset --- .../role.set.service.authorization.ts | 53 +--- .../community.service.authorization.ts | 245 +----------------- 2 files changed, 6 insertions(+), 292 deletions(-) diff --git a/src/domain/access/role-set/role.set.service.authorization.ts b/src/domain/access/role-set/role.set.service.authorization.ts index 68d28d9338..7ef1917176 100644 --- a/src/domain/access/role-set/role.set.service.authorization.ts +++ b/src/domain/access/role-set/role.set.service.authorization.ts @@ -10,9 +10,7 @@ import { IAuthorizationPolicy } from '@domain/common/authorization-policy/author import { AuthorizationPolicyRuleVerifiedCredential } from '@core/authorization/authorization.policy.rule.verified.credential'; import { IAuthorizationPolicyRuleCredential } from '@core/authorization/authorization.policy.rule.credential.interface'; import { - CREDENTIAL_RULE_TYPES_COMMUNITY_READ_GLOBAL_REGISTERED, CREDENTIAL_RULE_COMMUNITY_SELF_REMOVAL, - CREDENTIAL_RULE_TYPES_ACCESS_VIRTUAL_CONTRIBUTORS, CREDENTIAL_RULE_TYPES_COMMUNITY_ADD_MEMBERS, CREDENTIAL_RULE_TYPES_COMMUNITY_INVITE_MEMBERS, POLICY_RULE_COMMUNITY_ADD_VC, @@ -27,10 +25,7 @@ import { } from '@common/constants'; import { RelationshipNotFoundException } from '@common/exceptions/relationship.not.found.exception'; import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface'; -import { LicenseEngineService } from '@core/license-engine/license.engine.service'; -import { LicensePrivilege } from '@common/enums/license.privilege'; import { AuthorizationPolicyRulePrivilege } from '@core/authorization/authorization.policy.rule.privilege'; -import { IAgent } from '@domain/agent'; import { PlatformInvitationAuthorizationService } from '@platform/invitation/platform.invitation.service.authorization'; import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; import { EntityNotInitializedException } from '@common/exceptions/entity.not.initialized.exception'; @@ -40,11 +35,11 @@ import { VirtualContributorService } from '@domain/community/virtual-contributor import { CommunityMembershipPolicy } from '@common/enums/community.membership.policy'; import { CommunityRoleType } from '@common/enums/community.role'; import { IRoleSet } from './role.set.interface'; +import { UUID } from '@domain/common/scalars/scalar.uuid'; @Injectable() export class RoleSetAuthorizationService { constructor( - private licenseEngineService: LicenseEngineService, private roleSetService: RoleSetService, private authorizationPolicyService: AuthorizationPolicyService, private applicationAuthorizationService: ApplicationAuthorizationService, @@ -56,7 +51,6 @@ export class RoleSetAuthorizationService { async applyAuthorizationPolicy( roleSetID: string, parentAuthorization: IAuthorizationPolicy, - levelZeroSpaceAgent: IAgent, spaceSettings: ISpaceSettings, spaceMembershipAllowed: boolean, isSubspace: boolean @@ -93,8 +87,6 @@ export class RoleSetAuthorizationService { roleSet.authorization = await this.extendAuthorizationPolicy( roleSet, roleSet.authorization, - parentAuthorization?.anonymousReadAccess, - levelZeroSpaceAgent, spaceSettings ); roleSet.authorization = this.appendVerifiedCredentialRules( @@ -188,7 +180,8 @@ export class RoleSetAuthorizationService { } // Associates of trusted organizations can join - const trustedOrganizationIDs: string[] = []; + const trustedOrganizationIDs: UUID[] = + spaceSettings.membership.trustedOrganizations; for (const trustedOrganizationID of trustedOrganizationIDs) { const hostOrgMembersCanJoin = this.authorizationPolicyService.createCredentialRule( @@ -196,7 +189,7 @@ export class RoleSetAuthorizationService { [ { type: AuthorizationCredential.ORGANIZATION_ASSOCIATE, - resourceID: trustedOrganizationID, + resourceID: JSON.stringify(trustedOrganizationID), }, ], CREDENTIAL_RULE_SPACE_HOST_ASSOCIATES_JOIN @@ -214,8 +207,6 @@ export class RoleSetAuthorizationService { private async extendAuthorizationPolicy( roleSet: IRoleSet, authorization: IAuthorizationPolicy | undefined, - allowGlobalRegisteredReadAccess: boolean | undefined, - levelZeroSpaceAgent: IAgent, spaceSettings: ISpaceSettings ): Promise { const newRules: IAuthorizationPolicyRuleCredential[] = []; @@ -260,42 +251,6 @@ export class RoleSetAuthorizationService { spaceAdminsInvite.cascade = false; newRules.push(spaceAdminsInvite); - if (allowGlobalRegisteredReadAccess) { - const globalRegistered = - this.authorizationPolicyService.createCredentialRuleUsingTypesOnly( - [AuthorizationPrivilege.READ], - [AuthorizationCredential.GLOBAL_REGISTERED], - CREDENTIAL_RULE_TYPES_COMMUNITY_READ_GLOBAL_REGISTERED - ); - newRules.push(globalRegistered); - } - - const accessVirtualContributors = - await this.licenseEngineService.isAccessGranted( - LicensePrivilege.SPACE_VIRTUAL_CONTRIBUTOR_ACCESS, - levelZeroSpaceAgent - ); - if (accessVirtualContributors) { - const criterias: ICredentialDefinition[] = - await this.roleSetService.getCredentialsForRoleWithParents( - roleSet, - CommunityRoleType.ADMIN, - spaceSettings - ); - criterias.push({ - type: AuthorizationCredential.GLOBAL_ADMIN, - resourceID: '', - }); - const accessVCsRule = - this.authorizationPolicyService.createCredentialRule( - [AuthorizationPrivilege.ACCESS_VIRTUAL_CONTRIBUTOR], - criterias, - CREDENTIAL_RULE_TYPES_ACCESS_VIRTUAL_CONTRIBUTORS - ); - accessVCsRule.cascade = true; // TODO: ideally make this not cascade so it is more specific - newRules.push(accessVCsRule); - } - // const updatedAuthorization = this.authorizationPolicyService.appendCredentialAuthorizationRules( diff --git a/src/domain/community/community/community.service.authorization.ts b/src/domain/community/community/community.service.authorization.ts index 1612d761e3..a5f43b6eec 100644 --- a/src/domain/community/community/community.service.authorization.ts +++ b/src/domain/community/community/community.service.authorization.ts @@ -9,21 +9,10 @@ import { AuthorizationPolicyService } from '@domain/common/authorization-policy/ import { IAuthorizationPolicy } from '@domain/common/authorization-policy/authorization.policy.interface'; import { UserGroupAuthorizationService } from '../user-group/user-group.service.authorization'; import { CommunicationAuthorizationService } from '@domain/communication/communication/communication.service.authorization'; -import { AuthorizationPolicyRuleVerifiedCredential } from '@core/authorization/authorization.policy.rule.verified.credential'; import { IAuthorizationPolicyRuleCredential } from '@core/authorization/authorization.policy.rule.credential.interface'; import { - CREDENTIAL_RULE_TYPES_COMMUNITY_READ_GLOBAL_REGISTERED, CREDENTIAL_RULE_TYPES_ACCESS_VIRTUAL_CONTRIBUTORS, - CREDENTIAL_RULE_TYPES_COMMUNITY_ADD_MEMBERS, - CREDENTIAL_RULE_TYPES_COMMUNITY_INVITE_MEMBERS, - POLICY_RULE_COMMUNITY_ADD_VC, - POLICY_RULE_COMMUNITY_INVITE_MEMBER, - CREDENTIAL_RULE_SPACE_HOST_ASSOCIATES_JOIN, - CREDENTIAL_RULE_TYPES_SPACE_COMMUNITY_JOIN_GLOBAL_REGISTERED, - CREDENTIAL_RULE_TYPES_SPACE_COMMUNITY_APPLY_GLOBAL_REGISTERED, - CREDENTIAL_RULE_SUBSPACE_PARENT_MEMBER_APPLY, - CREDENTIAL_RULE_SUBSPACE_PARENT_MEMBER_JOIN, - CREDENTIAL_RULE_COMMUNITY_ADD_MEMBER, + CREDENTIAL_RULE_TYPES_COMMUNITY_READ_GLOBAL_REGISTERED, } from '@common/constants'; import { RelationshipNotFoundException } from '@common/exceptions/relationship.not.found.exception'; import { CommunityGuidelinesAuthorizationService } from '../community-guidelines/community.guidelines.service.authorization'; @@ -31,12 +20,8 @@ import { ICredentialDefinition } from '@domain/agent/credential/credential.defin import { CommunityRoleType } from '@common/enums/community.role'; import { LicenseEngineService } from '@core/license-engine/license.engine.service'; import { LicensePrivilege } from '@common/enums/license.privilege'; -import { AuthorizationPolicyRulePrivilege } from '@core/authorization/authorization.policy.rule.privilege'; import { IAgent } from '@domain/agent'; -import { VirtualContributorService } from '../virtual-contributor/virtual.contributor.service'; import { ISpaceSettings } from '@domain/space/space.settings/space.settings.interface'; -import { CommunityMembershipPolicy } from '@common/enums/community.membership.policy'; -import { EntityNotInitializedException } from '@common/exceptions/entity.not.initialized.exception'; import { RoleSetAuthorizationService } from '@domain/access/role-set/role.set.service.authorization'; import { RoleSetService } from '@domain/access/role-set/role.set.service'; import { IRoleSet } from '@domain/access/role-set'; @@ -49,7 +34,6 @@ export class CommunityAuthorizationService { private authorizationPolicyService: AuthorizationPolicyService, private userGroupAuthorizationService: UserGroupAuthorizationService, private communicationAuthorizationService: CommunicationAuthorizationService, - private virtualContributorService: VirtualContributorService, private roleSetService: RoleSetService, private roleSetAuthorizationService: RoleSetAuthorizationService, private communityGuidelinesAuthorizationService: CommunityGuidelinesAuthorizationService @@ -97,10 +81,6 @@ export class CommunityAuthorizationService { parentAuthorization ); - community.authorization = this.appendPrivilegeRules( - community.authorization - ); - community.authorization = await this.extendAuthorizationPolicy( community.authorization, parentAuthorization?.anonymousReadAccess, @@ -108,23 +88,6 @@ export class CommunityAuthorizationService { community.roleSet, spaceSettings ); - community.authorization = this.appendVerifiedCredentialRules( - community.authorization - ); - if (spaceMembershipAllowed) { - community.authorization = this.extendCommunityAuthorizationPolicySpace( - community.authorization, - community.roleSet, - spaceSettings - ); - } - if (isSubspace) { - community.authorization = await this.extendAuthorizationPolicySubspace( - community.authorization, - community.roleSet, - spaceSettings - ); - } // always false community.authorization.anonymousReadAccess = false; @@ -152,7 +115,6 @@ export class CommunityAuthorizationService { await this.roleSetAuthorizationService.applyAuthorizationPolicy( community.roleSet.id, community.authorization, - levelZeroSpaceAgent, spaceSettings, spaceMembershipAllowed, isSubspace @@ -171,67 +133,6 @@ export class CommunityAuthorizationService { return updatedAuthorizations; } - private extendCommunityAuthorizationPolicySpace( - communityAuthorization: IAuthorizationPolicy | undefined, - roleSet: IRoleSet, - spaceSettings: ISpaceSettings - ): IAuthorizationPolicy { - if (!communityAuthorization) - throw new EntityNotInitializedException( - `Authorization definition not found for: ${JSON.stringify(roleSet)}`, - LogContext.SPACES - ); - - const newRules: IAuthorizationPolicyRuleCredential[] = []; - - const membershipPolicy = spaceSettings.membership.policy; - switch (membershipPolicy) { - case CommunityMembershipPolicy.APPLICATIONS: - const anyUserCanApply = - this.authorizationPolicyService.createCredentialRuleUsingTypesOnly( - [AuthorizationPrivilege.COMMUNITY_APPLY], - [AuthorizationCredential.GLOBAL_REGISTERED], - CREDENTIAL_RULE_TYPES_SPACE_COMMUNITY_APPLY_GLOBAL_REGISTERED - ); - anyUserCanApply.cascade = false; - newRules.push(anyUserCanApply); - break; - case CommunityMembershipPolicy.OPEN: - const anyUserCanJoin = - this.authorizationPolicyService.createCredentialRuleUsingTypesOnly( - [AuthorizationPrivilege.COMMUNITY_JOIN], - [AuthorizationCredential.GLOBAL_REGISTERED], - CREDENTIAL_RULE_TYPES_SPACE_COMMUNITY_JOIN_GLOBAL_REGISTERED - ); - anyUserCanJoin.cascade = false; - newRules.push(anyUserCanJoin); - break; - } - - // Associates of trusted organizations can join - const trustedOrganizationIDs: string[] = []; - for (const trustedOrganizationID of trustedOrganizationIDs) { - const hostOrgMembersCanJoin = - this.authorizationPolicyService.createCredentialRule( - [AuthorizationPrivilege.COMMUNITY_JOIN], - [ - { - type: AuthorizationCredential.ORGANIZATION_ASSOCIATE, - resourceID: trustedOrganizationID, - }, - ], - CREDENTIAL_RULE_SPACE_HOST_ASSOCIATES_JOIN - ); - hostOrgMembersCanJoin.cascade = false; - newRules.push(hostOrgMembersCanJoin); - } - - return this.authorizationPolicyService.appendCredentialAuthorizationRules( - communityAuthorization, - newRules - ); - } - private async extendAuthorizationPolicy( authorization: IAuthorizationPolicy | undefined, allowGlobalRegisteredReadAccess: boolean | undefined, @@ -241,46 +142,6 @@ export class CommunityAuthorizationService { ): Promise { const newRules: IAuthorizationPolicyRuleCredential[] = []; - const globalAdminAddMembers = - this.authorizationPolicyService.createCredentialRuleUsingTypesOnly( - [AuthorizationPrivilege.COMMUNITY_ADD_MEMBER], - [ - AuthorizationCredential.GLOBAL_ADMIN, - AuthorizationCredential.GLOBAL_SUPPORT, - ], - CREDENTIAL_RULE_TYPES_COMMUNITY_ADD_MEMBERS - ); - newRules.push(globalAdminAddMembers); - - const inviteMembersCriterias: ICredentialDefinition[] = - await this.roleSetService.getCredentialsForRoleWithParents( - roleSet, - CommunityRoleType.ADMIN, - spaceSettings - ); - if (spaceSettings.membership.allowSubspaceAdminsToInviteMembers) { - // use the member credential to create subspace admin credential - const subspaceAdminCredential: ICredentialDefinition = - await this.roleSetService.getCredentialForRole( - roleSet, - CommunityRoleType.MEMBER - ); - subspaceAdminCredential.type = - AuthorizationCredential.SPACE_SUBSPACE_ADMIN; - inviteMembersCriterias.push(subspaceAdminCredential); - } - const spaceAdminsInvite = - this.authorizationPolicyService.createCredentialRule( - [ - AuthorizationPrivilege.COMMUNITY_INVITE, - AuthorizationPrivilege.COMMUNITY_ADD_MEMBER_VC_FROM_ACCOUNT, - ], - inviteMembersCriterias, - CREDENTIAL_RULE_TYPES_COMMUNITY_INVITE_MEMBERS - ); - spaceAdminsInvite.cascade = false; - newRules.push(spaceAdminsInvite); - if (allowGlobalRegisteredReadAccess) { const globalRegistered = this.authorizationPolicyService.createCredentialRuleUsingTypesOnly( @@ -288,6 +149,7 @@ export class CommunityAuthorizationService { [AuthorizationCredential.GLOBAL_REGISTERED], CREDENTIAL_RULE_TYPES_COMMUNITY_READ_GLOBAL_REGISTERED ); + globalRegistered.cascade = true; newRules.push(globalRegistered); } @@ -326,107 +188,4 @@ export class CommunityAuthorizationService { return updatedAuthorization; } - - private async extendAuthorizationPolicySubspace( - authorization: IAuthorizationPolicy | undefined, - roleSet: IRoleSet, - spaceSettings: ISpaceSettings - ): Promise { - if (!authorization) - throw new EntityNotInitializedException( - 'Authorization definition not found', - LogContext.SPACES - ); - - const newRules: IAuthorizationPolicyRuleCredential[] = []; - - const parentRoleSetMemberCredential = - await this.roleSetService.getDirectParentCredentialForRole( - roleSet, - CommunityRoleType.MEMBER - ); - - // Allow member of the parent community to Apply - if (parentRoleSetMemberCredential) { - const membershipSettings = spaceSettings.membership; - switch (membershipSettings.policy) { - case CommunityMembershipPolicy.APPLICATIONS: - const spaceMemberCanApply = - this.authorizationPolicyService.createCredentialRule( - [AuthorizationPrivilege.COMMUNITY_APPLY], - [parentRoleSetMemberCredential], - CREDENTIAL_RULE_SUBSPACE_PARENT_MEMBER_APPLY - ); - spaceMemberCanApply.cascade = false; - newRules.push(spaceMemberCanApply); - break; - case CommunityMembershipPolicy.OPEN: - const spaceMemberCanJoin = - this.authorizationPolicyService.createCredentialRule( - [AuthorizationPrivilege.COMMUNITY_JOIN], - [parentRoleSetMemberCredential], - CREDENTIAL_RULE_SUBSPACE_PARENT_MEMBER_JOIN - ); - spaceMemberCanJoin.cascade = false; - newRules.push(spaceMemberCanJoin); - break; - } - } - - const adminCredentials = - await this.roleSetService.getCredentialsForRoleWithParents( - roleSet, - CommunityRoleType.ADMIN, - spaceSettings - ); - - const addMembers = this.authorizationPolicyService.createCredentialRule( - [AuthorizationPrivilege.COMMUNITY_ADD_MEMBER], - adminCredentials, - CREDENTIAL_RULE_COMMUNITY_ADD_MEMBER - ); - addMembers.cascade = false; - newRules.push(addMembers); - - this.authorizationPolicyService.appendCredentialAuthorizationRules( - authorization, - newRules - ); - - return authorization; - } - - private appendVerifiedCredentialRules( - authorization: IAuthorizationPolicy - ): IAuthorizationPolicy { - const verifiedCredentialRules: AuthorizationPolicyRuleVerifiedCredential[] = - []; - - return this.authorizationPolicyService.appendVerifiedCredentialAuthorizationRules( - authorization, - verifiedCredentialRules - ); - } - - private appendPrivilegeRules( - authorization: IAuthorizationPolicy - ): IAuthorizationPolicy { - const createVCPrivilege = new AuthorizationPolicyRulePrivilege( - [AuthorizationPrivilege.COMMUNITY_ADD_MEMBER_VC_FROM_ACCOUNT], - AuthorizationPrivilege.GRANT, - POLICY_RULE_COMMUNITY_ADD_VC - ); - - // If you are able to add a member, then you are also logically able to invite a member - const invitePrivilege = new AuthorizationPolicyRulePrivilege( - [AuthorizationPrivilege.COMMUNITY_INVITE], - AuthorizationPrivilege.COMMUNITY_ADD_MEMBER, - POLICY_RULE_COMMUNITY_INVITE_MEMBER - ); - - return this.authorizationPolicyService.appendPrivilegeAuthorizationRules( - authorization, - [createVCPrivilege, invitePrivilege] - ); - } } From 8e031dae2bc8e3689a1d273d62e18b94af883609 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Wed, 25 Sep 2024 14:45:12 +0200 Subject: [PATCH 34/78] fixed deletion of space --- src/domain/space/space/space.service.ts | 95 +++++++++++-------------- 1 file changed, 40 insertions(+), 55 deletions(-) diff --git a/src/domain/space/space/space.service.ts b/src/domain/space/space/space.service.ts index 9427a31516..47c298b0e7 100644 --- a/src/domain/space/space/space.service.ts +++ b/src/domain/space/space/space.service.ts @@ -310,24 +310,60 @@ export class SpaceService { const space = await this.getSpaceOrFail(deleteData.ID, { relations: { subspaces: true, + collaboration: true, + community: true, + context: true, + agent: true, + profile: true, + storageAggregator: true, + library: true, + defaults: true, }, }); - if (!space.subspaces) { + if ( + !space.subspaces || + !space.collaboration || + !space.community || + !space.context || + !space.agent || + !space.profile || + !space.storageAggregator || + !space.authorization + ) { throw new RelationshipNotFoundException( - `Unable to load all entities for deletion of space ${space.id} `, + `Unable to load entities to delete Space: ${space.id} `, LogContext.SPACES ); } // Do not remove a space that has subspaces, require these to be individually first removed - if (space.subspaces.length > 0) + if (space.subspaces.length > 0) { throw new OperationNotAllowedException( `Unable to remove Space (${space.id}) as it contains ${space.subspaces.length} subspaces`, LogContext.SPACES ); + } - await this.deleteEntities(space.id); + await this.contextService.removeContext(space.context.id); + await this.collaborationService.deleteCollaboration(space.collaboration.id); + await this.communityService.removeCommunity(space.community.id); + await this.profileService.deleteProfile(space.profile.id); + await this.agentService.deleteAgent(space.agent.id); + await this.authorizationPolicyService.delete(space.authorization); + + if (space.level === SpaceLevel.SPACE) { + if (!space.library || !space.defaults) { + throw new RelationshipNotFoundException( + `Unable to load entities to delete base subspace: ${space.id} `, + LogContext.SPACES + ); + } + await this.templatesSetService.deleteTemplatesSet(space.library.id); + await this.spaceDefaultsService.deleteSpaceDefaults(space.defaults.id); + } + + await this.storageAggregatorService.delete(space.storageAggregator.id); const result = await this.spaceRepository.remove(space as Space); result.id = deleteData.ID; @@ -1099,57 +1135,6 @@ export class SpaceService { return await this.save(space); } - private async deleteEntities(spaceID: string) { - const space = await this.getSpaceOrFail(spaceID, { - relations: { - collaboration: true, - community: true, - context: true, - agent: true, - profile: true, - storageAggregator: true, - library: true, - defaults: true, - }, - }); - - if ( - !space.collaboration || - !space.community || - !space.community.roleSet || - !space.context || - !space.agent || - !space.profile || - !space.storageAggregator || - !space.authorization - ) { - throw new RelationshipNotFoundException( - `Unable to load entities to delete base subspace: ${space.id} `, - LogContext.SPACES - ); - } - - await this.contextService.removeContext(space.context.id); - await this.collaborationService.deleteCollaboration(space.collaboration.id); - await this.communityService.removeCommunity(space.community.id); - await this.profileService.deleteProfile(space.profile.id); - await this.agentService.deleteAgent(space.agent.id); - await this.authorizationPolicyService.delete(space.authorization); - - if (space.level === SpaceLevel.SPACE) { - if (!space.library || !space.defaults) { - throw new RelationshipNotFoundException( - `Unable to load entities to delete base subspace: ${space.id} `, - LogContext.SPACES - ); - } - await this.templatesSetService.deleteTemplatesSet(space.library.id); - await this.spaceDefaultsService.deleteSpaceDefaults(space.defaults.id); - } - - await this.storageAggregatorService.delete(space.storageAggregator.id); - } - async getSubspaceInLevelZeroSpace( subspaceID: string, levelZeroSpaceID: string, From f851685837a1f5fabf3046366df08835b85110e3 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Wed, 25 Sep 2024 14:53:54 +0200 Subject: [PATCH 35/78] fix roles test spec --- src/services/api/roles/roles.service.spec.ts | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/services/api/roles/roles.service.spec.ts b/src/services/api/roles/roles.service.spec.ts index 2ef69a6a7e..71d1ff4e12 100644 --- a/src/services/api/roles/roles.service.spec.ts +++ b/src/services/api/roles/roles.service.spec.ts @@ -16,8 +16,7 @@ import { ApplicationService } from '@domain/access/application/application.servi import { OrganizationService } from '@domain/community/organization/organization.service'; import { CommunityService } from '@domain/community/community/community.service'; import { SpaceFilterService } from '@services/infrastructure/space-filter/space.filter.service'; -import { asyncToThrow, testData } from '@test/utils'; -import { RelationshipNotFoundException } from '@common/exceptions'; +import { testData } from '@test/utils'; import { SpaceVisibility } from '@common/enums/space.visibility'; import * as getOrganizationRolesForUserEntityData from './util/get.organization.roles.for.user.entity.data'; import * as getSpaceRolesForContributorQueryResult from './util/get.space.roles.for.contributor.query.result'; @@ -42,7 +41,6 @@ describe('RolesService', () => { let spaceFilterService: SpaceFilterService; let applicationService: ApplicationService; let organizationService: OrganizationService; - let communityService: CommunityService; let communityResolverService: CommunityResolverService; beforeAll(async () => { @@ -131,8 +129,6 @@ describe('RolesService', () => { .spyOn(applicationService, 'getApplicationState') .mockResolvedValue('new'); - jest.spyOn(communityService, 'isSpaceCommunity').mockResolvedValue(true); - jest .spyOn(communityResolverService, 'getSpaceForCommunityOrFail') .mockResolvedValue(testData.space as any); @@ -181,17 +177,6 @@ describe('RolesService', () => { ]) ); }); - - it.skip('Should throw exception when community parent is not found', async () => { - jest - .spyOn(communityService, 'isSpaceCommunity') - .mockResolvedValueOnce(false); - - await asyncToThrow( - rolesService.getCommunityApplicationsForUser(testData.user.id), - RelationshipNotFoundException - ); - }); }); describe('Organization Roles', () => { From 7800a200bfe56857ca6d0f13beb401aebdf55608 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Wed, 25 Sep 2024 14:56:52 +0200 Subject: [PATCH 36/78] additional error fixed --- src/services/api/roles/roles.service.spec.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/services/api/roles/roles.service.spec.ts b/src/services/api/roles/roles.service.spec.ts index 71d1ff4e12..a36036afce 100644 --- a/src/services/api/roles/roles.service.spec.ts +++ b/src/services/api/roles/roles.service.spec.ts @@ -14,7 +14,6 @@ import { RolesService } from './roles.service'; import { UserService } from '@domain/community/user/user.service'; import { ApplicationService } from '@domain/access/application/application.service'; import { OrganizationService } from '@domain/community/organization/organization.service'; -import { CommunityService } from '@domain/community/community/community.service'; import { SpaceFilterService } from '@services/infrastructure/space-filter/space.filter.service'; import { testData } from '@test/utils'; import { SpaceVisibility } from '@common/enums/space.visibility'; @@ -67,7 +66,6 @@ describe('RolesService', () => { userService = moduleRef.get(UserService); applicationService = moduleRef.get(ApplicationService); organizationService = moduleRef.get(OrganizationService); - communityService = moduleRef.get(CommunityService); communityResolverService = moduleRef.get(CommunityResolverService); spaceFilterService = moduleRef.get(SpaceFilterService); }); From 116c0e388c251a59aec8c699ea2e65acf9ef1d2b Mon Sep 17 00:00:00 2001 From: Carlos Cano Date: Wed, 25 Sep 2024 14:56:02 +0200 Subject: [PATCH 37/78] Fix migration foreign key --- src/migrations/1726843779059-roleSet.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/migrations/1726843779059-roleSet.ts b/src/migrations/1726843779059-roleSet.ts index 25b3fe263f..d92c71e245 100644 --- a/src/migrations/1726843779059-roleSet.ts +++ b/src/migrations/1726843779059-roleSet.ts @@ -220,6 +220,9 @@ export class RoleSet1726843779059 implements MigrationInterface { await queryRunner.query(`DROP TABLE \`community_policy\``); + await queryRunner.query( + `ALTER TABLE \`community\` DROP FOREIGN KEY \`FK_8e8283bdacc9e770918fe689333\`` + ); await queryRunner.query( `ALTER TABLE \`community\` DROP COLUMN \`parentCommunityId\`` ); From 7236c1ec30e619ef11f2562dbca35cafb774b7ea Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Wed, 25 Sep 2024 19:26:57 +0200 Subject: [PATCH 38/78] streamline logging of events; additional info when cannot delete space --- src/common/utils/stringify.util.ts | 1 + src/domain/space/space/space.service.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/utils/stringify.util.ts b/src/common/utils/stringify.util.ts index 4dca213b6a..325a7bfaf5 100644 --- a/src/common/utils/stringify.util.ts +++ b/src/common/utils/stringify.util.ts @@ -1,6 +1,7 @@ export function stringifyWithoutAuthorizationMetaInfo(object: any): string { return JSON.stringify(object, (key, value) => { if ( + key === 'credentials' || key === 'authorization' || key === 'createdDate' || key === 'updatedDate' || diff --git a/src/domain/space/space/space.service.ts b/src/domain/space/space/space.service.ts index 47c298b0e7..57a0e48bb9 100644 --- a/src/domain/space/space/space.service.ts +++ b/src/domain/space/space/space.service.ts @@ -340,7 +340,7 @@ export class SpaceService { // Do not remove a space that has subspaces, require these to be individually first removed if (space.subspaces.length > 0) { throw new OperationNotAllowedException( - `Unable to remove Space (${space.id}) as it contains ${space.subspaces.length} subspaces`, + `Unable to remove Space (${space.id}), with level ${space.level}, as it contains ${space.subspaces.length} subspaces`, LogContext.SPACES ); } From 3dc3ebc7735595ffd08933b171d58735b4023848 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Wed, 25 Sep 2024 19:53:23 +0200 Subject: [PATCH 39/78] added DTOs used for roleset to validation --- .../validation/handlers/base/base.handler.ts | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/core/validation/handlers/base/base.handler.ts b/src/core/validation/handlers/base/base.handler.ts index 99a8e7590d..2ed62821f7 100644 --- a/src/core/validation/handlers/base/base.handler.ts +++ b/src/core/validation/handlers/base/base.handler.ts @@ -73,6 +73,14 @@ import { CreateCollaborationOnSpaceInput } from '@domain/space/space/dto/space.d import { InviteNewContributorForRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.platform.invitation.community'; import { ApplyForEntryRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.entry.role.apply'; import { InviteForEntryRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.entry.role.invite'; +import { AssignRoleOnRoleSetToUserInput } from '@domain/access/role-set/dto/role.set.dto.role.assign.user'; +import { AssignRoleOnRoleSetToOrganizationInput } from '@domain/access/role-set/dto/role.set.dto.role.assign.organization'; +import { AssignRoleOnRoleSetToVirtualContributorInput } from '@domain/access/role-set/dto/role.set.dto.role.assign.virtual'; +import { RemoveRoleOnRoleSetFromUserInput } from '@domain/access/role-set/dto/role.set.dto.role.remove.user'; +import { RemoveRoleOnRoleSetFromOrganizationInput } from '@domain/access/role-set/dto/role.set.dto.role.remove.organization'; +import { RemoveRoleOnRoleSetFromVirtualContributorInput } from '@domain/access/role-set/dto/role.set.dto.role.remove.virtual'; +import { UpdateApplicationFormOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.update.application.form'; +import { JoinAsEntryRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.entry.role.join'; export class BaseHandler extends AbstractHandler { public async handle( @@ -80,6 +88,17 @@ export class BaseHandler extends AbstractHandler { metatype: Function ): Promise { const types: Function[] = [ + AssignRoleOnRoleSetToUserInput, + AssignRoleOnRoleSetToOrganizationInput, + AssignRoleOnRoleSetToVirtualContributorInput, + RemoveRoleOnRoleSetFromUserInput, + RemoveRoleOnRoleSetFromOrganizationInput, + RemoveRoleOnRoleSetFromVirtualContributorInput, + UpdateApplicationFormOnRoleSetInput, + JoinAsEntryRoleOnRoleSetInput, + ApplyForEntryRoleOnRoleSetInput, + InviteForEntryRoleOnRoleSetInput, + InviteNewContributorForRoleOnRoleSetInput, ApplicationEventInput, UpdateInnovationFlowInput, RoomSendMessageInput, @@ -128,9 +147,6 @@ export class BaseHandler extends AbstractHandler { UpdateSpaceSettingsEntityInput, UpdateSpaceSettingsInput, VisualUploadImageInput, - ApplyForEntryRoleOnRoleSetInput, - InviteForEntryRoleOnRoleSetInput, - InviteNewContributorForRoleOnRoleSetInput, ForumCreateDiscussionInput, SendMessageOnCalloutInput, CreateCalloutOnCollaborationInput, From 487ebc04eb7e02207677622a2364a894e73df380 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Thu, 26 Sep 2024 07:42:28 +0200 Subject: [PATCH 40/78] updated access logic for RoleSet; split RoleSet field resolver into two, with one for public + one for protected fields --- .../validation/handlers/base/base.handler.ts | 2 + src/domain/access/role-set/role.set.module.ts | 2 + .../role.set.resolver.fields public.ts | 90 +++++++++++++++++++ .../role-set/role.set.resolver.fields.ts | 80 +---------------- .../community/community.resolver.fields.ts | 2 +- .../api/lookup/lookup.resolver.fields.ts | 24 +++-- 6 files changed, 113 insertions(+), 87 deletions(-) create mode 100644 src/domain/access/role-set/role.set.resolver.fields public.ts diff --git a/src/core/validation/handlers/base/base.handler.ts b/src/core/validation/handlers/base/base.handler.ts index 2ed62821f7..37b970c904 100644 --- a/src/core/validation/handlers/base/base.handler.ts +++ b/src/core/validation/handlers/base/base.handler.ts @@ -81,6 +81,7 @@ import { RemoveRoleOnRoleSetFromOrganizationInput } from '@domain/access/role-se import { RemoveRoleOnRoleSetFromVirtualContributorInput } from '@domain/access/role-set/dto/role.set.dto.role.remove.virtual'; import { UpdateApplicationFormOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.update.application.form'; import { JoinAsEntryRoleOnRoleSetInput } from '@domain/access/role-set/dto/role.set.dto.entry.role.join'; +import { RolesUserInput } from '@services/api/roles/dto/roles.dto.input.user'; export class BaseHandler extends AbstractHandler { public async handle( @@ -97,6 +98,7 @@ export class BaseHandler extends AbstractHandler { UpdateApplicationFormOnRoleSetInput, JoinAsEntryRoleOnRoleSetInput, ApplyForEntryRoleOnRoleSetInput, + RolesUserInput, InviteForEntryRoleOnRoleSetInput, InviteNewContributorForRoleOnRoleSetInput, ApplicationEventInput, diff --git a/src/domain/access/role-set/role.set.module.ts b/src/domain/access/role-set/role.set.module.ts index 2970114d8c..04b5afe441 100644 --- a/src/domain/access/role-set/role.set.module.ts +++ b/src/domain/access/role-set/role.set.module.ts @@ -28,6 +28,7 @@ import { ContributionReporterModule } from '@services/external/elasticsearch/con import { ActivityAdapterModule } from '@services/adapters/activity-adapter/activity.adapter.module'; import { LifecycleModule } from '@domain/common/lifecycle/lifecycle.module'; import { CommunityCommunicationModule } from '@domain/community/community-communication/community.communication.module'; +import { RoleSetResolverFieldsPublic } from './role.set.resolver.fields public'; @Module({ imports: [ @@ -58,6 +59,7 @@ import { CommunityCommunicationModule } from '@domain/community/community-commun RoleSetAuthorizationService, RoleSetResolverMutations, RoleSetResolverFields, + RoleSetResolverFieldsPublic, RoleSetEventsService, RoleSetApplicationLifecycleOptionsProvider, RoleSetInvitationLifecycleOptionsProvider, diff --git a/src/domain/access/role-set/role.set.resolver.fields public.ts b/src/domain/access/role-set/role.set.resolver.fields public.ts new file mode 100644 index 0000000000..591b18b720 --- /dev/null +++ b/src/domain/access/role-set/role.set.resolver.fields public.ts @@ -0,0 +1,90 @@ +import { GraphqlGuard } from '@core/authorization'; +import { UseGuards } from '@nestjs/common'; +import { Args, Parent, ResolveField, Resolver } from '@nestjs/graphql'; +import { CurrentUser } from '@src/common/decorators'; +import { RoleSetService } from './role.set.service'; +import { IForm } from '@domain/common/form/form.interface'; +import { IRoleSet } from './role.set.interface'; +import { RoleSet } from './role.set.entity'; +import { CommunityRoleType } from '@common/enums/community.role'; +import { IRole } from '../role/role.interface'; +import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; +import { AgentInfo } from '@core/authentication.agent.info/agent.info'; +import { CommunityRoleImplicit } from '@common/enums/community.role.implicit'; + +// Resolver for fields on RoleSet that are available without READ access +@Resolver(() => IRoleSet) +export class RoleSetResolverFieldsPublic { + constructor(private roleSetService: RoleSetService) {} + + @UseGuards(GraphqlGuard) + @ResolveField('applicationForm', () => IForm, { + nullable: false, + description: 'The Form used for Applications to this roleSet.', + }) + async applicationForm(@Parent() roleSet: RoleSet): Promise { + return await this.roleSetService.getApplicationForm(roleSet); + } + + // Role definitions are not protected + @UseGuards(GraphqlGuard) + @ResolveField('roleDefinitions', () => [IRole], { + nullable: false, + description: 'The Role Definitions included in this roleSet.', + }) + async roleDefinitions(@Parent() roleSet: RoleSet): Promise { + return await this.roleSetService.getRoleDefinitions(roleSet); + } + + // The set of fields from here down are not prote + @UseGuards(GraphqlGuard) + @ResolveField('roleDefinition', () => IRole, { + nullable: false, + description: 'The Role Definitions from this RoleSet to return.', + }) + async roleDefinition( + @Parent() roleSet: RoleSet, + @Args('role', { type: () => CommunityRoleType, nullable: false }) + role: CommunityRoleType + ): Promise { + return await this.roleSetService.getRoleDefinition(roleSet, role); + } + + @UseGuards(GraphqlGuard) + @ResolveField('myMembershipStatus', () => CommunityMembershipStatus, { + nullable: true, + description: 'The membership status of the currently logged in user.', + }) + async myMembershipStatus( + @CurrentUser() agentInfo: AgentInfo, + @Parent() roleSet: IRoleSet + ): Promise { + return this.roleSetService.getMembershipStatus(agentInfo, roleSet); + } + + @UseGuards(GraphqlGuard) + @ResolveField('myRoles', () => [CommunityRoleType], { + nullable: false, + description: + 'The roles on this community for the currently logged in user.', + }) + async myRoles( + @CurrentUser() agentInfo: AgentInfo, + @Parent() roleSet: IRoleSet + ): Promise { + return this.roleSetService.getRolesForAgentInfo(agentInfo, roleSet); + } + + @UseGuards(GraphqlGuard) + @ResolveField('myRolesImplicit', () => [CommunityRoleImplicit], { + nullable: false, + description: + 'The implicit roles on this community for the currently logged in user.', + }) + async myRolesImplicit( + @CurrentUser() agentInfo: AgentInfo, + @Parent() roleSet: IRoleSet + ): Promise { + return this.roleSetService.getCommunityImplicitRoles(agentInfo, roleSet); + } +} diff --git a/src/domain/access/role-set/role.set.resolver.fields.ts b/src/domain/access/role-set/role.set.resolver.fields.ts index 3e69a971a8..d650ad9e49 100644 --- a/src/domain/access/role-set/role.set.resolver.fields.ts +++ b/src/domain/access/role-set/role.set.resolver.fields.ts @@ -1,14 +1,9 @@ import { GraphqlGuard } from '@core/authorization'; import { UseGuards } from '@nestjs/common'; import { Args, Float, Parent, ResolveField, Resolver } from '@nestjs/graphql'; -import { - AuthorizationAgentPrivilege, - CurrentUser, -} from '@src/common/decorators'; +import { AuthorizationAgentPrivilege } from '@src/common/decorators'; import { RoleSetService } from './role.set.service'; -import { IForm } from '@domain/common/form/form.interface'; import { IRoleSet } from './role.set.interface'; -import { RoleSet } from './role.set.entity'; import { IApplication } from '../application/application.interface'; import { AuthorizationPrivilege } from '@common/enums/authorization.privilege'; import { UserFilterInput } from '@core/filtering/input-types/user.filter.input'; @@ -22,10 +17,6 @@ import { IOrganization } from '@domain/community/organization/organization.inter import { IVirtualContributor } from '@domain/community/virtual-contributor/virtual.contributor.interface'; import { IInvitation } from '../invitation/invitation.interface'; import { IPlatformInvitation } from '@platform/invitation/platform.invitation.interface'; -import { IRole } from '../role/role.interface'; -import { CommunityMembershipStatus } from '@common/enums/community.membership.status'; -import { AgentInfo } from '@core/authentication.agent.info/agent.info'; -import { CommunityRoleImplicit } from '@common/enums/community.role.implicit'; import { RoleSetMemberCredentials } from '@domain/community/user/dto/user.dto.role.set.member.credentials'; @Resolver(() => IRoleSet) @@ -203,73 +194,4 @@ export class RoleSetResolverFields { const apps = await this.roleSetService.getApplications(roleSet); return apps || []; } - - @UseGuards(GraphqlGuard) - @ResolveField('applicationForm', () => IForm, { - nullable: false, - description: 'The Form used for Applications to this roleSet.', - }) - async applicationForm(@Parent() roleSet: RoleSet): Promise { - return await this.roleSetService.getApplicationForm(roleSet); - } - - @UseGuards(GraphqlGuard) - @ResolveField('roleDefinitions', () => [IRole], { - nullable: false, - description: 'The Role Definitions included in this roleSet.', - }) - async roleDefinitions(@Parent() roleSet: RoleSet): Promise { - return await this.roleSetService.getRoleDefinitions(roleSet); - } - - @UseGuards(GraphqlGuard) - @ResolveField('roleDefinition', () => IRole, { - nullable: false, - description: 'The Role Definitions from this RoleSet to return.', - }) - async roleDefinition( - @Parent() roleSet: RoleSet, - @Args('role', { type: () => CommunityRoleType, nullable: false }) - role: CommunityRoleType - ): Promise { - return await this.roleSetService.getRoleDefinition(roleSet, role); - } - - @UseGuards(GraphqlGuard) - @ResolveField('myMembershipStatus', () => CommunityMembershipStatus, { - nullable: true, - description: 'The membership status of the currently logged in user.', - }) - async myMembershipStatus( - @CurrentUser() agentInfo: AgentInfo, - @Parent() roleSet: IRoleSet - ): Promise { - return this.roleSetService.getMembershipStatus(agentInfo, roleSet); - } - - @UseGuards(GraphqlGuard) - @ResolveField('myRoles', () => [CommunityRoleType], { - nullable: false, - description: - 'The roles on this community for the currently logged in user.', - }) - async myRoles( - @CurrentUser() agentInfo: AgentInfo, - @Parent() roleSet: IRoleSet - ): Promise { - return this.roleSetService.getRolesForAgentInfo(agentInfo, roleSet); - } - - @UseGuards(GraphqlGuard) - @ResolveField('myRolesImplicit', () => [CommunityRoleImplicit], { - nullable: false, - description: - 'The implicit roles on this community for the currently logged in user.', - }) - async myRolesImplicit( - @CurrentUser() agentInfo: AgentInfo, - @Parent() roleSet: IRoleSet - ): Promise { - return this.roleSetService.getCommunityImplicitRoles(agentInfo, roleSet); - } } diff --git a/src/domain/community/community/community.resolver.fields.ts b/src/domain/community/community/community.resolver.fields.ts index aaf6b7e215..2a528f6de9 100644 --- a/src/domain/community/community/community.resolver.fields.ts +++ b/src/domain/community/community/community.resolver.fields.ts @@ -51,7 +51,7 @@ export class CommunityResolverFields { }); } - @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) + // Note: do not check for READ so that it is accessible to check for authorization @UseGuards(GraphqlGuard) @ResolveField('roleSet', () => IRoleSet, { nullable: false, diff --git a/src/services/api/lookup/lookup.resolver.fields.ts b/src/services/api/lookup/lookup.resolver.fields.ts index f88ef41c4b..41198991c9 100644 --- a/src/services/api/lookup/lookup.resolver.fields.ts +++ b/src/services/api/lookup/lookup.resolver.fields.ts @@ -148,12 +148,13 @@ export class LookupResolverFields { @Args('ID', { type: () => UUID }) id: string ): Promise { const roleSet = await this.roleSetService.getRoleSetOrFail(id); - this.authorizationService.grantAccessOrFail( - agentInfo, - roleSet.authorization, - AuthorizationPrivilege.READ, - `lookup RoleSet: ${roleSet.id}` - ); + // Note: RoleSet is publicly accessible for information such as RoleDefinitions, so do not check for READ access here + // this.authorizationService.grantAccessOrFail( + // agentInfo, + // roleSet.authorization, + // AuthorizationPrivilege.READ, + // `lookup RoleSet: ${roleSet.id}` + // ); return roleSet; } @@ -184,9 +185,18 @@ export class LookupResolverFields { description: 'A particular VirtualContributor', }) async virtualContributor( + @CurrentUser() agentInfo: AgentInfo, @Args('ID', { type: () => UUID, nullable: false }) id: string ): Promise { - return await this.virtualContributorService.getVirtualContributorOrFail(id); + const virtualContributor = + await this.virtualContributorService.getVirtualContributorOrFail(id); + this.authorizationService.grantAccessOrFail( + agentInfo, + virtualContributor.authorization, + AuthorizationPrivilege.READ, + `lookup VirtualContributor: ${virtualContributor.id}` + ); + return virtualContributor; } @UseGuards(GraphqlGuard) From 20fcf54b0781d89971373eebe4bdd92966e0c5d5 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Thu, 26 Sep 2024 08:09:57 +0200 Subject: [PATCH 41/78] fix test import --- test/mocks/invitation.service.mock.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/mocks/invitation.service.mock.ts b/test/mocks/invitation.service.mock.ts index 2f8969d3d1..aac30d1e60 100644 --- a/test/mocks/invitation.service.mock.ts +++ b/test/mocks/invitation.service.mock.ts @@ -1,6 +1,6 @@ import { ValueProvider } from '@nestjs/common'; import { PublicPart } from '../utils/public-part'; -import { InvitationService } from '@domain/community/invitation/invitation.service'; +import { InvitationService } from '@domain/access/invitation/invitation.service'; export const MockInvitationService: ValueProvider< PublicPart From 06e4e196f80d4afdfc43d433435bffbf88663856 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Thu, 26 Sep 2024 08:41:43 +0200 Subject: [PATCH 42/78] tidy up error messages --- .../application/application.resolver.mutations.ts | 2 +- .../access/role-set/role.set.resolver.mutations.ts | 6 +++--- src/domain/access/role-set/role.set.service.ts | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/domain/access/application/application.resolver.mutations.ts b/src/domain/access/application/application.resolver.mutations.ts index 2135f8167e..ffdfb433f6 100644 --- a/src/domain/access/application/application.resolver.mutations.ts +++ b/src/domain/access/application/application.resolver.mutations.ts @@ -31,7 +31,7 @@ export class ApplicationResolverMutations { agentInfo, application.authorization, AuthorizationPrivilege.DELETE, - `delete application community: ${application.id}` + `delete application on RoleSet: ${application.id}` ); return await this.applicationService.deleteApplication(deleteData); } diff --git a/src/domain/access/role-set/role.set.resolver.mutations.ts b/src/domain/access/role-set/role.set.resolver.mutations.ts index 4f64034350..475fcc1c61 100644 --- a/src/domain/access/role-set/role.set.resolver.mutations.ts +++ b/src/domain/access/role-set/role.set.resolver.mutations.ts @@ -135,7 +135,7 @@ export class RoleSetResolverMutations { agentInfo, roleSet.authorization, AuthorizationPrivilege.GRANT, - `assign organization community role: ${roleSet.id}` + `assign organization RoleSet role: ${roleSet.id}` ); return await this.roleSetService.assignOrganizationToRole( roleSet, @@ -368,7 +368,7 @@ export class RoleSetResolverMutations { agentInfo, roleSet.authorization, AuthorizationPrivilege.COMMUNITY_APPLY, - `create application community: ${roleSet.id}` + `create application RoleSet: ${roleSet.id}` ); if (roleSet.parentRoleSet) { @@ -447,7 +447,7 @@ export class RoleSetResolverMutations { agentInfo, roleSet.authorization, AuthorizationPrivilege.COMMUNITY_INVITE, - `create invitation community: ${roleSet.id}` + `create invitation RoleSet: ${roleSet.id}` ); const contributors: IContributor[] = []; diff --git a/src/domain/access/role-set/role.set.service.ts b/src/domain/access/role-set/role.set.service.ts index 3337e813cb..48f371603c 100644 --- a/src/domain/access/role-set/role.set.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -219,7 +219,7 @@ export class RoleSetService { return applicationForm; } - // Update the Community policy to have the right resource ID + // Update the RoleSet policy to have the right resource ID public async updateRoleResourceID( roleSet: IRoleSet, resourceID: string @@ -1071,7 +1071,7 @@ export class RoleSetService { const openApplication = await this.findOpenApplication(user.id, roleSet.id); if (openApplication) { throw new RoleSetMembershipException( - `Application not possible: An open application (ID: ${openApplication.id}) already exists for contributor ${openApplication.user?.id} on Community: ${roleSet.id}.`, + `Application not possible: An open application (ID: ${openApplication.id}) already exists for contributor ${openApplication.user?.id} on RoleSet: ${roleSet.id}.`, LogContext.COMMUNITY ); } @@ -1079,7 +1079,7 @@ export class RoleSetService { const openInvitation = await this.findOpenInvitation(user.id, roleSet.id); if (openInvitation) { throw new RoleSetMembershipException( - `Application not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on Community: ${roleSet.id}.`, + `Application not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on RoleSet: ${roleSet.id}.`, LogContext.COMMUNITY ); } @@ -1088,7 +1088,7 @@ export class RoleSetService { const isExistingMember = await this.isMember(agent, roleSet); if (isExistingMember) throw new RoleSetMembershipException( - `Application not possible: Contributor ${user.id} is already a member of the Community: ${roleSet.id}.`, + `Application not possible: Contributor ${user.id} is already a member of the RoleSet: ${roleSet.id}.`, LogContext.COMMUNITY ); } @@ -1104,7 +1104,7 @@ export class RoleSetService { ); if (openInvitation) { throw new RoleSetMembershipException( - `Invitation not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on Community: ${roleSet.id}.`, + `Invitation not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on RoleSet: ${roleSet.id}.`, LogContext.COMMUNITY ); } @@ -1115,7 +1115,7 @@ export class RoleSetService { ); if (openApplication) { throw new RoleSetMembershipException( - `Invitation not possible: An open application (ID: ${openApplication.id}) already exists for contributor ${openApplication.user?.id} on Community: ${roleSet.id}.`, + `Invitation not possible: An open application (ID: ${openApplication.id}) already exists for contributor ${openApplication.user?.id} on RoleSet: ${roleSet.id}.`, LogContext.COMMUNITY ); } @@ -1124,7 +1124,7 @@ export class RoleSetService { const isExistingMember = await this.isMember(agent, roleSet); if (isExistingMember) throw new RoleSetMembershipException( - `Invitation not possible: Contributor ${contributor.id} is already a member of the Community: ${roleSet.id}.`, + `Invitation not possible: Contributor ${contributor.id} is already a member of the RoleSet: ${roleSet.id}.`, LogContext.COMMUNITY ); } From fe801b756c9b1386669c9e595d321f2cb06b4883 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Thu, 26 Sep 2024 09:24:55 +0200 Subject: [PATCH 43/78] fixed auth reset on RoleSet --- .../access/role-set/role.set.service.authorization.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/domain/access/role-set/role.set.service.authorization.ts b/src/domain/access/role-set/role.set.service.authorization.ts index 7ef1917176..5b19bc2654 100644 --- a/src/domain/access/role-set/role.set.service.authorization.ts +++ b/src/domain/access/role-set/role.set.service.authorization.ts @@ -113,21 +113,21 @@ export class RoleSetAuthorizationService { updatedAuthorizations.push(roleSet.authorization); for (const application of roleSet.applications) { - const applicationAuthReset = + const applicationAuth = await this.applicationAuthorizationService.applyAuthorizationPolicy( application, roleSet.authorization ); - application.authorization = applicationAuthReset; + updatedAuthorizations.push(applicationAuth); } for (const invitation of roleSet.invitations) { - const invitationReset = + const invitationAuth = await this.invitationAuthorizationService.applyAuthorizationPolicy( invitation, roleSet.authorization ); - invitation.authorization = invitationReset; + updatedAuthorizations.push(invitationAuth); } for (const externalInvitation of roleSet.platformInvitations) { From 4b7c2f738575cb1fc63988f8848a4e1fa2fdfca7 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Fri, 27 Sep 2024 07:45:45 +0200 Subject: [PATCH 44/78] fix error messages; updated error messages --- src/core/authorization/authorization.service.ts | 6 +++--- src/domain/access/role-set/role.set.resolver.mutations.ts | 4 ++-- src/domain/community/community/community.service.ts | 2 +- src/domain/space/space/space.service.ts | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/authorization/authorization.service.ts b/src/core/authorization/authorization.service.ts index 328ec417c2..402d67f4d4 100644 --- a/src/core/authorization/authorization.service.ts +++ b/src/core/authorization/authorization.service.ts @@ -33,7 +33,7 @@ export class AuthorizationService { if (this.isAccessGranted(agentInfo, auth, privilegeRequired)) return true; - const errorMsg = `Authorization: unable to grant '${privilegeRequired}' privilege: ${msg} user: ${agentInfo.userID} on authorization of type '${auth.type}'`; + const errorMsg = `Authorization: unable to grant '${privilegeRequired}' privilege: ${msg} user: ${agentInfo.userID} on authorization ${auth.id} of type '${auth.type}'`; this.logCredentialCheckFailDetails(errorMsg, agentInfo, auth); // If you get to here then no match was found throw new ForbiddenAuthorizationPolicyException( @@ -130,7 +130,7 @@ export class AuthorizationService { for (const privilege of rule.grantedPrivileges) { if (privilege === privilegeRequired) { this.logger.verbose?.( - `[CredentialRule] Granted privilege '${privilegeRequired}' using rule '${rule.name}'`, + `[CredentialRule] Granted privilege '${privilegeRequired}' using rule '${rule.name}' on authorization ${authorization.id}`, LogContext.AUTH_POLICY ); return true; @@ -172,7 +172,7 @@ export class AuthorizationService { if (grantedPrivileges.includes(rule.sourcePrivilege)) { if (rule.grantedPrivileges.includes(privilegeRequired)) { this.logger.verbose?.( - `[PrivilegeRule] Granted privilege '${privilegeRequired}' using privilege rule '${rule.name}'`, + `[PrivilegeRule] Granted privilege '${privilegeRequired}' using privilege rule '${rule.name}' on authorization ${authorization.id}`, LogContext.AUTH_POLICY ); return true; diff --git a/src/domain/access/role-set/role.set.resolver.mutations.ts b/src/domain/access/role-set/role.set.resolver.mutations.ts index 475fcc1c61..527f06f828 100644 --- a/src/domain/access/role-set/role.set.resolver.mutations.ts +++ b/src/domain/access/role-set/role.set.resolver.mutations.ts @@ -399,12 +399,12 @@ export class RoleSetResolverMutations { const community = await this.communityResolverService.getCommunityForRoleSet(roleSet.id); - const authorization = + application.authorization = await this.applicationAuthorizationService.applyAuthorizationPolicy( application, roleSet.authorization ); - await this.authorizationPolicyService.save(authorization); + await this.authorizationPolicyService.save(application.authorization); // Send the notification const notificationInput: NotificationInputCommunityApplication = { diff --git a/src/domain/community/community/community.service.ts b/src/domain/community/community/community.service.ts index 1086d9ecfe..039c4bcd64 100644 --- a/src/domain/community/community/community.service.ts +++ b/src/domain/community/community/community.service.ts @@ -219,7 +219,7 @@ export class CommunityService { if (!communityWithRoleSet.roleSet) { throw new EntityNotInitializedException( - `Unable to locate Role Manager for community: ${community.id}`, + `Unable to locate RoleSet for community: ${community.id}`, LogContext.COMMUNITY ); } diff --git a/src/domain/space/space/space.service.ts b/src/domain/space/space/space.service.ts index 57a0e48bb9..bbc53c926c 100644 --- a/src/domain/space/space/space.service.ts +++ b/src/domain/space/space/space.service.ts @@ -252,7 +252,7 @@ export class SpaceService { // set immediate community parent + resourceID if (!space.community || !space.community.roleSet) { throw new RelationshipNotFoundException( - `Unable to load community with role manager: ${space.id}`, + `Unable to load Community with RoleSet: ${space.id}`, LogContext.SPACES ); } From 3eccb5d917c1913df484db11a95726e9f00edd80 Mon Sep 17 00:00:00 2001 From: Carlos Cano Date: Fri, 27 Sep 2024 18:23:20 +0200 Subject: [PATCH 45/78] Fix something about whiteboard templates --- src/domain/template/template/template.service.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/domain/template/template/template.service.ts b/src/domain/template/template/template.service.ts index 8656cb665d..551bf1060c 100644 --- a/src/domain/template/template/template.service.ts +++ b/src/domain/template/template/template.service.ts @@ -250,6 +250,13 @@ export class TemplateService { template.whiteboard, templateData.whiteboard ); + if (templateData.whiteboard.content) { + template.whiteboard = + await this.whiteboardService.updateWhiteboardContent( + template.whiteboard.id, + templateData.whiteboard.content + ); + } } return await this.templateRepository.save(template); From c8cfe1b674cd967de9836f9cc84156ce0a453c17 Mon Sep 17 00:00:00 2001 From: Carlos Cano Date: Fri, 27 Sep 2024 18:23:47 +0200 Subject: [PATCH 46/78] Fix communityId instead of roleSetId --- src/domain/communication/room/room.service.mentions.ts | 2 +- .../entity-resolver/community.resolver.service.ts | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/domain/communication/room/room.service.mentions.ts b/src/domain/communication/room/room.service.mentions.ts index 9fd073f4b4..113205b303 100644 --- a/src/domain/communication/room/room.service.mentions.ts +++ b/src/domain/communication/room/room.service.mentions.ts @@ -51,7 +51,7 @@ export class RoomServiceMentions { // The ID of the actual community where the question is being asked const space = await this.communityResolverService.getSpaceForRoleSetOrFail( - community.id + community.roleSet.id ); return space.id; } diff --git a/src/services/infrastructure/entity-resolver/community.resolver.service.ts b/src/services/infrastructure/entity-resolver/community.resolver.service.ts index 125a8e6fff..95433376bb 100644 --- a/src/services/infrastructure/entity-resolver/community.resolver.service.ts +++ b/src/services/infrastructure/entity-resolver/community.resolver.service.ts @@ -398,7 +398,9 @@ export class CommunityResolverService { }, }, relations: { - community: true, + community: { + roleSet: true, + }, }, }); if (!space || !space.community) { @@ -428,7 +430,9 @@ export class CommunityResolverService { }, }, relations: { - community: true, + community: { + roleSet: true, + }, }, }); if (!space || !space.community) { From 09a8424d2f99ed5608e7c02cfa1be767a52f2882 Mon Sep 17 00:00:00 2001 From: Svetoslav Petkov Date: Mon, 30 Sep 2024 17:15:22 +0300 Subject: [PATCH 47/78] latest images (#4574) --- quickstart-services.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickstart-services.yml b/quickstart-services.yml index 32d5e0ef9c..a7c8dba980 100644 --- a/quickstart-services.yml +++ b/quickstart-services.yml @@ -280,7 +280,7 @@ services: whiteboard-collaboration: container_name: alkemio_dev_whiteboard_collaboration hostname: whiteboard-collaboration - image: alkemio/whiteboard-collaboration-service:v0.3.1 + image: alkemio/whiteboard-collaboration-service:v0.4.0 platform: linux/x86_64 depends_on: - rabbitmq From 74c8c2a976a436b94dfedd4be09101ef7dbdab7d Mon Sep 17 00:00:00 2001 From: Carlos Cano Date: Tue, 1 Oct 2024 08:28:23 +0200 Subject: [PATCH 48/78] fix build --- test/mocks/community.resolver.service.mock.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/mocks/community.resolver.service.mock.ts b/test/mocks/community.resolver.service.mock.ts index 99583272f0..f46a74f37d 100644 --- a/test/mocks/community.resolver.service.mock.ts +++ b/test/mocks/community.resolver.service.mock.ts @@ -5,7 +5,8 @@ export const MockCommunityResolverService: MockValueProvider Date: Tue, 1 Oct 2024 11:13:47 +0200 Subject: [PATCH 49/78] Fix infinite recursion call --- src/domain/access/role-set/role.set.service.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/domain/access/role-set/role.set.service.ts b/src/domain/access/role-set/role.set.service.ts index 48f371603c..54212e921e 100644 --- a/src/domain/access/role-set/role.set.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -751,8 +751,11 @@ export class RoleSetService { roleSetID: string, virtualContributorID: string ): Promise { - return await this.isCommunityAccountMatchingVcAccount( - roleSetID, + const community = + await this.communityResolverService.getCommunityForRoleSet(roleSetID); + + return await this.communityResolverService.isCommunityAccountMatchingVcAccount( + community.id, virtualContributorID ); } From 627a3458ef94e47dbf1e483d3afdd312149952df Mon Sep 17 00:00:00 2001 From: Carlos Cano Date: Tue, 1 Oct 2024 12:54:32 +0200 Subject: [PATCH 50/78] Fix roleSet authorization empty credentialsRules issue --- src/domain/space/space/space.service.authorization.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domain/space/space/space.service.authorization.ts b/src/domain/space/space/space.service.authorization.ts index 709b6b007d..7674281672 100644 --- a/src/domain/space/space/space.service.authorization.ts +++ b/src/domain/space/space/space.service.authorization.ts @@ -215,7 +215,7 @@ export class SpaceAuthorizationService { for (const subspace of space.subspaces) { const updatedSubspaceAuthorizations = await this.applyAuthorizationPolicy(subspace); - updatedSubspaceAuthorizations.push(...updatedSubspaceAuthorizations); + updatedAuthorizations.push(...updatedSubspaceAuthorizations); } return updatedAuthorizations; From c3bf1803c520431dc3baa481b2fe5531def819a8 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Tue, 1 Oct 2024 16:00:10 +0200 Subject: [PATCH 51/78] fixed cascade of auth reset to innovation flow in template --- src/core/bootstrap/bootstrap.service.ts | 39 +++++++++++++++++++ .../template.service.authorization.ts | 20 +++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/core/bootstrap/bootstrap.service.ts b/src/core/bootstrap/bootstrap.service.ts index 084bedf3a7..6b251b481c 100644 --- a/src/core/bootstrap/bootstrap.service.ts +++ b/src/core/bootstrap/bootstrap.service.ts @@ -35,6 +35,10 @@ import { AiServerService } from '@services/ai-server/ai-server/ai.server.service import { Space } from '@domain/space/space/space.entity'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { IUser } from '@domain/community/user/user.interface'; +import { TemplatesManagerService } from '@domain/template/templates-manager/templates.manager.service'; +import { TemplateDefaultType } from '@common/enums/template.default.type'; +import { TemplatesSetService } from '@domain/template/templates-set/templates.set.service'; +import { TemplateDefaultService } from '@domain/template/template-default/template.default.service'; @Injectable() export class BootstrapService { @@ -55,6 +59,9 @@ export class BootstrapService { private platformService: PlatformService, private platformAuthorizationService: PlatformAuthorizationService, private authorizationPolicyService: AuthorizationPolicyService, + private templatesManagerService: TemplatesManagerService, + private templatesSetService: TemplatesSetService, + private templateDefaultService: TemplateDefaultService, @InjectRepository(Space) private spaceRepository: Repository, @Inject(WINSTON_MODULE_NEST_PROVIDER) @@ -78,6 +85,7 @@ export class BootstrapService { } await this.bootstrapUserProfiles(); + await this.ensurePlatformTemplatesArePresent(); await this.ensureOrganizationSingleton(); await this.ensureSpaceSingleton(); await this.ensureSsiPopulated(); @@ -95,6 +103,37 @@ export class BootstrapService { } } + private async ensurePlatformTemplatesArePresent() { + const templatesManager = + await this.platformService.getTemplatesManagerOrFail(); + const templateDefaults = + await this.templatesManagerService.getTemplateDefaults( + templatesManager.id + ); + const templatesSet = + await this.templatesManagerService.getTemplatesSetOrFail( + templatesManager.id + ); + + const knowledgeTemmplate = templateDefaults.find( + td => td.type === TemplateDefaultType.COLLABORATION_KNOWLEDGE + ); + if (!knowledgeTemmplate) { + throw new BootstrapException( + `Unable to load Template Default for ${TemplateDefaultType.COLLABORATION_KNOWLEDGE}` + ); + } + if (!knowledgeTemmplate.template) { + // No template set, so create one and then set it + const template = await this.templatesSetService.createTemplate( + templatesSet, + {} + ); + knowledgeTemmplate.template = template; + await this.templateDefaultService.save(knowledgeTemmplate); + } + } + async bootstrapUserProfiles() { const bootstrapAuthorizationEnabled = this.configService.get( 'bootstrap.authorization.enabled', diff --git a/src/domain/template/template/template.service.authorization.ts b/src/domain/template/template/template.service.authorization.ts index 113bbd7834..7d2a2683b1 100644 --- a/src/domain/template/template/template.service.authorization.ts +++ b/src/domain/template/template/template.service.authorization.ts @@ -11,6 +11,7 @@ import { LogContext } from '@common/enums/logging.context'; import { CalloutAuthorizationService } from '@domain/collaboration/callout/callout.service.authorization'; import { WhiteboardAuthorizationService } from '@domain/common/whiteboard/whiteboard.service.authorization'; import { CollaborationAuthorizationService } from '@domain/collaboration/collaboration/collaboration.service.authorization'; +import { InnovationFlowAuthorizationService } from '@domain/collaboration/innovation-flow/innovation.flow.service.authorization'; @Injectable() export class TemplateAuthorizationService { @@ -21,7 +22,8 @@ export class TemplateAuthorizationService { private communityGuidelinesAuthorizationService: CommunityGuidelinesAuthorizationService, private calloutAuthorizationService: CalloutAuthorizationService, private whiteboardAuthorizationService: WhiteboardAuthorizationService, - private collaborationAuthorizationService: CollaborationAuthorizationService + private collaborationAuthorizationService: CollaborationAuthorizationService, + private innovationFlowAuthorizationService: InnovationFlowAuthorizationService ) {} async applyAuthorizationPolicy( @@ -142,6 +144,22 @@ export class TemplateAuthorizationService { updatedAuthorizations.push(...collaborationAuthorizations); } + if (template.type == TemplateType.INNOVATION_FLOW) { + if (!template.innovationFlow) { + throw new RelationshipNotFoundException( + `Unable to load Collaboration on Template of that type: ${template.id} `, + LogContext.TEMPLATES + ); + } + // Cascade + const innovationFlowAuthorizations = + await this.innovationFlowAuthorizationService.applyAuthorizationPolicy( + template.innovationFlow, + template.authorization + ); + updatedAuthorizations.push(...innovationFlowAuthorizations); + } + return updatedAuthorizations; } } From ac45ba8e029ddc9c4af1d8f324206225024212be Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Tue, 1 Oct 2024 16:22:06 +0200 Subject: [PATCH 52/78] removed code that can included by mistake --- src/core/bootstrap/bootstrap.service.ts | 39 ------------------------- 1 file changed, 39 deletions(-) diff --git a/src/core/bootstrap/bootstrap.service.ts b/src/core/bootstrap/bootstrap.service.ts index 6b251b481c..084bedf3a7 100644 --- a/src/core/bootstrap/bootstrap.service.ts +++ b/src/core/bootstrap/bootstrap.service.ts @@ -35,10 +35,6 @@ import { AiServerService } from '@services/ai-server/ai-server/ai.server.service import { Space } from '@domain/space/space/space.entity'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { IUser } from '@domain/community/user/user.interface'; -import { TemplatesManagerService } from '@domain/template/templates-manager/templates.manager.service'; -import { TemplateDefaultType } from '@common/enums/template.default.type'; -import { TemplatesSetService } from '@domain/template/templates-set/templates.set.service'; -import { TemplateDefaultService } from '@domain/template/template-default/template.default.service'; @Injectable() export class BootstrapService { @@ -59,9 +55,6 @@ export class BootstrapService { private platformService: PlatformService, private platformAuthorizationService: PlatformAuthorizationService, private authorizationPolicyService: AuthorizationPolicyService, - private templatesManagerService: TemplatesManagerService, - private templatesSetService: TemplatesSetService, - private templateDefaultService: TemplateDefaultService, @InjectRepository(Space) private spaceRepository: Repository, @Inject(WINSTON_MODULE_NEST_PROVIDER) @@ -85,7 +78,6 @@ export class BootstrapService { } await this.bootstrapUserProfiles(); - await this.ensurePlatformTemplatesArePresent(); await this.ensureOrganizationSingleton(); await this.ensureSpaceSingleton(); await this.ensureSsiPopulated(); @@ -103,37 +95,6 @@ export class BootstrapService { } } - private async ensurePlatformTemplatesArePresent() { - const templatesManager = - await this.platformService.getTemplatesManagerOrFail(); - const templateDefaults = - await this.templatesManagerService.getTemplateDefaults( - templatesManager.id - ); - const templatesSet = - await this.templatesManagerService.getTemplatesSetOrFail( - templatesManager.id - ); - - const knowledgeTemmplate = templateDefaults.find( - td => td.type === TemplateDefaultType.COLLABORATION_KNOWLEDGE - ); - if (!knowledgeTemmplate) { - throw new BootstrapException( - `Unable to load Template Default for ${TemplateDefaultType.COLLABORATION_KNOWLEDGE}` - ); - } - if (!knowledgeTemmplate.template) { - // No template set, so create one and then set it - const template = await this.templatesSetService.createTemplate( - templatesSet, - {} - ); - knowledgeTemmplate.template = template; - await this.templateDefaultService.save(knowledgeTemmplate); - } - } - async bootstrapUserProfiles() { const bootstrapAuthorizationEnabled = this.configService.get( 'bootstrap.authorization.enabled', From 4a56d61b01ace17c38b354b6c9091732b4e073e2 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Tue, 1 Oct 2024 16:23:17 +0200 Subject: [PATCH 53/78] fixed typo --- src/domain/template/template/template.service.authorization.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domain/template/template/template.service.authorization.ts b/src/domain/template/template/template.service.authorization.ts index 7d2a2683b1..c8c7c14b99 100644 --- a/src/domain/template/template/template.service.authorization.ts +++ b/src/domain/template/template/template.service.authorization.ts @@ -147,7 +147,7 @@ export class TemplateAuthorizationService { if (template.type == TemplateType.INNOVATION_FLOW) { if (!template.innovationFlow) { throw new RelationshipNotFoundException( - `Unable to load Collaboration on Template of that type: ${template.id} `, + `Unable to load InnovationFlow on Template of that type: ${template.id} `, LogContext.TEMPLATES ); } From ba88861c3c0a8ab2be98c1d32c5ebce09dc8c7fc Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Wed, 2 Oct 2024 13:38:11 +0200 Subject: [PATCH 54/78] fixed loading of auth reset entities on template --- .../template/template/template.service.authorization.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/domain/template/template/template.service.authorization.ts b/src/domain/template/template/template.service.authorization.ts index c8c7c14b99..557e8ff64f 100644 --- a/src/domain/template/template/template.service.authorization.ts +++ b/src/domain/template/template/template.service.authorization.ts @@ -52,6 +52,10 @@ export class TemplateAuthorizationService { collaboration: { authorization: true, }, + innovationFlow: { + profile: true, + authorization: true, + }, }, } ); From efd46280a7b6dc184f9e4e9a3eafb705f920f142 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Wed, 2 Oct 2024 14:02:52 +0200 Subject: [PATCH 55/78] allow auth reset on ai server --- .../ai-server/ai-server/ai.server.resolver.mutations.ts | 2 +- .../ai-server/ai-server/ai.server.service.authorization.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/ai-server/ai-server/ai.server.resolver.mutations.ts b/src/services/ai-server/ai-server/ai.server.resolver.mutations.ts index 3e077c2aaa..05d19ce48a 100644 --- a/src/services/ai-server/ai-server/ai.server.resolver.mutations.ts +++ b/src/services/ai-server/ai-server/ai.server.resolver.mutations.ts @@ -144,7 +144,7 @@ export class AiServerResolverMutations { this.authorizationService.grantAccessOrFail( agentInfo, aiServer.authorization, - AuthorizationPrivilege.PLATFORM_ADMIN, + AuthorizationPrivilege.GRANT, // To be authorizatin reset `create Virtual persona: ${aiPersonaServiceData.engine}` ); let aiPersonaService = diff --git a/src/services/ai-server/ai-server/ai.server.service.authorization.ts b/src/services/ai-server/ai-server/ai.server.service.authorization.ts index 142cb52e29..57a577237d 100644 --- a/src/services/ai-server/ai-server/ai.server.service.authorization.ts +++ b/src/services/ai-server/ai-server/ai.server.service.authorization.ts @@ -80,6 +80,7 @@ export class AiServerAuthorizationService { AuthorizationPrivilege.UPDATE, AuthorizationPrivilege.DELETE, AuthorizationPrivilege.GRANT, + AuthorizationPrivilege.AUTHORIZATION_RESET, ], [AuthorizationCredential.GLOBAL_ADMIN], CREDENTIAL_RULE_AI_SERVER_GLOBAL_ADMINS From ae106a0104d3226c7c61510920f21380489232ef Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Wed, 2 Oct 2024 14:06:33 +0200 Subject: [PATCH 56/78] fix checked privilege --- .../ai-server/ai-server/ai.server.resolver.mutations.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/ai-server/ai-server/ai.server.resolver.mutations.ts b/src/services/ai-server/ai-server/ai.server.resolver.mutations.ts index 05d19ce48a..011b031f56 100644 --- a/src/services/ai-server/ai-server/ai.server.resolver.mutations.ts +++ b/src/services/ai-server/ai-server/ai.server.resolver.mutations.ts @@ -122,7 +122,7 @@ export class AiServerResolverMutations { this.authorizationService.grantAccessOrFail( agentInfo, aiServer.authorization, - AuthorizationPrivilege.AUTHORIZATION_RESET, + AuthorizationPrivilege.GRANT, // to be auth reset `reset authorization on aiServer: ${agentInfo.email}` ); const authorizations = @@ -144,7 +144,7 @@ export class AiServerResolverMutations { this.authorizationService.grantAccessOrFail( agentInfo, aiServer.authorization, - AuthorizationPrivilege.GRANT, // To be authorizatin reset + AuthorizationPrivilege.CREATE, `create Virtual persona: ${aiPersonaServiceData.engine}` ); let aiPersonaService = From 45efc53b8a4f1f14cf8900cee649b87406777545 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Wed, 2 Oct 2024 14:33:22 +0200 Subject: [PATCH 57/78] made lookup by nameID to be separate to lookup by UUID --- .../template/template/template.service.ts | 21 +++-------- .../innovation-pack/innovaton.pack.service.ts | 37 +++++++++++-------- .../lookup.by.name.resolver.fields.ts | 15 ++++---- 3 files changed, 36 insertions(+), 37 deletions(-) diff --git a/src/domain/template/template/template.service.ts b/src/domain/template/template/template.service.ts index 551bf1060c..6bad82f5ba 100644 --- a/src/domain/template/template/template.service.ts +++ b/src/domain/template/template/template.service.ts @@ -380,31 +380,22 @@ export class TemplateService { }); } - async getTemplateInTemplatesSetOrFail( + async getTemplateByNameIDInTemplatesSetOrFail( templatesSetID: string, - templateIdOrNameId: string + templateNameId: string ): Promise { - let template = await this.templateRepository.findOne({ + const template = await this.templateRepository.findOne({ where: { templatesSet: { id: templatesSetID, }, - nameID: templateIdOrNameId, + id: templateNameId, }, }); - if (!template) { - template = await this.templateRepository.findOne({ - where: { - templatesSet: { - id: templatesSetID, - }, - id: templateIdOrNameId, - }, - }); - } + if (!template) { throw new EntityNotFoundException( - `Templates with ID/NameId(${templatesSetID}) not found!`, + `Templates with NameID (${templateNameId}) not found in templatesSet with ID: ${templatesSetID}!`, LogContext.TEMPLATES ); } diff --git a/src/library/innovation-pack/innovaton.pack.service.ts b/src/library/innovation-pack/innovaton.pack.service.ts index 8819879937..7daf3d1024 100644 --- a/src/library/innovation-pack/innovaton.pack.service.ts +++ b/src/library/innovation-pack/innovaton.pack.service.ts @@ -1,4 +1,3 @@ -import { UUID_LENGTH } from '@common/constants'; import { LogContext, ProfileType } from '@common/enums'; import { EntityNotFoundException, @@ -148,20 +147,11 @@ export class InnovationPackService { innovationPackID: string, options?: FindOneOptions ): Promise { - let innovationPack: IInnovationPack | null = null; - if (innovationPackID.length === UUID_LENGTH) { - innovationPack = await this.innovationPackRepository.findOne({ - where: { id: innovationPackID }, - ...options, - }); - } - if (!innovationPack) { - // look up based on nameID - innovationPack = await this.innovationPackRepository.findOne({ - where: { nameID: innovationPackID }, - ...options, - }); - } + const innovationPack = await this.innovationPackRepository.findOne({ + where: { id: innovationPackID }, + ...options, + }); + if (!innovationPack) throw new EntityNotFoundException( `Unable to find InnovationPack with ID: ${innovationPackID}`, @@ -170,6 +160,23 @@ export class InnovationPackService { return innovationPack; } + async getInnovationPackByNameIdOrFail( + innovationPackNameID: string, + options?: FindOneOptions + ): Promise { + const innovationPack = await this.innovationPackRepository.findOne({ + where: { nameID: innovationPackNameID }, + ...options, + }); + + if (!innovationPack) + throw new EntityNotFoundException( + `Unable to find InnovationPack using NameID: ${innovationPackNameID}`, + LogContext.LIBRARY + ); + return innovationPack; + } + public async getProfile( innovationPackInput: IInnovationPack, relations?: FindOptionsRelations diff --git a/src/services/api/lookup-by-name/lookup.by.name.resolver.fields.ts b/src/services/api/lookup-by-name/lookup.by.name.resolver.fields.ts index c011908be3..1283aefc0b 100644 --- a/src/services/api/lookup-by-name/lookup.by.name.resolver.fields.ts +++ b/src/services/api/lookup-by-name/lookup.by.name.resolver.fields.ts @@ -31,7 +31,7 @@ export class LookupByNameResolverFields { @Args('NAMEID', { type: () => NameID }) nameid: string ): Promise { const innovationPack = - await this.innovationPackService.getInnovationPackOrFail(nameid); + await this.innovationPackService.getInnovationPackByNameIdOrFail(nameid); this.authorizationService.grantAccessOrFail( agentInfo, innovationPack.authorization, @@ -46,23 +46,24 @@ export class LookupByNameResolverFields { @ResolveField(() => ITemplate, { nullable: true, description: - 'Lookup the specified Template using a templatesSetId and NameID', + 'Lookup the specified Template using a templatesSetId and the template NameID', }) async template( @CurrentUser() agentInfo: AgentInfo, @Args('templatesSetID', { type: () => UUID }) ID: string, @Args('NAMEID', { type: () => NameID }) nameID: string ): Promise { - const template = await this.templateService.getTemplateInTemplatesSetOrFail( - ID, - nameID - ); + const template = + await this.templateService.getTemplateByNameIDInTemplatesSetOrFail( + ID, + nameID + ); this.authorizationService.grantAccessOrFail( agentInfo, template.authorization, AuthorizationPrivilege.READ, - `lookup InnovationPack by NameID: ${template.id}` + `lookup template by NameID: ${template.id}` ); return template; From fa93a95f77351cb9e805e033a4bfb47bb50efe85 Mon Sep 17 00:00:00 2001 From: Valentin Yanakiev Date: Wed, 2 Oct 2024 15:41:20 +0300 Subject: [PATCH 58/78] Cleanup roles indexes --- .../1727872794564-cleanupRolesIndexes.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/migrations/1727872794564-cleanupRolesIndexes.ts diff --git a/src/migrations/1727872794564-cleanupRolesIndexes.ts b/src/migrations/1727872794564-cleanupRolesIndexes.ts new file mode 100644 index 0000000000..81d41754a1 --- /dev/null +++ b/src/migrations/1727872794564-cleanupRolesIndexes.ts @@ -0,0 +1,29 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class Test1727872794564 implements MigrationInterface { + name = 'Test1727872794564'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + 'DROP INDEX `FK_b3d3f3c2ce851d1059c4ed26ba2` ON `platform_invitation`' + ); + await queryRunner.query( + 'DROP INDEX `FK_500cee6f635849f50e19c7e2b76` ON `application`' + ); + await queryRunner.query( + 'DROP INDEX `FK_339c1fe2a9c5caef5b982303fb0` ON `invitation`' + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + 'CREATE INDEX `FK_339c1fe2a9c5caef5b982303fb0` ON `invitation` (`roleSetId`)' + ); + await queryRunner.query( + 'CREATE INDEX `FK_500cee6f635849f50e19c7e2b76` ON `application` (`roleSetId`)' + ); + await queryRunner.query( + 'CREATE INDEX `FK_b3d3f3c2ce851d1059c4ed26ba2` ON `platform_invitation` (`roleSetId`)' + ); + } +} From 2108452e542ecf6044a12318376f26bbd1b8fd31 Mon Sep 17 00:00:00 2001 From: Carlos Cano Date: Wed, 2 Oct 2024 15:45:52 +0200 Subject: [PATCH 59/78] fix migration --- .../1727872794564-cleanupRolesIndexes.ts | 49 ++++++++++++++++--- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/src/migrations/1727872794564-cleanupRolesIndexes.ts b/src/migrations/1727872794564-cleanupRolesIndexes.ts index 81d41754a1..445281aac7 100644 --- a/src/migrations/1727872794564-cleanupRolesIndexes.ts +++ b/src/migrations/1727872794564-cleanupRolesIndexes.ts @@ -1,29 +1,62 @@ import { MigrationInterface, QueryRunner } from 'typeorm'; -export class Test1727872794564 implements MigrationInterface { - name = 'Test1727872794564'; +export class cleanupRolesIndexes1727872794564 implements MigrationInterface { + name = 'cleanupRolesIndexes1727872794564'; public async up(queryRunner: QueryRunner): Promise { + // platform_invitation + // This is the new FK using the old index await queryRunner.query( - 'DROP INDEX `FK_b3d3f3c2ce851d1059c4ed26ba2` ON `platform_invitation`' + `ALTER TABLE \`platform_invitation\` DROP FOREIGN KEY \`FK_562dce4a08bb214f08107b3631e\`` ); + // This is the old index that needs to be removed await queryRunner.query( - 'DROP INDEX `FK_500cee6f635849f50e19c7e2b76` ON `application`' + `DROP INDEX \`FK_b3d3f3c2ce851d1059c4ed26ba2\` ON \`platform_invitation\` + ` ); + // This will add the index and the FK back await queryRunner.query( - 'DROP INDEX `FK_339c1fe2a9c5caef5b982303fb0` ON `invitation`' + `ALTER TABLE \`platform_invitation\` ADD CONSTRAINT \`FK_562dce4a08bb214f08107b3631e\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION + ` + ); + + // application + await queryRunner.query( + `ALTER TABLE \`application\` DROP FOREIGN KEY \`FK_8fb220ad1ac1f9c86ec39d134e4\`` + ); + await queryRunner.query( + `DROP INDEX \`FK_500cee6f635849f50e19c7e2b76\` ON \`application\`` + ); + await queryRunner.query( + `ALTER TABLE \`application\` ADD CONSTRAINT \`FK_8fb220ad1ac1f9c86ec39d134e4\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + + // invitation + await queryRunner.query( + `ALTER TABLE \`invitation\` DROP FOREIGN KEY \`FK_6a3b86c6db10582baae7058f5b9\`` + ); + await queryRunner.query( + `DROP INDEX \`FK_339c1fe2a9c5caef5b982303fb0\` ON \`invitation\`` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` ADD CONSTRAINT \`FK_6a3b86c6db10582baae7058f5b9\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` ); } public async down(queryRunner: QueryRunner): Promise { + // leave the FK just add back that wrong index to platform_invitation table await queryRunner.query( - 'CREATE INDEX `FK_339c1fe2a9c5caef5b982303fb0` ON `invitation` (`roleSetId`)' + `CREATE INDEX \`FK_b3d3f3c2ce851d1059c4ed26ba2\` ON \`platform_invitation\` (\`roleSetId\`)` ); + + // leave the FK just add back that wrong index to application table await queryRunner.query( - 'CREATE INDEX `FK_500cee6f635849f50e19c7e2b76` ON `application` (`roleSetId`)' + `CREATE INDEX \`FK_500cee6f635849f50e19c7e2b76\` ON \`application\` (\`roleSetId\`)` ); + + // leave the FK just add back that wrong index to invitation table await queryRunner.query( - 'CREATE INDEX `FK_b3d3f3c2ce851d1059c4ed26ba2` ON `platform_invitation` (`roleSetId`)' + `CREATE INDEX \`FK_339c1fe2a9c5caef5b982303fb0\` ON \`invitation\` (\`roleSetId\`)` ); } } From 8e19cd44cb995fdb1300693ac77ec0eddea7ec36 Mon Sep 17 00:00:00 2001 From: Carlos Cano Date: Wed, 2 Oct 2024 18:38:39 +0200 Subject: [PATCH 60/78] Fix migration --- src/migrations/1726843779059-roleSet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/migrations/1726843779059-roleSet.ts b/src/migrations/1726843779059-roleSet.ts index d92c71e245..52b8da1804 100644 --- a/src/migrations/1726843779059-roleSet.ts +++ b/src/migrations/1726843779059-roleSet.ts @@ -115,7 +115,7 @@ export class RoleSet1726843779059 implements MigrationInterface { await this.createRole( queryRunner, 'admin', - policy.member, + policy.admin, roleSetID, 0, 0, From 0cd64d85a474592427190c5c0e05c5b3c7f3f3d8 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Thu, 3 Oct 2024 06:40:53 +0200 Subject: [PATCH 61/78] added support for inviting a contributor to an additional role besides the entry role --- .../invitation/dto/invitation.dto.create.ts | 22 +++++++++++++++---- .../access/invitation/invitation.entity.ts | 7 ++++++ .../access/invitation/invitation.interface.ts | 8 +++++++ ...t.lifecycle.invitation.options.provider.ts | 10 +++++++++ .../role-set/role.set.resolver.mutations.ts | 3 ++- .../access/role-set/role.set.service.ts | 2 +- .../1727930288139-invitationToRole.ts | 17 ++++++++++++++ .../api/registration/registration.service.ts | 2 +- 8 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 src/migrations/1727930288139-invitationToRole.ts diff --git a/src/domain/access/invitation/dto/invitation.dto.create.ts b/src/domain/access/invitation/dto/invitation.dto.create.ts index cde00875f8..bb99b58532 100644 --- a/src/domain/access/invitation/dto/invitation.dto.create.ts +++ b/src/domain/access/invitation/dto/invitation.dto.create.ts @@ -1,4 +1,9 @@ -import { MID_TEXT_LENGTH, UUID_LENGTH } from '@common/constants'; +import { + MID_TEXT_LENGTH, + SMALL_TEXT_LENGTH, + UUID_LENGTH, +} from '@common/constants'; +import { CommunityRoleType } from '@common/enums/community.role'; import { UUID } from '@domain/common/scalars'; import { Field, InputType } from '@nestjs/graphql'; import { IsOptional, MaxLength } from 'class-validator'; @@ -7,11 +12,11 @@ import { IsOptional, MaxLength } from 'class-validator'; export class CreateInvitationInput { @Field(() => UUID, { nullable: false, - description: 'The identifier for the contributor being invited.', + description: + 'The identifier for the contributor being invited to join in the entry Role.', }) - @IsOptional() @MaxLength(UUID_LENGTH) - invitedContributor!: string; + invitedContributorID!: string; @Field({ nullable: true }) @IsOptional() @@ -22,4 +27,13 @@ export class CreateInvitationInput { roleSetID!: string; invitedToParent!: boolean; + + @Field(() => CommunityRoleType, { + nullable: true, + description: + 'An additional role to assign to the Contributor, in addition to the entry Role.', + }) + @IsOptional() + @MaxLength(SMALL_TEXT_LENGTH) + extraRole?: CommunityRoleType; } diff --git a/src/domain/access/invitation/invitation.entity.ts b/src/domain/access/invitation/invitation.entity.ts index 5983600a6b..821d252dce 100644 --- a/src/domain/access/invitation/invitation.entity.ts +++ b/src/domain/access/invitation/invitation.entity.ts @@ -5,6 +5,7 @@ import { AuthorizableEntity } from '@domain/common/entity/authorizable-entity'; import { CommunityContributorType } from '@common/enums/community.contributor.type'; import { ENUM_LENGTH, MID_TEXT_LENGTH, UUID_LENGTH } from '@common/constants'; import { RoleSet } from '@domain/access/role-set/role.set.entity'; +import { CommunityRoleType } from '@common/enums/community.role'; @Entity() export class Invitation extends AuthorizableEntity implements IInvitation { // todo ID in migration is varchar - must be char(36) @@ -37,4 +38,10 @@ export class Invitation extends AuthorizableEntity implements IInvitation { onDelete: 'CASCADE', }) roleSet?: RoleSet; + + @Column('varchar', { + length: ENUM_LENGTH, + nullable: true, + }) + extraRole?: CommunityRoleType; } diff --git a/src/domain/access/invitation/invitation.interface.ts b/src/domain/access/invitation/invitation.interface.ts index b550ad6473..5d1297f9b6 100644 --- a/src/domain/access/invitation/invitation.interface.ts +++ b/src/domain/access/invitation/invitation.interface.ts @@ -3,6 +3,7 @@ import { Field, ObjectType } from '@nestjs/graphql'; import { IAuthorizable } from '@domain/common/entity/authorizable-entity'; import { CommunityContributorType } from '@common/enums/community.contributor.type'; import { IRoleSet } from '@domain/access/role-set'; +import { CommunityRoleType } from '@common/enums/community.role'; @ObjectType('Invitation') export class IInvitation extends IAuthorizable { @@ -35,4 +36,11 @@ export class IInvitation extends IAuthorizable { description: 'The type of contributor that is invited.', }) contributorType!: CommunityContributorType; + + @Field(() => CommunityRoleType, { + nullable: true, + description: + 'An additional role to assign to the Contributor, in addition to the entry Role.', + }) + extraRole?: CommunityRoleType; } diff --git a/src/domain/access/role-set/role.set.lifecycle.invitation.options.provider.ts b/src/domain/access/role-set/role.set.lifecycle.invitation.options.provider.ts index ead2b31f44..1f09b71746 100644 --- a/src/domain/access/role-set/role.set.lifecycle.invitation.options.provider.ts +++ b/src/domain/access/role-set/role.set.lifecycle.invitation.options.provider.ts @@ -127,6 +127,16 @@ export class RoleSetInvitationLifecycleOptionsProvider { event.agentInfo, true ); + if (invitation.extraRole) { + await this.roleSetService.assignContributorToRole( + roleSet, + invitation.extraRole, + contributorID, + invitation.contributorType, + event.agentInfo, + false + ); + } } finally { resolve(); } diff --git a/src/domain/access/role-set/role.set.resolver.mutations.ts b/src/domain/access/role-set/role.set.resolver.mutations.ts index 527f06f828..3234fd106e 100644 --- a/src/domain/access/role-set/role.set.resolver.mutations.ts +++ b/src/domain/access/role-set/role.set.resolver.mutations.ts @@ -306,6 +306,7 @@ export class RoleSetResolverMutations { roleData.contributorID ); } + @UseGuards(GraphqlGuard) @Mutation(() => IRoleSet, { description: @@ -521,7 +522,7 @@ export class RoleSetResolverMutations { ): Promise { const input: CreateInvitationInput = { roleSetID: roleSet.id, - invitedContributor: invitedContributor.id, + invitedContributorID: invitedContributor.id, createdBy: agentInfo.userID, invitedToParent, welcomeMessage, diff --git a/src/domain/access/role-set/role.set.service.ts b/src/domain/access/role-set/role.set.service.ts index 54212e921e..6e0354c582 100644 --- a/src/domain/access/role-set/role.set.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -1023,7 +1023,7 @@ export class RoleSetService { ): Promise { const { contributor: contributor, agent } = await this.contributorService.getContributorAndAgent( - invitationData.invitedContributor + invitationData.invitedContributorID ); const roleSet = await this.getRoleSetOrFail(invitationData.roleSetID); diff --git a/src/migrations/1727930288139-invitationToRole.ts b/src/migrations/1727930288139-invitationToRole.ts new file mode 100644 index 0000000000..5e4b5f92a3 --- /dev/null +++ b/src/migrations/1727930288139-invitationToRole.ts @@ -0,0 +1,17 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class InvitationToRole1727930288139 implements MigrationInterface { + name = 'InvitationToRole1727930288139'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE \`invitation\` ADD \`extraRole\` varchar(128) NULL` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE \`invitation\` DROP COLUMN \`extraRole\`` + ); + } +} diff --git a/src/services/api/registration/registration.service.ts b/src/services/api/registration/registration.service.ts index 76aefe4083..f17c244d6f 100644 --- a/src/services/api/registration/registration.service.ts +++ b/src/services/api/registration/registration.service.ts @@ -124,7 +124,7 @@ export class RegistrationService { // Process community invitations if (roleSet) { const invitationInput: CreateInvitationInput = { - invitedContributor: user.id, + invitedContributorID: user.id, roleSetID: roleSet.id, createdBy: platformInvitation.createdBy, invitedToParent: platformInvitation.communityInvitedToParent, From 33f2adabb547de24bc2a25055a995f56db88b9db Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Thu, 3 Oct 2024 07:03:46 +0200 Subject: [PATCH 62/78] better information in log messages on auth --- src/core/authorization/authorization.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/authorization/authorization.service.ts b/src/core/authorization/authorization.service.ts index 402d67f4d4..c765bcaa03 100644 --- a/src/core/authorization/authorization.service.ts +++ b/src/core/authorization/authorization.service.ts @@ -89,7 +89,7 @@ export class AuthorizationService { ); if (authorization.credentialRules === '') { throw new AuthorizationInvalidPolicyException( - `AuthorizationPolicy without credential rules provided: ${authorization.id}`, + `AuthorizationPolicy without credential rules provided: ${authorization.id}, type: ${authorization.type}`, LogContext.AUTH ); } @@ -130,7 +130,7 @@ export class AuthorizationService { for (const privilege of rule.grantedPrivileges) { if (privilege === privilegeRequired) { this.logger.verbose?.( - `[CredentialRule] Granted privilege '${privilegeRequired}' using rule '${rule.name}' on authorization ${authorization.id}`, + `[CredentialRule] Granted privilege '${privilegeRequired}' using rule '${rule.name}' on authorization ${authorization.id} on type: ${authorization.type}`, LogContext.AUTH_POLICY ); return true; From 80c4708e563298b821bae23270d00cb3cc598ae2 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Thu, 3 Oct 2024 07:40:06 +0200 Subject: [PATCH 63/78] renamed invitedContributor to be with ID suffix --- .../access/invitation/invitation.entity.ts | 2 +- .../access/invitation/invitation.interface.ts | 2 +- .../access/invitation/invitation.service.ts | 6 +- ...t.lifecycle.invitation.options.provider.ts | 2 +- .../access/role-set/role.set.service.ts | 4 +- .../virtual.contributor.service.ts | 2 +- .../1727872794564-cleanupRolesIndexes.ts | 68 +++++++++---------- .../1727930288139-invitationToRole.ts | 18 +++++ src/services/api/roles/roles.service.ts | 2 +- 9 files changed, 61 insertions(+), 45 deletions(-) diff --git a/src/domain/access/invitation/invitation.entity.ts b/src/domain/access/invitation/invitation.entity.ts index 821d252dce..e2931f9111 100644 --- a/src/domain/access/invitation/invitation.entity.ts +++ b/src/domain/access/invitation/invitation.entity.ts @@ -18,7 +18,7 @@ export class Invitation extends AuthorizableEntity implements IInvitation { lifecycle!: Lifecycle; @Column('char', { length: UUID_LENGTH, nullable: false }) - invitedContributor!: string; + invitedContributorID!: string; @Column('char', { length: UUID_LENGTH, nullable: false }) createdBy!: string; diff --git a/src/domain/access/invitation/invitation.interface.ts b/src/domain/access/invitation/invitation.interface.ts index 5d1297f9b6..b9674421ba 100644 --- a/src/domain/access/invitation/invitation.interface.ts +++ b/src/domain/access/invitation/invitation.interface.ts @@ -7,7 +7,7 @@ import { CommunityRoleType } from '@common/enums/community.role'; @ObjectType('Invitation') export class IInvitation extends IAuthorizable { - invitedContributor!: string; + invitedContributorID!: string; createdBy!: string; roleSet?: IRoleSet; diff --git a/src/domain/access/invitation/invitation.service.ts b/src/domain/access/invitation/invitation.service.ts index fbf1b35599..a17c231f13 100644 --- a/src/domain/access/invitation/invitation.service.ts +++ b/src/domain/access/invitation/invitation.service.ts @@ -111,7 +111,7 @@ export class InvitationService { async getInvitedContributor(invitation: IInvitation): Promise { const contributor = await this.contributorService.getContributorByUuidOrFail( - invitation.invitedContributor + invitation.invitedContributorID ); if (!contributor) throw new RelationshipNotFoundException( @@ -137,7 +137,7 @@ export class InvitationService { ): Promise { const existingInvitations = await this.invitationRepository.find({ where: { - invitedContributor: contributorID, + invitedContributorID: contributorID, roleSet: { id: roleSetID }, }, relations: { roleSet: true }, @@ -153,7 +153,7 @@ export class InvitationService { ): Promise { const findOpts: FindManyOptions = { relations: { roleSet: true }, - where: { invitedContributor: contributorID }, + where: { invitedContributorID: contributorID }, }; if (states.length) { diff --git a/src/domain/access/role-set/role.set.lifecycle.invitation.options.provider.ts b/src/domain/access/role-set/role.set.lifecycle.invitation.options.provider.ts index 1f09b71746..296e725cc6 100644 --- a/src/domain/access/role-set/role.set.lifecycle.invitation.options.provider.ts +++ b/src/domain/access/role-set/role.set.lifecycle.invitation.options.provider.ts @@ -94,7 +94,7 @@ export class RoleSetInvitationLifecycleOptionsProvider { }, } ); - const contributorID = invitation.invitedContributor; + const contributorID = invitation.invitedContributorID; const roleSet = invitation.roleSet; if (!contributorID || !roleSet) { throw new EntityNotInitializedException( diff --git a/src/domain/access/role-set/role.set.service.ts b/src/domain/access/role-set/role.set.service.ts index 6e0354c582..918d0d8743 100644 --- a/src/domain/access/role-set/role.set.service.ts +++ b/src/domain/access/role-set/role.set.service.ts @@ -1082,7 +1082,7 @@ export class RoleSetService { const openInvitation = await this.findOpenInvitation(user.id, roleSet.id); if (openInvitation) { throw new RoleSetMembershipException( - `Application not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on RoleSet: ${roleSet.id}.`, + `Application not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributorID} (${openInvitation.contributorType}) on RoleSet: ${roleSet.id}.`, LogContext.COMMUNITY ); } @@ -1107,7 +1107,7 @@ export class RoleSetService { ); if (openInvitation) { throw new RoleSetMembershipException( - `Invitation not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributor} (${openInvitation.contributorType}) on RoleSet: ${roleSet.id}.`, + `Invitation not possible: An open invitation (ID: ${openInvitation.id}) already exists for contributor ${openInvitation.invitedContributorID} (${openInvitation.contributorType}) on RoleSet: ${roleSet.id}.`, LogContext.COMMUNITY ); } diff --git a/src/domain/community/virtual-contributor/virtual.contributor.service.ts b/src/domain/community/virtual-contributor/virtual.contributor.service.ts index 42531756c4..45a3a8ca87 100644 --- a/src/domain/community/virtual-contributor/virtual.contributor.service.ts +++ b/src/domain/community/virtual-contributor/virtual.contributor.service.ts @@ -592,7 +592,7 @@ export class VirtualContributorService { //adding this to avoid circular dependency between VirtualContributor, Room, and Invitation private async deleteVCInvitations(contributorID: string) { const invitations = await this.entityManager.find(Invitation, { - where: { invitedContributor: contributorID }, + where: { invitedContributorID: contributorID }, }); for (const invitation of invitations) { if (invitation.authorization) { diff --git a/src/migrations/1727872794564-cleanupRolesIndexes.ts b/src/migrations/1727872794564-cleanupRolesIndexes.ts index 445281aac7..5bfeb29750 100644 --- a/src/migrations/1727872794564-cleanupRolesIndexes.ts +++ b/src/migrations/1727872794564-cleanupRolesIndexes.ts @@ -6,41 +6,39 @@ export class cleanupRolesIndexes1727872794564 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { // platform_invitation // This is the new FK using the old index - await queryRunner.query( - `ALTER TABLE \`platform_invitation\` DROP FOREIGN KEY \`FK_562dce4a08bb214f08107b3631e\`` - ); - // This is the old index that needs to be removed - await queryRunner.query( - `DROP INDEX \`FK_b3d3f3c2ce851d1059c4ed26ba2\` ON \`platform_invitation\` - ` - ); - // This will add the index and the FK back - await queryRunner.query( - `ALTER TABLE \`platform_invitation\` ADD CONSTRAINT \`FK_562dce4a08bb214f08107b3631e\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION - ` - ); - - // application - await queryRunner.query( - `ALTER TABLE \`application\` DROP FOREIGN KEY \`FK_8fb220ad1ac1f9c86ec39d134e4\`` - ); - await queryRunner.query( - `DROP INDEX \`FK_500cee6f635849f50e19c7e2b76\` ON \`application\`` - ); - await queryRunner.query( - `ALTER TABLE \`application\` ADD CONSTRAINT \`FK_8fb220ad1ac1f9c86ec39d134e4\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` - ); - - // invitation - await queryRunner.query( - `ALTER TABLE \`invitation\` DROP FOREIGN KEY \`FK_6a3b86c6db10582baae7058f5b9\`` - ); - await queryRunner.query( - `DROP INDEX \`FK_339c1fe2a9c5caef5b982303fb0\` ON \`invitation\`` - ); - await queryRunner.query( - `ALTER TABLE \`invitation\` ADD CONSTRAINT \`FK_6a3b86c6db10582baae7058f5b9\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` - ); + // await queryRunner.query( + // `ALTER TABLE \`platform_invitation\` DROP FOREIGN KEY \`FK_562dce4a08bb214f08107b3631e\`` + // ); + // // This is the old index that needs to be removed + // await queryRunner.query( + // `DROP INDEX \`FK_b3d3f3c2ce851d1059c4ed26ba2\` ON \`platform_invitation\` + // ` + // ); + // // This will add the index and the FK back + // await queryRunner.query( + // `ALTER TABLE \`platform_invitation\` ADD CONSTRAINT \`FK_562dce4a08bb214f08107b3631e\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION + // ` + // ); + // // application + // await queryRunner.query( + // `ALTER TABLE \`application\` DROP FOREIGN KEY \`FK_8fb220ad1ac1f9c86ec39d134e4\`` + // ); + // await queryRunner.query( + // `DROP INDEX \`FK_500cee6f635849f50e19c7e2b76\` ON \`application\`` + // ); + // await queryRunner.query( + // `ALTER TABLE \`application\` ADD CONSTRAINT \`FK_8fb220ad1ac1f9c86ec39d134e4\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + // ); + // // invitation + // await queryRunner.query( + // `ALTER TABLE \`invitation\` DROP FOREIGN KEY \`FK_6a3b86c6db10582baae7058f5b9\`` + // ); + // await queryRunner.query( + // `DROP INDEX \`FK_339c1fe2a9c5caef5b982303fb0\` ON \`invitation\`` + // ); + // await queryRunner.query( + // `ALTER TABLE \`invitation\` ADD CONSTRAINT \`FK_6a3b86c6db10582baae7058f5b9\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + // ); } public async down(queryRunner: QueryRunner): Promise { diff --git a/src/migrations/1727930288139-invitationToRole.ts b/src/migrations/1727930288139-invitationToRole.ts index 5e4b5f92a3..f871b36e5b 100644 --- a/src/migrations/1727930288139-invitationToRole.ts +++ b/src/migrations/1727930288139-invitationToRole.ts @@ -7,11 +7,29 @@ export class InvitationToRole1727930288139 implements MigrationInterface { await queryRunner.query( `ALTER TABLE \`invitation\` ADD \`extraRole\` varchar(128) NULL` ); + await queryRunner.query( + `DROP INDEX \`FK_562dce4a08bb214f08107b3631e\` ON \`platform_invitation\`` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` CHANGE \`invitedContributor\` \`invitedContributorID\` char(36) NOT NULL` + ); + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` ADD CONSTRAINT \`FK_562dce4a08bb214f08107b3631e\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); } public async down(queryRunner: QueryRunner): Promise { await queryRunner.query( `ALTER TABLE \`invitation\` DROP COLUMN \`extraRole\`` ); + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` DROP FOREIGN KEY \`FK_562dce4a08bb214f08107b3631e\`` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` CHANGE \`invitedContributorID\` \`invitedContributor\` char(36) NOT NULL` + ); + await queryRunner.query( + `CREATE INDEX \`FK_562dce4a08bb214f08107b3631e\` ON \`platform_invitation\` (\`roleSetId\`)` + ); } } diff --git a/src/services/api/roles/roles.service.ts b/src/services/api/roles/roles.service.ts index e1d64c146f..256b82c652 100644 --- a/src/services/api/roles/roles.service.ts +++ b/src/services/api/roles/roles.service.ts @@ -241,7 +241,7 @@ export class RolesService { invitation.createdDate, invitation.updatedDate ); - invitationResult.contributorID = invitation.invitedContributor; + invitationResult.contributorID = invitation.invitedContributorID; invitationResult.contributorType = invitation.contributorType; invitationResult.createdBy = invitation.createdBy ?? ''; From 8853f67e313dfd8e5d19b1965cd903fa7ffffdc1 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Thu, 3 Oct 2024 07:41:16 +0200 Subject: [PATCH 64/78] recommented change in roleIndex clean up --- .../1727872794564-cleanupRolesIndexes.ts | 68 ++++++++++--------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/src/migrations/1727872794564-cleanupRolesIndexes.ts b/src/migrations/1727872794564-cleanupRolesIndexes.ts index 5bfeb29750..445281aac7 100644 --- a/src/migrations/1727872794564-cleanupRolesIndexes.ts +++ b/src/migrations/1727872794564-cleanupRolesIndexes.ts @@ -6,39 +6,41 @@ export class cleanupRolesIndexes1727872794564 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { // platform_invitation // This is the new FK using the old index - // await queryRunner.query( - // `ALTER TABLE \`platform_invitation\` DROP FOREIGN KEY \`FK_562dce4a08bb214f08107b3631e\`` - // ); - // // This is the old index that needs to be removed - // await queryRunner.query( - // `DROP INDEX \`FK_b3d3f3c2ce851d1059c4ed26ba2\` ON \`platform_invitation\` - // ` - // ); - // // This will add the index and the FK back - // await queryRunner.query( - // `ALTER TABLE \`platform_invitation\` ADD CONSTRAINT \`FK_562dce4a08bb214f08107b3631e\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION - // ` - // ); - // // application - // await queryRunner.query( - // `ALTER TABLE \`application\` DROP FOREIGN KEY \`FK_8fb220ad1ac1f9c86ec39d134e4\`` - // ); - // await queryRunner.query( - // `DROP INDEX \`FK_500cee6f635849f50e19c7e2b76\` ON \`application\`` - // ); - // await queryRunner.query( - // `ALTER TABLE \`application\` ADD CONSTRAINT \`FK_8fb220ad1ac1f9c86ec39d134e4\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` - // ); - // // invitation - // await queryRunner.query( - // `ALTER TABLE \`invitation\` DROP FOREIGN KEY \`FK_6a3b86c6db10582baae7058f5b9\`` - // ); - // await queryRunner.query( - // `DROP INDEX \`FK_339c1fe2a9c5caef5b982303fb0\` ON \`invitation\`` - // ); - // await queryRunner.query( - // `ALTER TABLE \`invitation\` ADD CONSTRAINT \`FK_6a3b86c6db10582baae7058f5b9\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` - // ); + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` DROP FOREIGN KEY \`FK_562dce4a08bb214f08107b3631e\`` + ); + // This is the old index that needs to be removed + await queryRunner.query( + `DROP INDEX \`FK_b3d3f3c2ce851d1059c4ed26ba2\` ON \`platform_invitation\` + ` + ); + // This will add the index and the FK back + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` ADD CONSTRAINT \`FK_562dce4a08bb214f08107b3631e\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION + ` + ); + + // application + await queryRunner.query( + `ALTER TABLE \`application\` DROP FOREIGN KEY \`FK_8fb220ad1ac1f9c86ec39d134e4\`` + ); + await queryRunner.query( + `DROP INDEX \`FK_500cee6f635849f50e19c7e2b76\` ON \`application\`` + ); + await queryRunner.query( + `ALTER TABLE \`application\` ADD CONSTRAINT \`FK_8fb220ad1ac1f9c86ec39d134e4\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + + // invitation + await queryRunner.query( + `ALTER TABLE \`invitation\` DROP FOREIGN KEY \`FK_6a3b86c6db10582baae7058f5b9\`` + ); + await queryRunner.query( + `DROP INDEX \`FK_339c1fe2a9c5caef5b982303fb0\` ON \`invitation\`` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` ADD CONSTRAINT \`FK_6a3b86c6db10582baae7058f5b9\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); } public async down(queryRunner: QueryRunner): Promise { From f8befda12b589e0462b19886500a8bbdf96da7f9 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Thu, 3 Oct 2024 07:58:06 +0200 Subject: [PATCH 65/78] extended invitation to roleset for multiple contributors to pass through the extraRole --- .../dto/role.set.dto.entry.role.invite.ts | 16 +++++++++++++++- .../role-set/role.set.resolver.mutations.ts | 3 +++ src/migrations/1727930288139-invitationToRole.ts | 14 ++------------ 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/domain/access/role-set/dto/role.set.dto.entry.role.invite.ts b/src/domain/access/role-set/dto/role.set.dto.entry.role.invite.ts index d6f89354ac..6bdeeb958e 100644 --- a/src/domain/access/role-set/dto/role.set.dto.entry.role.invite.ts +++ b/src/domain/access/role-set/dto/role.set.dto.entry.role.invite.ts @@ -1,7 +1,12 @@ import { Field, InputType } from '@nestjs/graphql'; import { UUID } from '@domain/common/scalars'; import { IsOptional, MaxLength } from 'class-validator'; -import { MID_TEXT_LENGTH, UUID_LENGTH } from '@common/constants'; +import { + MID_TEXT_LENGTH, + SMALL_TEXT_LENGTH, + UUID_LENGTH, +} from '@common/constants'; +import { CommunityRoleType } from '@common/enums/community.role'; @InputType() export class InviteForEntryRoleOnRoleSetInput { @@ -20,6 +25,15 @@ export class InviteForEntryRoleOnRoleSetInput { @MaxLength(MID_TEXT_LENGTH) welcomeMessage?: string; + @Field(() => CommunityRoleType, { + nullable: true, + description: + 'An additional role to assign to the Contributors, in addition to the entry Role.', + }) + @IsOptional() + @MaxLength(SMALL_TEXT_LENGTH) + extraRole?: CommunityRoleType; + createdBy!: string; invitedToParent!: boolean; diff --git a/src/domain/access/role-set/role.set.resolver.mutations.ts b/src/domain/access/role-set/role.set.resolver.mutations.ts index 3234fd106e..609be0bcb1 100644 --- a/src/domain/access/role-set/role.set.resolver.mutations.ts +++ b/src/domain/access/role-set/role.set.resolver.mutations.ts @@ -507,6 +507,7 @@ export class RoleSetResolverMutations { invitedContributor, agentInfo, invitationData.invitedToParent, + invitationData.extraRole, invitationData.welcomeMessage ); }) @@ -518,6 +519,7 @@ export class RoleSetResolverMutations { invitedContributor: IContributor, agentInfo: AgentInfo, invitedToParent: boolean, + extraRole?: CommunityRoleType, welcomeMessage?: string ): Promise { const input: CreateInvitationInput = { @@ -525,6 +527,7 @@ export class RoleSetResolverMutations { invitedContributorID: invitedContributor.id, createdBy: agentInfo.userID, invitedToParent, + extraRole, welcomeMessage, }; diff --git a/src/migrations/1727930288139-invitationToRole.ts b/src/migrations/1727930288139-invitationToRole.ts index f871b36e5b..0d25ed8ec3 100644 --- a/src/migrations/1727930288139-invitationToRole.ts +++ b/src/migrations/1727930288139-invitationToRole.ts @@ -7,29 +7,19 @@ export class InvitationToRole1727930288139 implements MigrationInterface { await queryRunner.query( `ALTER TABLE \`invitation\` ADD \`extraRole\` varchar(128) NULL` ); - await queryRunner.query( - `DROP INDEX \`FK_562dce4a08bb214f08107b3631e\` ON \`platform_invitation\`` - ); + await queryRunner.query( `ALTER TABLE \`invitation\` CHANGE \`invitedContributor\` \`invitedContributorID\` char(36) NOT NULL` ); - await queryRunner.query( - `ALTER TABLE \`platform_invitation\` ADD CONSTRAINT \`FK_562dce4a08bb214f08107b3631e\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` - ); } public async down(queryRunner: QueryRunner): Promise { await queryRunner.query( `ALTER TABLE \`invitation\` DROP COLUMN \`extraRole\`` ); - await queryRunner.query( - `ALTER TABLE \`platform_invitation\` DROP FOREIGN KEY \`FK_562dce4a08bb214f08107b3631e\`` - ); + await queryRunner.query( `ALTER TABLE \`invitation\` CHANGE \`invitedContributorID\` \`invitedContributor\` char(36) NOT NULL` ); - await queryRunner.query( - `CREATE INDEX \`FK_562dce4a08bb214f08107b3631e\` ON \`platform_invitation\` (\`roleSetId\`)` - ); } } From 563ceb16a137cb600ac0fd0a2f4485917d027b9f Mon Sep 17 00:00:00 2001 From: Svetoslav Petkov Date: Thu, 3 Oct 2024 10:49:48 +0300 Subject: [PATCH 66/78] [Unauth Home page] Sort Explore Spaces section on most activity (#4578) --- .../space/dto/explore.spaces.dto.input.ts | 18 ++++++++++++ .../space/space/space.resolver.queries.ts | 12 ++++++++ src/domain/space/space/space.service.ts | 29 +++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 src/domain/space/space/dto/explore.spaces.dto.input.ts diff --git a/src/domain/space/space/dto/explore.spaces.dto.input.ts b/src/domain/space/space/dto/explore.spaces.dto.input.ts new file mode 100644 index 0000000000..b3418e76a7 --- /dev/null +++ b/src/domain/space/space/dto/explore.spaces.dto.input.ts @@ -0,0 +1,18 @@ +import { Field, InputType } from '@nestjs/graphql'; + +@InputType() +export class ExploreSpacesInput { + @Field(() => Number, { + nullable: true, + description: 'Amount of Spaces returned.', + defaultValue: 30, + }) + limit!: number; + + @Field(() => Number, { + nullable: true, + description: 'Take into account only the activity in the past X days.', + defaultValue: 30, + }) + daysOld!: number; +} diff --git a/src/domain/space/space/space.resolver.queries.ts b/src/domain/space/space/space.resolver.queries.ts index ee16f218a1..92236552a7 100644 --- a/src/domain/space/space/space.resolver.queries.ts +++ b/src/domain/space/space/space.resolver.queries.ts @@ -5,6 +5,7 @@ import { UUID_NAMEID } from '@domain/common/scalars'; import { SpaceService } from './space.service'; import { ISpace } from './space.interface'; import { SpacesQueryArgs } from './dto/space.args.query.spaces'; +import { ExploreSpacesInput } from './dto/explore.spaces.dto.input'; import { InnovationHub } from '@domain/innovation-hub/types'; import { GraphqlGuard } from '@core/authorization'; import { PaginatedSpaces, PaginationArgs } from '@core/pagination'; @@ -64,4 +65,15 @@ export class SpaceResolverQueries { }); return space; } + + @UseGuards(GraphqlGuard) + @Query(() => [ISpace], { + nullable: false, + description: 'Active Spaces only, order by most active in the past X days.', + }) + public exploreSpaces( + @Args('options', { nullable: true }) options?: ExploreSpacesInput + ): Promise { + return this.spaceService.getExploreSpaces(options?.limit, options?.daysOld); + } } diff --git a/src/domain/space/space/space.service.ts b/src/domain/space/space/space.service.ts index bbc53c926c..d31da861f8 100644 --- a/src/domain/space/space/space.service.ts +++ b/src/domain/space/space/space.service.ts @@ -80,6 +80,10 @@ import { templatesSetDefaults } from '../space.defaults/definitions/space.defaul import { InputCreatorService } from '@services/api/input-creator/input.creator.service'; import { RoleSetService } from '@domain/access/role-set/role.set.service'; import { IRoleSet } from '@domain/access/role-set/role.set.interface'; +import { Activity } from '@platform/activity'; + +const EXPLORE_SPACES_LIMIT = 30; +const EXPLORE_SPACES_ACTIVITY_DAYS_OLD = 30; @Injectable() export class SpaceService { @@ -726,6 +730,31 @@ export class SpaceService { return space; } + public getExploreSpaces( + limit = EXPLORE_SPACES_LIMIT, + daysOld = EXPLORE_SPACES_ACTIVITY_DAYS_OLD + ): Promise { + const daysAgo = new Date(); + daysAgo.setDate(daysAgo.getDate() - daysOld); + + return ( + this.spaceRepository + .createQueryBuilder('s') + .leftJoinAndSelect('s.authorization', 'authorization') // eager load the authorization + .innerJoin(Activity, 'a', 's.collaborationId = a.collaborationID') + .where({ + level: SpaceLevel.SPACE, + visibility: SpaceVisibility.ACTIVE, + }) + // activities in the past "daysOld" days + .andWhere('a.createdDate >= :daysAgo', { daysAgo }) + .groupBy('s.id') + .orderBy('COUNT(a.id)', 'DESC') + .limit(limit) + .getMany() + ); + } + async getSpace( spaceID: string, options?: FindOneOptions From 04dc30aa8916f2cc6c7f889b370dc2111daa7b93 Mon Sep 17 00:00:00 2001 From: Carlos Cano Date: Thu, 3 Oct 2024 19:08:43 +0200 Subject: [PATCH 67/78] Fix nameId resolution of template --- src/domain/template/template/template.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domain/template/template/template.service.ts b/src/domain/template/template/template.service.ts index 6bad82f5ba..4dc1313576 100644 --- a/src/domain/template/template/template.service.ts +++ b/src/domain/template/template/template.service.ts @@ -389,7 +389,7 @@ export class TemplateService { templatesSet: { id: templatesSetID, }, - id: templateNameId, + nameID: templateNameId, }, }); From 78b145d814ad8f2f892b295799f3cc6e7419ec0a Mon Sep 17 00:00:00 2001 From: Valentin Yanakiev Date: Fri, 4 Oct 2024 10:43:31 +0300 Subject: [PATCH 68/78] AuthenticationType enum added and resolver added to User --- src/common/enums/authentication.type.ts | 12 ++++++++++++ .../authentication.agent.info/agent.info.ts | 3 ++- .../authentication/authentication.service.ts | 18 ++++++++++++++++++ .../community/user/user.resolver.fields.ts | 14 ++++++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 src/common/enums/authentication.type.ts diff --git a/src/common/enums/authentication.type.ts b/src/common/enums/authentication.type.ts new file mode 100644 index 0000000000..f07561b34e --- /dev/null +++ b/src/common/enums/authentication.type.ts @@ -0,0 +1,12 @@ +import { registerEnumType } from '@nestjs/graphql'; + +export enum AuthenticationType { + LINKEDIN = 'linkedin', + MICROSOFT = 'microsoft', + EMAIL = 'email', + UNKNOWN = 'unknown', +} + +registerEnumType(AuthenticationType, { + name: 'AuthenticationType', +}); diff --git a/src/core/authentication.agent.info/agent.info.ts b/src/core/authentication.agent.info/agent.info.ts index 8d1716f2b8..dcf68b4c60 100644 --- a/src/core/authentication.agent.info/agent.info.ts +++ b/src/core/authentication.agent.info/agent.info.ts @@ -1,6 +1,6 @@ +import { AuthenticationType } from '@common/enums/authentication.type'; import { ICredential } from '@domain/agent/credential'; import { IVerifiedCredential } from '@domain/agent/verified-credential/verified.credential.interface'; - export class AgentInfo { userID = ''; email = ''; @@ -13,4 +13,5 @@ export class AgentInfo { agentID = ''; avatarURL = ''; expiry?: number = undefined; + authenticationType?: AuthenticationType; } diff --git a/src/core/authentication/authentication.service.ts b/src/core/authentication/authentication.service.ts index 34e064be6b..ee10b5a86d 100644 --- a/src/core/authentication/authentication.service.ts +++ b/src/core/authentication/authentication.service.ts @@ -5,6 +5,8 @@ import { FrontendApi, IdentityApi, Session, + SessionAuthenticationMethod, + SessionAuthenticationMethodMethodEnum, } from '@ory/kratos-client'; import { LogContext } from '@common/enums'; import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; @@ -20,6 +22,7 @@ import { AgentInfoCacheService } from '@core/authentication.agent.info/agent.inf import { getSession } from '@common/utils'; import ConfigUtils from '@config/config.utils'; import { AlkemioConfig } from '@src/types'; +import { AuthenticationType } from '@common/enums/authentication.type'; @Injectable() export class AuthenticationService { @@ -122,6 +125,21 @@ export class AuthenticationService { agentInfo.expiry = session?.expires_at ? new Date(session.expires_at).getTime() : undefined; + const authenticationMethod: SessionAuthenticationMethod | undefined = + session?.authentication_methods?.[0]; + const provider: string | undefined = authenticationMethod?.provider; + const method: SessionAuthenticationMethodMethodEnum | undefined = + authenticationMethod?.method; + + agentInfo.authenticationType = + provider === 'microsoft' + ? AuthenticationType.MICROSOFT + : provider === 'linkedin' + ? AuthenticationType.LINKEDIN + : method === 'password' + ? AuthenticationType.EMAIL + : AuthenticationType.UNKNOWN; + let agentInfoMetadata; try { diff --git a/src/domain/community/user/user.resolver.fields.ts b/src/domain/community/user/user.resolver.fields.ts index a60d8033a0..a15219d0ae 100644 --- a/src/domain/community/user/user.resolver.fields.ts +++ b/src/domain/community/user/user.resolver.fields.ts @@ -32,6 +32,7 @@ import { Loader } from '@core/dataloader/decorators'; import { IStorageAggregator } from '@domain/storage/storage-aggregator/storage.aggregator.interface'; import { IAccount } from '@domain/space/account/account.interface'; import { User } from './user.entity'; +import { AuthenticationType } from '@common/enums/authentication.type'; @Resolver(() => IUser) export class UserResolverFields { @@ -245,6 +246,19 @@ export class UserResolverFields { return loader.load(user.id); } + @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) + @ResolveField('authenticationMethod', () => AuthenticationType, { + nullable: true, + description: + 'The Authentication Method used for this User. One of email, linkedin, microsoft, or unknown', + }) + @UseGuards(GraphqlGuard) + authenticationMethod( + @CurrentUser() agentInfo: AgentInfo + ): AuthenticationType { + return agentInfo.authenticationType ?? AuthenticationType.UNKNOWN; + } + private async isAccessGranted( user: IUser, agentInfo: AgentInfo, From e4a51456069b05681f19ee86277acc8981485752 Mon Sep 17 00:00:00 2001 From: Valentin Yanakiev Date: Fri, 4 Oct 2024 10:55:59 +0300 Subject: [PATCH 69/78] Refactor --- .../authentication/authentication.service.ts | 150 +++++++++++------- .../ory.default.identity.schema.ts | 24 +-- 2 files changed, 102 insertions(+), 72 deletions(-) diff --git a/src/core/authentication/authentication.service.ts b/src/core/authentication/authentication.service.ts index ee10b5a86d..08c23f3eb1 100644 --- a/src/core/authentication/authentication.service.ts +++ b/src/core/authentication/authentication.service.ts @@ -5,14 +5,15 @@ import { FrontendApi, IdentityApi, Session, - SessionAuthenticationMethod, - SessionAuthenticationMethodMethodEnum, } from '@ory/kratos-client'; import { LogContext } from '@common/enums'; import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; import { UserService } from '@domain/community/user/user.service'; import { AgentInfo } from '../authentication.agent.info/agent.info'; -import { OryDefaultIdentitySchema } from './ory.default.identity.schema'; +import { + OryDefaultIdentitySchema, + OryTraits, +} from './ory.default.identity.schema'; import { NotSupportedException } from '@common/exceptions'; import { AgentService } from '@domain/agent/agent/agent.service'; import { getBearerToken } from './get.bearer.token'; @@ -23,6 +24,7 @@ import { getSession } from '@common/utils'; import ConfigUtils from '@config/config.utils'; import { AlkemioConfig } from '@src/types'; import { AuthenticationType } from '@common/enums/authentication.type'; +import { AgentInfoMetadata } from '@core/authentication.agent.info/agent.info.metadata'; @Injectable() export class AuthenticationService { @@ -95,11 +97,31 @@ export class AuthenticationService { oryIdentity?: OryDefaultIdentitySchema, session?: Session ): Promise { - const agentInfo = new AgentInfo(); - if (!oryIdentity) { - return agentInfo; - } + if (!oryIdentity) return new AgentInfo(); + + const oryTraits = this.validateEmail(oryIdentity); + + const cachedAgentInfo = await this.getCachedAgentInfo(oryTraits.email); + if (cachedAgentInfo) return cachedAgentInfo; + + const agentInfo = this.buildBasicAgentInfo(oryIdentity, session); + agentInfo.authenticationType = this.mapAuthenticationType(session); + + const agentInfoMetadata = await this.getAgentInfoMetadata(agentInfo.email); + if (!agentInfoMetadata) return agentInfo; + + this.populateAgentInfoWithMetadata(agentInfo, agentInfoMetadata); + await this.addVerifiedCredentialsIfEnabled( + agentInfo, + agentInfoMetadata.agentID + ); + + await this.agentCacheService.setAgentInfoCache(agentInfo); + return agentInfo; + } + // Helper to validate the existence of email and return traits + private validateEmail(oryIdentity: OryDefaultIdentitySchema): OryTraits { const oryTraits = oryIdentity.traits; if (!oryTraits.email || oryTraits.email.length === 0) { throw new NotSupportedException( @@ -107,16 +129,27 @@ export class AuthenticationService { LogContext.AUTH ); } + return oryTraits; + } - const cachedAgentInfo = await this.agentCacheService.getAgentInfoFromCache( - oryTraits.email - ); - if (cachedAgentInfo) return cachedAgentInfo; + // Helper to retrieve agent info from cache + private async getCachedAgentInfo( + email: string + ): Promise { + return await this.agentCacheService.getAgentInfoFromCache(email); + } + // Helper to build basic agent info from identity and session + private buildBasicAgentInfo( + oryIdentity: OryDefaultIdentitySchema, + session?: Session + ): AgentInfo { + const agentInfo = new AgentInfo(); + const oryTraits = oryIdentity.traits; const isEmailVerified = oryIdentity.verifiable_addresses.find(x => x.via === 'email')?.verified ?? false; - // Have a valid identity, get the information from Ory + agentInfo.email = oryTraits.email; agentInfo.emailVerified = isEmailVerified; agentInfo.firstName = oryTraits.name.first; @@ -125,73 +158,68 @@ export class AuthenticationService { agentInfo.expiry = session?.expires_at ? new Date(session.expires_at).getTime() : undefined; - const authenticationMethod: SessionAuthenticationMethod | undefined = - session?.authentication_methods?.[0]; - const provider: string | undefined = authenticationMethod?.provider; - const method: SessionAuthenticationMethodMethodEnum | undefined = - authenticationMethod?.method; - - agentInfo.authenticationType = - provider === 'microsoft' - ? AuthenticationType.MICROSOFT - : provider === 'linkedin' - ? AuthenticationType.LINKEDIN - : method === 'password' - ? AuthenticationType.EMAIL - : AuthenticationType.UNKNOWN; - - let agentInfoMetadata; + return agentInfo; + } + + // Helper to map authentication type from session + private mapAuthenticationType(session?: Session): AuthenticationType { + const authenticationMethod = session?.authentication_methods?.[0]; + const provider = authenticationMethod?.provider; + const method = authenticationMethod?.method; + + if (provider === 'microsoft') return AuthenticationType.MICROSOFT; + if (provider === 'linkedin') return AuthenticationType.LINKEDIN; + if (method === 'password') return AuthenticationType.EMAIL; + + return AuthenticationType.UNKNOWN; + } + + // Helper to retrieve agent info metadata and handle logging + private async getAgentInfoMetadata( + email: string + ): Promise { try { - agentInfoMetadata = await this.userService.getAgentInfoMetadata( - agentInfo.email - ); + return await this.userService.getAgentInfoMetadata(email); } catch (error) { this.logger.verbose?.( - `User not registered: ${agentInfo.email}, ${error}`, + `User not registered: ${email}, ${error}`, LogContext.AUTH ); + return undefined; } + } - if (!agentInfoMetadata) { - this.logger.verbose?.( - `User: no profile: ${agentInfo.email}`, - LogContext.AUTH - ); - // No credentials to obtain, pass on what is there - return agentInfo; - } - this.logger.verbose?.( - `Use: registered: ${agentInfo.email}`, - LogContext.AUTH - ); + // Helper to populate agent info with metadata + private populateAgentInfoWithMetadata( + agentInfo: AgentInfo, + agentInfoMetadata: AgentInfoMetadata + ): void { + agentInfo.agentID = agentInfoMetadata.agentID; + agentInfo.userID = agentInfoMetadata.userID; + agentInfo.communicationID = agentInfoMetadata.communicationID; - if (!agentInfoMetadata.credentials) { + if (agentInfoMetadata.credentials) { + agentInfo.credentials = agentInfoMetadata.credentials; + } else { this.logger.warn?.( `Authentication Info: Unable to retrieve credentials for registered user: ${agentInfo.email}`, LogContext.AUTH ); - } else { - agentInfo.credentials = agentInfoMetadata.credentials; } - agentInfo.agentID = agentInfoMetadata.agentID; - agentInfo.userID = agentInfoMetadata.userID; - agentInfo.communicationID = agentInfoMetadata.communicationID; + } - // Store also retrieved verified credentials; todo: likely slow, need to evaluate other options + // Helper to add verified credentials if SSI is enabled + private async addVerifiedCredentialsIfEnabled( + agentInfo: AgentInfo, + agentID: string + ): Promise { const ssiEnabled = this.configService.get('ssi.enabled', { infer: true }); - if (ssiEnabled) { - const VCs = await this.agentService.getVerifiedCredentials( - agentInfoMetadata.agentID - ); - - agentInfo.verifiedCredentials = VCs; + const verifiedCredentials = + await this.agentService.getVerifiedCredentials(agentID); + agentInfo.verifiedCredentials = verifiedCredentials; } - - await this.agentCacheService.setAgentInfoCache(agentInfo); - - return agentInfo; } public async extendSession(sessionToBeExtended: Session): Promise { diff --git a/src/core/authentication/ory.default.identity.schema.ts b/src/core/authentication/ory.default.identity.schema.ts index e5f6ef4d4e..316142e923 100644 --- a/src/core/authentication/ory.default.identity.schema.ts +++ b/src/core/authentication/ory.default.identity.schema.ts @@ -10,21 +10,13 @@ export interface OryDefaultIdentitySchema extends Identity { updated_at: string; //UTC Zulu time value: string; via: 'email'; - } + }, ]; schema_id: 'default' | 'customer' | 'employee'; schema_url: string; state: 'active'; state_changed_at: string; - traits: { - accepted_terms: boolean; - picture: string; - email: string; - name: { - first: string; - last: string; - }; - }; + traits: OryTraits; updated_at: string; //UTC Zulu time verifiable_addresses: [ { @@ -36,6 +28,16 @@ export interface OryDefaultIdentitySchema extends Identity { verified: boolean; verified_at: string; //UTC Zulu time via: 'email'; - } + }, ]; } + +export interface OryTraits { + accepted_terms: boolean; + picture: string; + email: string; + name: { + first: string; + last: string; + }; +} From 947cff71b24958c84e829fff076bd931691d1923 Mon Sep 17 00:00:00 2001 From: Valentin Yanakiev Date: Fri, 4 Oct 2024 10:59:58 +0300 Subject: [PATCH 70/78] Added documentation --- .../authentication/authentication.service.ts | 80 +++++++++++++++++-- 1 file changed, 73 insertions(+), 7 deletions(-) diff --git a/src/core/authentication/authentication.service.ts b/src/core/authentication/authentication.service.ts index 08c23f3eb1..5c0b2236a4 100644 --- a/src/core/authentication/authentication.service.ts +++ b/src/core/authentication/authentication.service.ts @@ -93,6 +93,23 @@ export class AuthenticationService { return this.createAgentInfo(oryIdentity); } + /** + * Creates and returns an `AgentInfo` object based on the provided Ory identity and session. + * + * @param oryIdentity - Optional Ory identity schema containing user traits. + * @param session - Optional session information. + * @returns A promise that resolves to an `AgentInfo` object. + * + * This method performs the following steps: + * 1. Validates the provided Ory identity. + * 2. Checks for cached agent information based on the email from the Ory identity. + * 3. Builds basic agent information if no cached information is found. + * 4. Maps the authentication type from the session. + * 5. Retrieves additional metadata for the agent. + * 6. Populates the agent information with the retrieved metadata. + * 7. Adds verified credentials if enabled. + * 8. Caches the agent information. + */ async createAgentInfo( oryIdentity?: OryDefaultIdentitySchema, session?: Session @@ -120,7 +137,13 @@ export class AuthenticationService { return agentInfo; } - // Helper to validate the existence of email and return traits + /** + * Validates the email trait of the provided Ory identity. + * + * @param oryIdentity - The Ory identity schema containing traits to be validated. + * @returns The validated Ory traits. + * @throws NotSupportedException - If the email trait is missing or empty. + */ private validateEmail(oryIdentity: OryDefaultIdentitySchema): OryTraits { const oryTraits = oryIdentity.traits; if (!oryTraits.email || oryTraits.email.length === 0) { @@ -132,14 +155,25 @@ export class AuthenticationService { return oryTraits; } - // Helper to retrieve agent info from cache + /** + * Retrieves the cached agent information for a given email. + * + * @param email - The email address of the agent. + * @returns A promise that resolves to the agent information if found in the cache, or undefined if not found. + */ private async getCachedAgentInfo( email: string ): Promise { return await this.agentCacheService.getAgentInfoFromCache(email); } - // Helper to build basic agent info from identity and session + /** + * Builds and returns an `AgentInfo` object based on the provided Ory identity schema and session. + * + * @param oryIdentity - The Ory identity schema containing user traits and verifiable addresses. + * @param session - Optional session object containing session details like expiration time. + * @returns An `AgentInfo` object populated with the user's email, name, avatar URL, and session expiry. + */ private buildBasicAgentInfo( oryIdentity: OryDefaultIdentitySchema, session?: Session @@ -162,7 +196,17 @@ export class AuthenticationService { return agentInfo; } - // Helper to map authentication type from session + /** + * Maps the authentication type based on the provided session information. + * + * @param session - The session object containing authentication methods. + * @returns The corresponding `AuthenticationType` based on the provider and method. + * + * - If the provider is 'microsoft', returns `AuthenticationType.MICROSOFT`. + * - If the provider is 'linkedin', returns `AuthenticationType.LINKEDIN`. + * - If the method is 'password', returns `AuthenticationType.EMAIL`. + * - Otherwise, returns `AuthenticationType.UNKNOWN`. + */ private mapAuthenticationType(session?: Session): AuthenticationType { const authenticationMethod = session?.authentication_methods?.[0]; const provider = authenticationMethod?.provider; @@ -175,7 +219,13 @@ export class AuthenticationService { return AuthenticationType.UNKNOWN; } - // Helper to retrieve agent info metadata and handle logging + /** + * Retrieves the agent information metadata for a given email. + * + * @param email - The email address of the user whose agent information metadata is to be retrieved. + * @returns A promise that resolves to the agent information metadata if found, or undefined if the user is not registered. + * @throws Will log an error message if the user is not registered. + */ private async getAgentInfoMetadata( email: string ): Promise { @@ -190,7 +240,17 @@ export class AuthenticationService { } } - // Helper to populate agent info with metadata + /** + * Populates the given `agentInfo` object with metadata from `agentInfoMetadata`. + * + * @param agentInfo - The agent information object to be populated. + * @param agentInfoMetadata - The metadata containing information to populate the agent info. + * + * @remarks + * This method assigns the `agentID`, `userID`, and `communicationID` from `agentInfoMetadata` to `agentInfo`. + * If `agentInfoMetadata` contains credentials, they are also assigned to `agentInfo`. + * If credentials are not available, a warning is logged. + */ private populateAgentInfoWithMetadata( agentInfo: AgentInfo, agentInfoMetadata: AgentInfoMetadata @@ -209,7 +269,13 @@ export class AuthenticationService { } } - // Helper to add verified credentials if SSI is enabled + /** + * Adds verified credentials to the agent information if SSI (Self-Sovereign Identity) is enabled. + * + * @param agentInfo - The information of the agent to which verified credentials will be added. + * @param agentID - The unique identifier of the agent. + * @returns A promise that resolves when the operation is complete. + */ private async addVerifiedCredentialsIfEnabled( agentInfo: AgentInfo, agentID: string From aaed152c32ac48afd5cefef072d31a756e68af8d Mon Sep 17 00:00:00 2001 From: Valentin Yanakiev Date: Fri, 4 Oct 2024 12:24:35 +0300 Subject: [PATCH 71/78] Fixed migrations on clean / prod db schema --- .../1727872794564-cleanupRolesIndexes.ts | 73 +- ...728032163157-dropAllIndexesFKsRelations.ts | 103 + .../1728033222394-fixIndexesFKsRelations-1.ts | 2561 +++++++++++++++++ .../1728033296139-fixIndexesFKsRelations-2.ts | 779 +++++ 4 files changed, 3494 insertions(+), 22 deletions(-) create mode 100644 src/migrations/1728032163157-dropAllIndexesFKsRelations.ts create mode 100644 src/migrations/1728033222394-fixIndexesFKsRelations-1.ts create mode 100644 src/migrations/1728033296139-fixIndexesFKsRelations-2.ts diff --git a/src/migrations/1727872794564-cleanupRolesIndexes.ts b/src/migrations/1727872794564-cleanupRolesIndexes.ts index 445281aac7..147b9cdc74 100644 --- a/src/migrations/1727872794564-cleanupRolesIndexes.ts +++ b/src/migrations/1727872794564-cleanupRolesIndexes.ts @@ -1,4 +1,7 @@ import { MigrationInterface, QueryRunner } from 'typeorm'; +import { safelyDropFK } from './utils/safely-drop-foreignKey'; +import { safelyDropIndex } from './utils/safely-drop-index'; +import { safelyAddFK } from './utils/safely-add-foreignKey'; export class cleanupRolesIndexes1727872794564 implements MigrationInterface { name = 'cleanupRolesIndexes1727872794564'; @@ -6,40 +9,66 @@ export class cleanupRolesIndexes1727872794564 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { // platform_invitation // This is the new FK using the old index - await queryRunner.query( - `ALTER TABLE \`platform_invitation\` DROP FOREIGN KEY \`FK_562dce4a08bb214f08107b3631e\`` + safelyDropFK( + queryRunner, + 'platform_invitation', + 'FK_562dce4a08bb214f08107b3631e' ); + // This is the old index that needs to be removed - await queryRunner.query( - `DROP INDEX \`FK_b3d3f3c2ce851d1059c4ed26ba2\` ON \`platform_invitation\` - ` + safelyDropIndex( + queryRunner, + 'platform_invitation', + 'FK_b3d3f3c2ce851d1059c4ed26ba2' ); + // This will add the index and the FK back - await queryRunner.query( - `ALTER TABLE \`platform_invitation\` ADD CONSTRAINT \`FK_562dce4a08bb214f08107b3631e\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION - ` + safelyAddFK( + queryRunner, + 'platform_invitation', + 'FK_562dce4a08bb214f08107b3631e', + 'roleSetId', + 'role_set', + 'id', + 'CASCADE', + 'NO ACTION' ); // application - await queryRunner.query( - `ALTER TABLE \`application\` DROP FOREIGN KEY \`FK_8fb220ad1ac1f9c86ec39d134e4\`` - ); - await queryRunner.query( - `DROP INDEX \`FK_500cee6f635849f50e19c7e2b76\` ON \`application\`` + + safelyDropFK(queryRunner, 'application', 'FK_8fb220ad1ac1f9c86ec39d134e4'); + safelyDropIndex( + queryRunner, + 'application', + 'FK_500cee6f635849f50e19c7e2b76' ); - await queryRunner.query( - `ALTER TABLE \`application\` ADD CONSTRAINT \`FK_8fb220ad1ac1f9c86ec39d134e4\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + safelyAddFK( + queryRunner, + 'application', + 'FK_8fb220ad1ac1f9c86ec39d134e4', + 'roleSetId', + 'role_set', + 'id', + 'CASCADE', + 'NO ACTION' ); // invitation - await queryRunner.query( - `ALTER TABLE \`invitation\` DROP FOREIGN KEY \`FK_6a3b86c6db10582baae7058f5b9\`` + safelyDropFK(queryRunner, 'invitation', 'FK_6a3b86c6db10582baae7058f5b9'); + safelyDropIndex( + queryRunner, + 'invitation', + 'FK_339c1fe2a9c5caef5b982303fb0' ); - await queryRunner.query( - `DROP INDEX \`FK_339c1fe2a9c5caef5b982303fb0\` ON \`invitation\`` - ); - await queryRunner.query( - `ALTER TABLE \`invitation\` ADD CONSTRAINT \`FK_6a3b86c6db10582baae7058f5b9\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + safelyAddFK( + queryRunner, + 'invitation', + 'FK_6a3b86c6db10582baae7058f5b9', + 'roleSetId', + 'role_set', + 'id', + 'CASCADE', + 'NO ACTION' ); } diff --git a/src/migrations/1728032163157-dropAllIndexesFKsRelations.ts b/src/migrations/1728032163157-dropAllIndexesFKsRelations.ts new file mode 100644 index 0000000000..8684ddebbf --- /dev/null +++ b/src/migrations/1728032163157-dropAllIndexesFKsRelations.ts @@ -0,0 +1,103 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DropAllIndexesFKsRelations1728032163157 + implements MigrationInterface +{ + name = 'DropAllIndexesFKsRelations1728032163157'; + + public async up(queryRunner: QueryRunner): Promise { + const dbName = 'alkemio'; // Your database name + + // 1. Drop all foreign keys that can be deleted + const foreignKeysResult = await queryRunner.query(` + SELECT TABLE_NAME, CONSTRAINT_NAME + FROM information_schema.KEY_COLUMN_USAGE + WHERE CONSTRAINT_SCHEMA = '${dbName}' + AND REFERENCED_TABLE_NAME IS NOT NULL; + `); + + for (const fk of foreignKeysResult) { + try { + await queryRunner.query( + `ALTER TABLE \`${fk.TABLE_NAME}\` DROP FOREIGN KEY \`${fk.CONSTRAINT_NAME}\`` + ); + console.log( + `Dropped foreign key ${fk.CONSTRAINT_NAME} on table ${fk.TABLE_NAME}` + ); + } catch (error) { + console.error( + `Could not drop foreign key ${fk.CONSTRAINT_NAME} on table ${fk.TABLE_NAME}:`, + error + ); + continue; + } + } + + // 2. Drop all unique constraints that can be deleted + const uniqueConstraintsResult = await queryRunner.query(` + SELECT TABLE_NAME, CONSTRAINT_NAME + FROM information_schema.TABLE_CONSTRAINTS + WHERE CONSTRAINT_SCHEMA = '${dbName}' + AND CONSTRAINT_TYPE = 'UNIQUE'; + `); + + for (const unique of uniqueConstraintsResult) { + try { + await queryRunner.query( + `ALTER TABLE \`${unique.TABLE_NAME}\` DROP INDEX \`${unique.CONSTRAINT_NAME}\`` + ); + console.log( + `Dropped unique constraint ${unique.CONSTRAINT_NAME} on table ${unique.TABLE_NAME}` + ); + } catch (error) { + console.error( + `Could not drop unique constraint ${unique.CONSTRAINT_NAME} on table ${unique.TABLE_NAME}:`, + error + ); + continue; + } + } + + // 3. Drop all regular indexes (except primary keys and indexes on AUTO_INCREMENT columns) + const indexesResult = await queryRunner.query(` + SELECT s.TABLE_NAME, s.INDEX_NAME, c.COLUMN_NAME, MAX(c.EXTRA) AS EXTRA + FROM information_schema.STATISTICS s + JOIN information_schema.COLUMNS c + ON s.TABLE_NAME = c.TABLE_NAME + AND s.TABLE_SCHEMA = c.TABLE_SCHEMA + AND s.COLUMN_NAME = c.COLUMN_NAME + WHERE s.TABLE_SCHEMA = '${dbName}' + AND s.INDEX_NAME != 'PRIMARY' + GROUP BY s.TABLE_NAME, s.INDEX_NAME, c.COLUMN_NAME + `); + + for (const index of indexesResult) { + if (index.EXTRA && index.EXTRA.includes('auto_increment')) { + console.log( + `Skipping index ${index.INDEX_NAME} on table ${index.TABLE_NAME} (AUTO_INCREMENT column)` + ); + continue; + } + + // Try to drop the index + try { + await queryRunner.query( + `DROP INDEX \`${index.INDEX_NAME}\` ON \`${index.TABLE_NAME}\`` + ); + console.log( + `Dropped index ${index.INDEX_NAME} on table ${index.TABLE_NAME}` + ); + } catch (error) { + console.error( + `Could not drop index ${index.INDEX_NAME} on table ${index.TABLE_NAME}:`, + error + ); + continue; + } + } + } + + public async down(queryRunner: QueryRunner): Promise { + // Logic for down migration can be added here if needed + } +} diff --git a/src/migrations/1728033222394-fixIndexesFKsRelations-1.ts b/src/migrations/1728033222394-fixIndexesFKsRelations-1.ts new file mode 100644 index 0000000000..12f4e0d62d --- /dev/null +++ b/src/migrations/1728033222394-fixIndexesFKsRelations-1.ts @@ -0,0 +1,2561 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FixIndexesFKsRelations1728033222394 implements MigrationInterface { + name = 'FixIndexesFKsRelations1728033222394'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE \`tagset\` ADD UNIQUE INDEX \`IDX_eb59b98ee6ef26c993d0d75c83\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`visual\` ADD UNIQUE INDEX \`IDX_4fbd109f9bb84f58b7a3c60649\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`document\` ADD UNIQUE INDEX \`IDX_d9e2dfcccf59233c17cc6bc641\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`document\` ADD UNIQUE INDEX \`IDX_9fb9257b14ec21daf5bc9aa4c8\` (\`tagsetId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`storage_aggregator\` ADD UNIQUE INDEX \`IDX_f3b4d59c0b805c9c1ecb0070e1\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`storage_aggregator\` ADD UNIQUE INDEX \`IDX_0647707288c243e60091c8d862\` (\`directStorageId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`storage_bucket\` ADD UNIQUE INDEX \`IDX_f2f48b57269987b13b415a0058\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`profile\` ADD UNIQUE INDEX \`IDX_a96475631aba7dce41db03cc8b\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`profile\` ADD UNIQUE INDEX \`IDX_432056041df0e4337b17ff7b09\` (\`locationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`profile\` ADD UNIQUE INDEX \`IDX_4a1c74fd2a61b32d9d9500e065\` (\`storageBucketId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`reference\` ADD UNIQUE INDEX \`IDX_73e8ae665a49366ca7e2866a45\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`room\` ADD UNIQUE INDEX \`IDX_d1d94dd8e0c417b4188a05ccbc\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`whiteboard\` ADD UNIQUE INDEX \`IDX_d3b86160bb7d704212382b0ca4\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`whiteboard\` ADD UNIQUE INDEX \`IDX_3f9e9e2798d2a4d84b16ee8477\` (\`profileId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`callout_framing\` ADD UNIQUE INDEX \`IDX_c9d7c2c4eb8a1d012ddc6605da\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`callout_framing\` ADD UNIQUE INDEX \`IDX_f53e2d266432e58e538a366705\` (\`profileId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`callout_framing\` ADD UNIQUE INDEX \`IDX_8bc0e1f40be5816d3a593cbf7f\` (\`whiteboardId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`post\` ADD UNIQUE INDEX \`IDX_390343b22abec869bf80041933\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`post\` ADD UNIQUE INDEX \`IDX_970844fcd10c2b6df7c1b49eac\` (\`profileId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`post\` ADD UNIQUE INDEX \`IDX_042b9825d770d6b3009ae206c2\` (\`commentsId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`link\` ADD UNIQUE INDEX \`IDX_07f249ac87502495710a62c5c0\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`link\` ADD UNIQUE INDEX \`IDX_3bfc8c1aaec1395cc148268d3c\` (\`profileId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` ADD UNIQUE INDEX \`IDX_dfa86c46f509a61c6510536cd9\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` ADD UNIQUE INDEX \`IDX_5e34f9a356f6254b8da24f8947\` (\`whiteboardId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` ADD UNIQUE INDEX \`IDX_97fefc97fb254c30577696e1c0\` (\`postId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` ADD UNIQUE INDEX \`IDX_bdf2d0eced5c95968a85caaaae\` (\`linkId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` ADD UNIQUE INDEX \`IDX_6289dee12effb51320051c6f1f\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` ADD UNIQUE INDEX \`IDX_cf776244b01436d8ca5cc76284\` (\`framingId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` ADD UNIQUE INDEX \`IDX_1e740008a7e1512966e3b08414\` (\`contributionPolicyId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` ADD UNIQUE INDEX \`IDX_36b0da55acff774d0845aeb55f\` (\`contributionDefaultsId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` ADD UNIQUE INDEX \`IDX_62ed316cda7b75735b20307b47\` (\`commentsId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`calendar_event\` ADD UNIQUE INDEX \`IDX_8ee86afa2808a4ab523b9ee6c5\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`calendar_event\` ADD UNIQUE INDEX \`IDX_9349e137959f3ca5818c2e62b3\` (\`profileId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`calendar_event\` ADD UNIQUE INDEX \`IDX_b5069b11030e9608ee4468f850\` (\`commentsId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`calendar\` ADD UNIQUE INDEX \`IDX_6e74d59afda096b68d12a69969\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`timeline\` ADD UNIQUE INDEX \`IDX_5fe58ece01b48496aebc04733d\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`timeline\` ADD UNIQUE INDEX \`IDX_56aae15a664b2889a1a11c2cf8\` (\`calendarId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_flow\` ADD UNIQUE INDEX \`IDX_a6e050daa4c7a3ab1e411c3651\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_flow\` ADD UNIQUE INDEX \`IDX_96a8cbe1706f459fd7d883be9b\` (\`profileId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`collaboration\` ADD UNIQUE INDEX \`IDX_262ecf3f5d70b82a4833618425\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`collaboration\` ADD UNIQUE INDEX \`IDX_b7ece56376ac7ca0b9a56c33b3\` (\`tagsetTemplateSetId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`collaboration\` ADD UNIQUE INDEX \`IDX_f67a2d25c945269d602c182fbc\` (\`timelineId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`collaboration\` ADD UNIQUE INDEX \`IDX_35c6b1de6d4d89dfe8e9c85d77\` (\`innovationFlowId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`organization_verification\` ADD UNIQUE INDEX \`IDX_c66eddab0caacb1ef8d46bcafd\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`organization_verification\` ADD UNIQUE INDEX \`IDX_1cc3b275fc2a9d9d9b0ae33b31\` (\`lifecycleId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`preference\` ADD UNIQUE INDEX \`IDX_b4cf0f96bf08cf396f68355522\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`preference_set\` ADD UNIQUE INDEX \`IDX_8e76dcf171c45875c44febb1d8\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`agent\` ADD UNIQUE INDEX \`IDX_8ed9d1af584fa62f1ad3405b33\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` ADD UNIQUE INDEX \`IDX_e0e150e4f11d906b931b46a2d8\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` ADD UNIQUE INDEX \`IDX_d2cb77c14644156ec8e865608e\` (\`profileId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` ADD UNIQUE INDEX \`IDX_7f1bec8979b57ed7ebd392a2ca\` (\`agentId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` ADD UNIQUE INDEX \`IDX_5a72d5b37312bac2e0a0115718\` (\`verificationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` ADD UNIQUE INDEX \`IDX_58fd47c4a6ac8df9fe2bcaed87\` (\`preferenceSetId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` ADD UNIQUE INDEX \`IDX_395aa74996a1f978b4969d114b\` (\`storageAggregatorId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`user_group\` ADD UNIQUE INDEX \`IDX_e8e32f1e59c349b406a4752e54\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`user_group\` ADD UNIQUE INDEX \`IDX_9912e4cfc1e09848a392a65151\` (\`profileId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`communication\` ADD UNIQUE INDEX \`IDX_a20c5901817dd09d5906537e08\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`communication\` ADD UNIQUE INDEX \`IDX_eb99e588873c788a68a035478a\` (\`updatesId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`community_guidelines\` ADD UNIQUE INDEX \`IDX_684b272e6f7459439d41d2879e\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`community_guidelines\` ADD UNIQUE INDEX \`IDX_3d60fe4fa40d54bad7d51bb4bd\` (\`profileId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`library\` ADD UNIQUE INDEX \`IDX_3879db652f2421337691219ace\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`license_policy\` ADD UNIQUE INDEX \`IDX_23d4d78ea8db637df031f86f03\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`licensing\` ADD UNIQUE INDEX \`IDX_0c6a4d0a6c13a3f5df6ac01509\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`licensing\` ADD UNIQUE INDEX \`IDX_a5dae5a376dd49c7c076893d40\` (\`licensePolicyId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`discussion\` ADD UNIQUE INDEX \`IDX_4555dccdda9ba57d8e3a634cd0\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`discussion\` ADD UNIQUE INDEX \`IDX_2d8a3ca181c3f0346817685d21\` (\`profileId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`discussion\` ADD UNIQUE INDEX \`IDX_5337074c9b818bb63e6f314c80\` (\`commentsId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`forum\` ADD UNIQUE INDEX \`IDX_3b0c92945f36d06f37de80285d\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` ADD UNIQUE INDEX \`IDX_9f621c51dd854634d8766a9cfa\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` ADD UNIQUE INDEX \`IDX_dd88d373c64b04e24705d575c9\` (\`forumId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` ADD UNIQUE INDEX \`IDX_ca469f5ec53a7719d155d60aca\` (\`libraryId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` ADD UNIQUE INDEX \`IDX_f516dd9a46616999c7e9a6adc1\` (\`storageAggregatorId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` ADD UNIQUE INDEX \`IDX_425bbb4b951f7f4629710763fc\` (\`licensingId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` ADD UNIQUE INDEX \`IDX_c0448d2c992a62c9c11bd0f142\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`user\` ADD UNIQUE INDEX \`IDX_09f909622aa177a097256b7cc2\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`user\` ADD UNIQUE INDEX \`IDX_9466682df91534dd95e4dbaa61\` (\`profileId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`user\` ADD UNIQUE INDEX \`IDX_b61c694cacfab25533bd23d9ad\` (\`agentId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`user\` ADD UNIQUE INDEX \`IDX_028322b763dc94242dc9f638f9\` (\`preferenceSetId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`user\` ADD UNIQUE INDEX \`IDX_10458c50c10436b6d589b40e5c\` (\`storageAggregatorId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`application\` ADD UNIQUE INDEX \`IDX_56f5614fff0028d40370499582\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`application\` ADD UNIQUE INDEX \`IDX_7ec2857c7d8d16432ffca1cb3d\` (\`lifecycleId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` ADD UNIQUE INDEX \`IDX_b132226941570cb650a4023d49\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` ADD UNIQUE INDEX \`IDX_b0c80ccf319a1c7a7af12b3998\` (\`lifecycleId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`role_set\` ADD UNIQUE INDEX \`IDX_b038f74c8d4eadb839e78b99ce\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`role_set\` ADD UNIQUE INDEX \`IDX_00905b142498f63e76d38fb254\` (\`applicationFormId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`community\` ADD UNIQUE INDEX \`IDX_6e7584bfb417bd0f8e8696ab58\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`community\` ADD UNIQUE INDEX \`IDX_7fbe50fa78a37776ad962cb764\` (\`communicationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`community\` ADD UNIQUE INDEX \`IDX_2e7dd2fa8c829352cfbecb2cc9\` (\`guidelinesId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`community\` ADD UNIQUE INDEX \`IDX_3b8f390d76263ef5996869da07\` (\`roleSetId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`actor\` ADD UNIQUE INDEX \`IDX_a2afa3851ea733de932251b3a1\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`actor_group\` ADD UNIQUE INDEX \`IDX_bde98d59e8984e7d17034c3b93\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`ecosystem_model\` ADD UNIQUE INDEX \`IDX_658580aea4e1a892227e27db90\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`context\` ADD UNIQUE INDEX \`IDX_5f0dbc3b097ef297bd5f4ddb1a\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`context\` ADD UNIQUE INDEX \`IDX_a03169c3f86480ba3863924f4d\` (\`ecosystemModelId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`template\` ADD UNIQUE INDEX \`IDX_4318f97beabd362a8a09e9d320\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`template\` ADD UNIQUE INDEX \`IDX_f58c3b144b6e010969e199beef\` (\`profileId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`template\` ADD UNIQUE INDEX \`IDX_45cf273f30c1fa509456b6b0dd\` (\`innovationFlowId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`template\` ADD UNIQUE INDEX \`IDX_eedeae5e63f9a9c3a0161541e9\` (\`communityGuidelinesId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`template\` ADD UNIQUE INDEX \`IDX_c6e4d1a07781a809ad3b3ee826\` (\`calloutId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`template\` ADD UNIQUE INDEX \`IDX_f09090a77e07377eefb3f731d9\` (\`whiteboardId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`template\` ADD UNIQUE INDEX \`IDX_21fdaf6dc88bdd6e8839e29b0b\` (\`collaborationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`templates_set\` ADD UNIQUE INDEX \`IDX_eb0176ef4b98c143322aa6f809\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`space_defaults\` ADD UNIQUE INDEX \`IDX_413ba75964e5a534e4bfa54846\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`space_defaults\` ADD UNIQUE INDEX \`IDX_592a23e68922853bae6ebecd85\` (\`innovationFlowTemplateId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD UNIQUE INDEX \`IDX_8d03fd2c8e8411ec9192c79cd9\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD UNIQUE INDEX \`IDX_b4250035291aac1329d59224a9\` (\`profileId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD UNIQUE INDEX \`IDX_ea06eb8894469a0f262d929bf0\` (\`collaborationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD UNIQUE INDEX \`IDX_cc0b08eb9679d3daa95153c2af\` (\`contextId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD UNIQUE INDEX \`IDX_68fa2c2b00cc1ed77e7c225e8b\` (\`communityId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD UNIQUE INDEX \`IDX_9c664d684f987a735678b0ba82\` (\`agentId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD UNIQUE INDEX \`IDX_980c4643d7d9de1b97bc39f518\` (\`storageAggregatorId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD UNIQUE INDEX \`IDX_43559aeadc1a5169d17e81b3d4\` (\`libraryId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD UNIQUE INDEX \`IDX_6b1efee39d076d9f7ecb8fef4c\` (\`defaultsId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`ai_persona\` ADD UNIQUE INDEX \`IDX_293f0d3ef60cb0ca0006044ecf\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` ADD UNIQUE INDEX \`IDX_e2eaa2213ac4454039cd8abc07\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` ADD UNIQUE INDEX \`IDX_4504c37764f6962ccbd165a21d\` (\`profileId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` ADD UNIQUE INDEX \`IDX_a8890dcd65b8c3ee6e160d33f3\` (\`agentId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` ADD UNIQUE INDEX \`IDX_55b8101bdf4f566645e928c26e\` (\`aiPersonaId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_pack\` ADD UNIQUE INDEX \`IDX_8af8122897b05315e7eb892525\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_pack\` ADD UNIQUE INDEX \`IDX_5facd6d188068a5a1c5b6f07fc\` (\`profileId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_pack\` ADD UNIQUE INDEX \`IDX_a1441e46c8d36090e1f6477cea\` (\`templatesSetId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`account\` ADD UNIQUE INDEX \`IDX_91a165c1091a6959cc19d52239\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`account\` ADD UNIQUE INDEX \`IDX_833582df0c439ab8c9adc5240d\` (\`agentId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`account\` ADD UNIQUE INDEX \`IDX_950221e932175dc7cf7c006488\` (\`storageAggregatorId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_hub\` ADD UNIQUE INDEX \`IDX_1d39dac2c6d2f17286d90c306b\` (\`nameID\`)` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_hub\` ADD UNIQUE INDEX \`IDX_8f35d04d098bb6c7c57a9a83ac\` (\`subdomain\`)` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_hub\` ADD UNIQUE INDEX \`IDX_b411e4f27d77a96eccdabbf4b4\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_hub\` ADD UNIQUE INDEX \`IDX_36c8905c2c6c59467c60d94fd8\` (\`profileId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`ai_server\` ADD UNIQUE INDEX \`IDX_9d520fa5fed56042918e48fc4b\` (\`authorizationId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`ai_persona_service\` ADD UNIQUE INDEX \`IDX_79206feb0038b1c5597668dc4b\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_eb59b98ee6ef26c993d0d75c83\` ON \`tagset\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_4fbd109f9bb84f58b7a3c60649\` ON \`visual\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_d9e2dfcccf59233c17cc6bc641\` ON \`document\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_9fb9257b14ec21daf5bc9aa4c8\` ON \`document\` (\`tagsetId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_f3b4d59c0b805c9c1ecb0070e1\` ON \`storage_aggregator\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_0647707288c243e60091c8d862\` ON \`storage_aggregator\` (\`directStorageId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_f2f48b57269987b13b415a0058\` ON \`storage_bucket\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_a96475631aba7dce41db03cc8b\` ON \`profile\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_432056041df0e4337b17ff7b09\` ON \`profile\` (\`locationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_4a1c74fd2a61b32d9d9500e065\` ON \`profile\` (\`storageBucketId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_73e8ae665a49366ca7e2866a45\` ON \`reference\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_d1d94dd8e0c417b4188a05ccbc\` ON \`room\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_d3b86160bb7d704212382b0ca4\` ON \`whiteboard\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_3f9e9e2798d2a4d84b16ee8477\` ON \`whiteboard\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_c9d7c2c4eb8a1d012ddc6605da\` ON \`callout_framing\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_f53e2d266432e58e538a366705\` ON \`callout_framing\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_8bc0e1f40be5816d3a593cbf7f\` ON \`callout_framing\` (\`whiteboardId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_390343b22abec869bf80041933\` ON \`post\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_970844fcd10c2b6df7c1b49eac\` ON \`post\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_042b9825d770d6b3009ae206c2\` ON \`post\` (\`commentsId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_07f249ac87502495710a62c5c0\` ON \`link\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_3bfc8c1aaec1395cc148268d3c\` ON \`link\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_dfa86c46f509a61c6510536cd9\` ON \`callout_contribution\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_5e34f9a356f6254b8da24f8947\` ON \`callout_contribution\` (\`whiteboardId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_97fefc97fb254c30577696e1c0\` ON \`callout_contribution\` (\`postId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_bdf2d0eced5c95968a85caaaae\` ON \`callout_contribution\` (\`linkId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_6289dee12effb51320051c6f1f\` ON \`callout\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_cf776244b01436d8ca5cc76284\` ON \`callout\` (\`framingId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_1e740008a7e1512966e3b08414\` ON \`callout\` (\`contributionPolicyId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_36b0da55acff774d0845aeb55f\` ON \`callout\` (\`contributionDefaultsId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_62ed316cda7b75735b20307b47\` ON \`callout\` (\`commentsId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_8ee86afa2808a4ab523b9ee6c5\` ON \`calendar_event\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_9349e137959f3ca5818c2e62b3\` ON \`calendar_event\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_b5069b11030e9608ee4468f850\` ON \`calendar_event\` (\`commentsId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_6e74d59afda096b68d12a69969\` ON \`calendar\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_5fe58ece01b48496aebc04733d\` ON \`timeline\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_56aae15a664b2889a1a11c2cf8\` ON \`timeline\` (\`calendarId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_a6e050daa4c7a3ab1e411c3651\` ON \`innovation_flow\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_96a8cbe1706f459fd7d883be9b\` ON \`innovation_flow\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_262ecf3f5d70b82a4833618425\` ON \`collaboration\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_b7ece56376ac7ca0b9a56c33b3\` ON \`collaboration\` (\`tagsetTemplateSetId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_f67a2d25c945269d602c182fbc\` ON \`collaboration\` (\`timelineId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_35c6b1de6d4d89dfe8e9c85d77\` ON \`collaboration\` (\`innovationFlowId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_c66eddab0caacb1ef8d46bcafd\` ON \`organization_verification\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_1cc3b275fc2a9d9d9b0ae33b31\` ON \`organization_verification\` (\`lifecycleId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_b4cf0f96bf08cf396f68355522\` ON \`preference\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_8e76dcf171c45875c44febb1d8\` ON \`preference_set\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_8ed9d1af584fa62f1ad3405b33\` ON \`agent\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_e0e150e4f11d906b931b46a2d8\` ON \`organization\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_d2cb77c14644156ec8e865608e\` ON \`organization\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_7f1bec8979b57ed7ebd392a2ca\` ON \`organization\` (\`agentId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_5a72d5b37312bac2e0a0115718\` ON \`organization\` (\`verificationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_58fd47c4a6ac8df9fe2bcaed87\` ON \`organization\` (\`preferenceSetId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_395aa74996a1f978b4969d114b\` ON \`organization\` (\`storageAggregatorId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_e8e32f1e59c349b406a4752e54\` ON \`user_group\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_9912e4cfc1e09848a392a65151\` ON \`user_group\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_a20c5901817dd09d5906537e08\` ON \`communication\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_eb99e588873c788a68a035478a\` ON \`communication\` (\`updatesId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_684b272e6f7459439d41d2879e\` ON \`community_guidelines\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_3d60fe4fa40d54bad7d51bb4bd\` ON \`community_guidelines\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_3879db652f2421337691219ace\` ON \`library\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_23d4d78ea8db637df031f86f03\` ON \`license_policy\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_0c6a4d0a6c13a3f5df6ac01509\` ON \`licensing\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_a5dae5a376dd49c7c076893d40\` ON \`licensing\` (\`licensePolicyId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_4555dccdda9ba57d8e3a634cd0\` ON \`discussion\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_2d8a3ca181c3f0346817685d21\` ON \`discussion\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_5337074c9b818bb63e6f314c80\` ON \`discussion\` (\`commentsId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_3b0c92945f36d06f37de80285d\` ON \`forum\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_9f621c51dd854634d8766a9cfa\` ON \`platform\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_dd88d373c64b04e24705d575c9\` ON \`platform\` (\`forumId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_ca469f5ec53a7719d155d60aca\` ON \`platform\` (\`libraryId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_f516dd9a46616999c7e9a6adc1\` ON \`platform\` (\`storageAggregatorId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_425bbb4b951f7f4629710763fc\` ON \`platform\` (\`licensingId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_c0448d2c992a62c9c11bd0f142\` ON \`platform_invitation\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_09f909622aa177a097256b7cc2\` ON \`user\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_9466682df91534dd95e4dbaa61\` ON \`user\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_b61c694cacfab25533bd23d9ad\` ON \`user\` (\`agentId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_028322b763dc94242dc9f638f9\` ON \`user\` (\`preferenceSetId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_10458c50c10436b6d589b40e5c\` ON \`user\` (\`storageAggregatorId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_56f5614fff0028d40370499582\` ON \`application\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_7ec2857c7d8d16432ffca1cb3d\` ON \`application\` (\`lifecycleId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_b132226941570cb650a4023d49\` ON \`invitation\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_b0c80ccf319a1c7a7af12b3998\` ON \`invitation\` (\`lifecycleId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_b038f74c8d4eadb839e78b99ce\` ON \`role_set\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_00905b142498f63e76d38fb254\` ON \`role_set\` (\`applicationFormId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_6e7584bfb417bd0f8e8696ab58\` ON \`community\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_7fbe50fa78a37776ad962cb764\` ON \`community\` (\`communicationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_2e7dd2fa8c829352cfbecb2cc9\` ON \`community\` (\`guidelinesId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_3b8f390d76263ef5996869da07\` ON \`community\` (\`roleSetId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_a2afa3851ea733de932251b3a1\` ON \`actor\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_bde98d59e8984e7d17034c3b93\` ON \`actor_group\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_658580aea4e1a892227e27db90\` ON \`ecosystem_model\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_5f0dbc3b097ef297bd5f4ddb1a\` ON \`context\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_a03169c3f86480ba3863924f4d\` ON \`context\` (\`ecosystemModelId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_4318f97beabd362a8a09e9d320\` ON \`template\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_f58c3b144b6e010969e199beef\` ON \`template\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_45cf273f30c1fa509456b6b0dd\` ON \`template\` (\`innovationFlowId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_eedeae5e63f9a9c3a0161541e9\` ON \`template\` (\`communityGuidelinesId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_c6e4d1a07781a809ad3b3ee826\` ON \`template\` (\`calloutId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_f09090a77e07377eefb3f731d9\` ON \`template\` (\`whiteboardId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_21fdaf6dc88bdd6e8839e29b0b\` ON \`template\` (\`collaborationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_eb0176ef4b98c143322aa6f809\` ON \`templates_set\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_413ba75964e5a534e4bfa54846\` ON \`space_defaults\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_592a23e68922853bae6ebecd85\` ON \`space_defaults\` (\`innovationFlowTemplateId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_8d03fd2c8e8411ec9192c79cd9\` ON \`space\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_b4250035291aac1329d59224a9\` ON \`space\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_ea06eb8894469a0f262d929bf0\` ON \`space\` (\`collaborationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_cc0b08eb9679d3daa95153c2af\` ON \`space\` (\`contextId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_68fa2c2b00cc1ed77e7c225e8b\` ON \`space\` (\`communityId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_9c664d684f987a735678b0ba82\` ON \`space\` (\`agentId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_980c4643d7d9de1b97bc39f518\` ON \`space\` (\`storageAggregatorId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_43559aeadc1a5169d17e81b3d4\` ON \`space\` (\`libraryId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_6b1efee39d076d9f7ecb8fef4c\` ON \`space\` (\`defaultsId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_293f0d3ef60cb0ca0006044ecf\` ON \`ai_persona\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_e2eaa2213ac4454039cd8abc07\` ON \`virtual_contributor\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_4504c37764f6962ccbd165a21d\` ON \`virtual_contributor\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_a8890dcd65b8c3ee6e160d33f3\` ON \`virtual_contributor\` (\`agentId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_55b8101bdf4f566645e928c26e\` ON \`virtual_contributor\` (\`aiPersonaId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_8af8122897b05315e7eb892525\` ON \`innovation_pack\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_5facd6d188068a5a1c5b6f07fc\` ON \`innovation_pack\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_a1441e46c8d36090e1f6477cea\` ON \`innovation_pack\` (\`templatesSetId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_91a165c1091a6959cc19d52239\` ON \`account\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_833582df0c439ab8c9adc5240d\` ON \`account\` (\`agentId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_950221e932175dc7cf7c006488\` ON \`account\` (\`storageAggregatorId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_b411e4f27d77a96eccdabbf4b4\` ON \`innovation_hub\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_36c8905c2c6c59467c60d94fd8\` ON \`innovation_hub\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_9d520fa5fed56042918e48fc4b\` ON \`ai_server\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`REL_79206feb0038b1c5597668dc4b\` ON \`ai_persona_service\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE INDEX \`IDX_8495fae86f13836b0745642baa\` ON \`application_questions\` (\`applicationId\`)` + ); + await queryRunner.query( + `CREATE INDEX \`IDX_fe50118fd82e7fe2f74f986a19\` ON \`application_questions\` (\`nvpId\`)` + ); + await queryRunner.query( + `ALTER TABLE \`tagset_template\` ADD CONSTRAINT \`FK_96f23f044acf305c1699e0319d2\` FOREIGN KEY (\`tagsetTemplateSetId\`) REFERENCES \`tagset_template_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`tagset\` ADD CONSTRAINT \`FK_eb59b98ee6ef26c993d0d75c83c\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`tagset\` ADD CONSTRAINT \`FK_81fc213b2d9ad0cddeab1a9ce64\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`tagset\` ADD CONSTRAINT \`FK_644155610ddc40dc4e19781c8f0\` FOREIGN KEY (\`tagsetTemplateId\`) REFERENCES \`tagset_template\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`visual\` ADD CONSTRAINT \`FK_4fbd109f9bb84f58b7a3c60649c\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`visual\` ADD CONSTRAINT \`FK_1104f3ef8497ca40d99b9f46b87\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`document\` ADD CONSTRAINT \`FK_d9e2dfcccf59233c17cc6bc6418\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`document\` ADD CONSTRAINT \`FK_851e50ec4be7c62a1f9b9a430bf\` FOREIGN KEY (\`storageBucketId\`) REFERENCES \`storage_bucket\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`document\` ADD CONSTRAINT \`FK_9fb9257b14ec21daf5bc9aa4c8e\` FOREIGN KEY (\`tagsetId\`) REFERENCES \`tagset\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`storage_aggregator\` ADD CONSTRAINT \`FK_f3b4d59c0b805c9c1ecb0070e16\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`storage_aggregator\` ADD CONSTRAINT \`FK_b80c28f5335ab5442f63c644d94\` FOREIGN KEY (\`parentStorageAggregatorId\`) REFERENCES \`storage_aggregator\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`storage_aggregator\` ADD CONSTRAINT \`FK_0647707288c243e60091c8d8620\` FOREIGN KEY (\`directStorageId\`) REFERENCES \`storage_bucket\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`storage_bucket\` ADD CONSTRAINT \`FK_f2f48b57269987b13b415a00587\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`storage_bucket\` ADD CONSTRAINT \`FK_11d0ed50a26da5513f7e4347847\` FOREIGN KEY (\`storageAggregatorId\`) REFERENCES \`storage_aggregator\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`profile\` ADD CONSTRAINT \`FK_a96475631aba7dce41db03cc8b2\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`profile\` ADD CONSTRAINT \`FK_432056041df0e4337b17ff7b09d\` FOREIGN KEY (\`locationId\`) REFERENCES \`location\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`profile\` ADD CONSTRAINT \`FK_4a1c74fd2a61b32d9d9500e0650\` FOREIGN KEY (\`storageBucketId\`) REFERENCES \`storage_bucket\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`reference\` ADD CONSTRAINT \`FK_73e8ae665a49366ca7e2866a45d\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`reference\` ADD CONSTRAINT \`FK_2f46c698fc4c19a8cc233c5f255\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`vc_interaction\` ADD CONSTRAINT \`FK_d6f78c95ff41cdd30e505a4ebbb\` FOREIGN KEY (\`roomId\`) REFERENCES \`room\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`room\` ADD CONSTRAINT \`FK_d1d94dd8e0c417b4188a05ccbca\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`whiteboard\` ADD CONSTRAINT \`FK_d3b86160bb7d704212382b0ca44\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`whiteboard\` ADD CONSTRAINT \`FK_3f9e9e2798d2a4d84b16ee8477c\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`callout_framing\` ADD CONSTRAINT \`FK_c9d7c2c4eb8a1d012ddc6605da9\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`callout_framing\` ADD CONSTRAINT \`FK_f53e2d266432e58e538a366705d\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`callout_framing\` ADD CONSTRAINT \`FK_8bc0e1f40be5816d3a593cbf7fa\` FOREIGN KEY (\`whiteboardId\`) REFERENCES \`whiteboard\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`post\` ADD CONSTRAINT \`FK_390343b22abec869bf800419333\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`post\` ADD CONSTRAINT \`FK_970844fcd10c2b6df7c1b49eacf\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`post\` ADD CONSTRAINT \`FK_042b9825d770d6b3009ae206c2f\` FOREIGN KEY (\`commentsId\`) REFERENCES \`room\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`link\` ADD CONSTRAINT \`FK_07f249ac87502495710a62c5c01\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`link\` ADD CONSTRAINT \`FK_3bfc8c1aaec1395cc148268d3cd\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` ADD CONSTRAINT \`FK_dfa86c46f509a61c6510536cd9a\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` ADD CONSTRAINT \`FK_5e34f9a356f6254b8da24f8947b\` FOREIGN KEY (\`whiteboardId\`) REFERENCES \`whiteboard\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` ADD CONSTRAINT \`FK_97fefc97fb254c30577696e1c0a\` FOREIGN KEY (\`postId\`) REFERENCES \`post\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` ADD CONSTRAINT \`FK_bdf2d0eced5c95968a85caaaaee\` FOREIGN KEY (\`linkId\`) REFERENCES \`link\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` ADD CONSTRAINT \`FK_7370de8eb79ed00b0d403f2299a\` FOREIGN KEY (\`calloutId\`) REFERENCES \`callout\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` ADD CONSTRAINT \`FK_6289dee12effb51320051c6f1fc\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` ADD CONSTRAINT \`FK_cf776244b01436d8ca5cc762848\` FOREIGN KEY (\`framingId\`) REFERENCES \`callout_framing\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` ADD CONSTRAINT \`FK_1e740008a7e1512966e3b084148\` FOREIGN KEY (\`contributionPolicyId\`) REFERENCES \`callout_contribution_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` ADD CONSTRAINT \`FK_36b0da55acff774d0845aeb55f2\` FOREIGN KEY (\`contributionDefaultsId\`) REFERENCES \`callout_contribution_defaults\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` ADD CONSTRAINT \`FK_62ed316cda7b75735b20307b47e\` FOREIGN KEY (\`commentsId\`) REFERENCES \`room\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` ADD CONSTRAINT \`FK_9b1c5ee044611ac78249194ec35\` FOREIGN KEY (\`collaborationId\`) REFERENCES \`collaboration\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`calendar_event\` ADD CONSTRAINT \`FK_8ee86afa2808a4ab523b9ee6c5e\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`calendar_event\` ADD CONSTRAINT \`FK_9349e137959f3ca5818c2e62b3f\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`calendar_event\` ADD CONSTRAINT \`FK_b5069b11030e9608ee4468f850d\` FOREIGN KEY (\`commentsId\`) REFERENCES \`room\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`calendar_event\` ADD CONSTRAINT \`FK_80ab7835e1749581a27462eb87f\` FOREIGN KEY (\`calendarId\`) REFERENCES \`calendar\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`calendar\` ADD CONSTRAINT \`FK_6e74d59afda096b68d12a699691\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`timeline\` ADD CONSTRAINT \`FK_5fe58ece01b48496aebc04733da\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`timeline\` ADD CONSTRAINT \`FK_56aae15a664b2889a1a11c2cf82\` FOREIGN KEY (\`calendarId\`) REFERENCES \`calendar\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_flow\` ADD CONSTRAINT \`FK_a6e050daa4c7a3ab1e411c36517\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_flow\` ADD CONSTRAINT \`FK_96a8cbe1706f459fd7d883be9bd\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`collaboration\` ADD CONSTRAINT \`FK_262ecf3f5d70b82a48336184251\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`collaboration\` ADD CONSTRAINT \`FK_b7ece56376ac7ca0b9a56c33b3a\` FOREIGN KEY (\`tagsetTemplateSetId\`) REFERENCES \`tagset_template_set\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`collaboration\` ADD CONSTRAINT \`FK_f67a2d25c945269d602c182fbc0\` FOREIGN KEY (\`timelineId\`) REFERENCES \`timeline\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`collaboration\` ADD CONSTRAINT \`FK_35c6b1de6d4d89dfe8e9c85d771\` FOREIGN KEY (\`innovationFlowId\`) REFERENCES \`innovation_flow\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`organization_verification\` ADD CONSTRAINT \`FK_c66eddab0caacb1ef8d46bcafdb\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`organization_verification\` ADD CONSTRAINT \`FK_1cc3b275fc2a9d9d9b0ae33b310\` FOREIGN KEY (\`lifecycleId\`) REFERENCES \`lifecycle\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`preference\` ADD CONSTRAINT \`FK_b4cf0f96bf08cf396f683555229\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`preference\` ADD CONSTRAINT \`FK_46d60bf133073f749b8f07e534c\` FOREIGN KEY (\`preferenceDefinitionId\`) REFERENCES \`preference_definition\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`preference\` ADD CONSTRAINT \`FK_f4b5742f589e2ac8bfe48b708c0\` FOREIGN KEY (\`preferenceSetId\`) REFERENCES \`preference_set\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`preference_set\` ADD CONSTRAINT \`FK_8e76dcf171c45875c44febb1d8d\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`credential\` ADD CONSTRAINT \`FK_dbe0929355f82e5995f0b7fd5e2\` FOREIGN KEY (\`agentId\`) REFERENCES \`agent\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`agent\` ADD CONSTRAINT \`FK_8ed9d1af584fa62f1ad3405b33b\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` ADD CONSTRAINT \`FK_e0e150e4f11d906b931b46a2d89\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` ADD CONSTRAINT \`FK_d2cb77c14644156ec8e865608e0\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` ADD CONSTRAINT \`FK_7f1bec8979b57ed7ebd392a2ca9\` FOREIGN KEY (\`agentId\`) REFERENCES \`agent\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` ADD CONSTRAINT \`FK_5a72d5b37312bac2e0a01157185\` FOREIGN KEY (\`verificationId\`) REFERENCES \`organization_verification\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` ADD CONSTRAINT \`FK_58fd47c4a6ac8df9fe2bcaed874\` FOREIGN KEY (\`preferenceSetId\`) REFERENCES \`preference_set\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` ADD CONSTRAINT \`FK_395aa74996a1f978b4969d114b1\` FOREIGN KEY (\`storageAggregatorId\`) REFERENCES \`storage_aggregator\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`user_group\` ADD CONSTRAINT \`FK_e8e32f1e59c349b406a4752e545\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`user_group\` ADD CONSTRAINT \`FK_9912e4cfc1e09848a392a651514\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`user_group\` ADD CONSTRAINT \`FK_694ebec955a90e999d9926b7da8\` FOREIGN KEY (\`organizationId\`) REFERENCES \`organization\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`user_group\` ADD CONSTRAINT \`FK_9fcc131f256e969d773327f07cb\` FOREIGN KEY (\`communityId\`) REFERENCES \`community\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`communication\` ADD CONSTRAINT \`FK_a20c5901817dd09d5906537e087\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`communication\` ADD CONSTRAINT \`FK_eb99e588873c788a68a035478ab\` FOREIGN KEY (\`updatesId\`) REFERENCES \`room\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`community_guidelines\` ADD CONSTRAINT \`FK_684b272e6f7459439d41d2879ee\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`community_guidelines\` ADD CONSTRAINT \`FK_3d60fe4fa40d54bad7d51bb4bd1\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`role\` ADD CONSTRAINT \`FK_66d695b73839e9b66ff1350d34f\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`library\` ADD CONSTRAINT \`FK_3879db652f2421337691219ace8\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`license_policy\` ADD CONSTRAINT \`FK_23d4d78ea8db637df031f86f030\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`license_plan\` ADD CONSTRAINT \`FK_3030904030f5d30f483b49905d1\` FOREIGN KEY (\`licensingId\`) REFERENCES \`licensing\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`licensing\` ADD CONSTRAINT \`FK_0c6a4d0a6c13a3f5df6ac015096\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`licensing\` ADD CONSTRAINT \`FK_a5dae5a376dd49c7c076893d40b\` FOREIGN KEY (\`licensePolicyId\`) REFERENCES \`license_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`discussion\` ADD CONSTRAINT \`FK_4555dccdda9ba57d8e3a634cd0d\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`discussion\` ADD CONSTRAINT \`FK_2d8a3ca181c3f0346817685d21d\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`discussion\` ADD CONSTRAINT \`FK_5337074c9b818bb63e6f314c808\` FOREIGN KEY (\`commentsId\`) REFERENCES \`room\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`discussion\` ADD CONSTRAINT \`FK_0de78853c1ee793f61bda7eff79\` FOREIGN KEY (\`forumId\`) REFERENCES \`forum\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`forum\` ADD CONSTRAINT \`FK_3b0c92945f36d06f37de80285dd\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` ADD CONSTRAINT \`FK_9f621c51dd854634d8766a9cfaf\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` ADD CONSTRAINT \`FK_dd88d373c64b04e24705d575c99\` FOREIGN KEY (\`forumId\`) REFERENCES \`forum\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` ADD CONSTRAINT \`FK_ca469f5ec53a7719d155d60aca1\` FOREIGN KEY (\`libraryId\`) REFERENCES \`library\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` ADD CONSTRAINT \`FK_f516dd9a46616999c7e9a6adc15\` FOREIGN KEY (\`storageAggregatorId\`) REFERENCES \`storage_aggregator\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` ADD CONSTRAINT \`FK_425bbb4b951f7f4629710763fc0\` FOREIGN KEY (\`licensingId\`) REFERENCES \`licensing\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` ADD CONSTRAINT \`FK_c0448d2c992a62c9c11bd0f1422\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` ADD CONSTRAINT \`FK_562dce4a08bb214f08107b3631e\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` ADD CONSTRAINT \`FK_809c1e6cf3ef6be03a0a1db3f70\` FOREIGN KEY (\`platformId\`) REFERENCES \`platform\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`user\` ADD CONSTRAINT \`FK_09f909622aa177a097256b7cc22\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`user\` ADD CONSTRAINT \`FK_9466682df91534dd95e4dbaa616\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`user\` ADD CONSTRAINT \`FK_b61c694cacfab25533bd23d9add\` FOREIGN KEY (\`agentId\`) REFERENCES \`agent\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`user\` ADD CONSTRAINT \`FK_028322b763dc94242dc9f638f9b\` FOREIGN KEY (\`preferenceSetId\`) REFERENCES \`preference_set\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`user\` ADD CONSTRAINT \`FK_10458c50c10436b6d589b40e5ca\` FOREIGN KEY (\`storageAggregatorId\`) REFERENCES \`storage_aggregator\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`application\` ADD CONSTRAINT \`FK_56f5614fff0028d403704995822\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`application\` ADD CONSTRAINT \`FK_7ec2857c7d8d16432ffca1cb3d9\` FOREIGN KEY (\`lifecycleId\`) REFERENCES \`lifecycle\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`application\` ADD CONSTRAINT \`FK_b4ae3fea4a24b4be1a86dacf8a2\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`application\` ADD CONSTRAINT \`FK_8fb220ad1ac1f9c86ec39d134e4\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` ADD CONSTRAINT \`FK_b132226941570cb650a4023d493\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` ADD CONSTRAINT \`FK_b0c80ccf319a1c7a7af12b39987\` FOREIGN KEY (\`lifecycleId\`) REFERENCES \`lifecycle\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` ADD CONSTRAINT \`FK_6a3b86c6db10582baae7058f5b9\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`role_set\` ADD CONSTRAINT \`FK_b038f74c8d4eadb839e78b99ce5\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`role_set\` ADD CONSTRAINT \`FK_00905b142498f63e76d38fb254e\` FOREIGN KEY (\`applicationFormId\`) REFERENCES \`form\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`role_set\` ADD CONSTRAINT \`FK_86acc254af20d20c9d87c3503d5\` FOREIGN KEY (\`parentRoleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`community\` ADD CONSTRAINT \`FK_6e7584bfb417bd0f8e8696ab585\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`community\` ADD CONSTRAINT \`FK_7fbe50fa78a37776ad962cb7643\` FOREIGN KEY (\`communicationId\`) REFERENCES \`communication\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`community\` ADD CONSTRAINT \`FK_2e7dd2fa8c829352cfbecb2cc93\` FOREIGN KEY (\`guidelinesId\`) REFERENCES \`community_guidelines\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`community\` ADD CONSTRAINT \`FK_3b8f390d76263ef5996869da071\` FOREIGN KEY (\`roleSetId\`) REFERENCES \`role_set\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`actor\` ADD CONSTRAINT \`FK_a2afa3851ea733de932251b3a1f\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`actor\` ADD CONSTRAINT \`FK_0f9d41ee193d631a5439bb4f404\` FOREIGN KEY (\`actorGroupId\`) REFERENCES \`actor_group\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`actor_group\` ADD CONSTRAINT \`FK_bde98d59e8984e7d17034c3b937\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`actor_group\` ADD CONSTRAINT \`FK_cbb1d7afa052a184471723d3297\` FOREIGN KEY (\`ecosystemModelId\`) REFERENCES \`ecosystem_model\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`ecosystem_model\` ADD CONSTRAINT \`FK_658580aea4e1a892227e27db902\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`context\` ADD CONSTRAINT \`FK_5f0dbc3b097ef297bd5f4ddb1a9\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`context\` ADD CONSTRAINT \`FK_a03169c3f86480ba3863924f4d7\` FOREIGN KEY (\`ecosystemModelId\`) REFERENCES \`ecosystem_model\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`template\` ADD CONSTRAINT \`FK_4318f97beabd362a8a09e9d3203\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`template\` ADD CONSTRAINT \`FK_f58c3b144b6e010969e199beeff\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`template\` ADD CONSTRAINT \`FK_c7f54e6269c013d9c273f025edd\` FOREIGN KEY (\`templatesSetId\`) REFERENCES \`templates_set\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`template\` ADD CONSTRAINT \`FK_45cf273f30c1fa509456b6b0ddf\` FOREIGN KEY (\`innovationFlowId\`) REFERENCES \`innovation_flow\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`template\` ADD CONSTRAINT \`FK_eedeae5e63f9a9c3a0161541e98\` FOREIGN KEY (\`communityGuidelinesId\`) REFERENCES \`community_guidelines\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`template\` ADD CONSTRAINT \`FK_c6e4d1a07781a809ad3b3ee8265\` FOREIGN KEY (\`calloutId\`) REFERENCES \`callout\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`template\` ADD CONSTRAINT \`FK_f09090a77e07377eefb3f731d9f\` FOREIGN KEY (\`whiteboardId\`) REFERENCES \`whiteboard\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`template\` ADD CONSTRAINT \`FK_21fdaf6dc88bdd6e8839e29b0bd\` FOREIGN KEY (\`collaborationId\`) REFERENCES \`collaboration\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`templates_set\` ADD CONSTRAINT \`FK_eb0176ef4b98c143322aa6f8090\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`space_defaults\` ADD CONSTRAINT \`FK_413ba75964e5a534e4bfa54846e\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`space_defaults\` ADD CONSTRAINT \`FK_592a23e68922853bae6ebecd85e\` FOREIGN KEY (\`innovationFlowTemplateId\`) REFERENCES \`template\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD CONSTRAINT \`FK_8d03fd2c8e8411ec9192c79cd99\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD CONSTRAINT \`FK_b4250035291aac1329d59224a96\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD CONSTRAINT \`FK_ef1ff4ac7f613cc0677e2295b30\` FOREIGN KEY (\`parentSpaceId\`) REFERENCES \`space\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD CONSTRAINT \`FK_6bdeffaf6ea6159b4672a2aed70\` FOREIGN KEY (\`accountId\`) REFERENCES \`account\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD CONSTRAINT \`FK_ea06eb8894469a0f262d929bf06\` FOREIGN KEY (\`collaborationId\`) REFERENCES \`collaboration\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD CONSTRAINT \`FK_cc0b08eb9679d3daa95153c2af5\` FOREIGN KEY (\`contextId\`) REFERENCES \`context\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD CONSTRAINT \`FK_68fa2c2b00cc1ed77e7c225e8ba\` FOREIGN KEY (\`communityId\`) REFERENCES \`community\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD CONSTRAINT \`FK_9c664d684f987a735678b0ba825\` FOREIGN KEY (\`agentId\`) REFERENCES \`agent\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD CONSTRAINT \`FK_980c4643d7d9de1b97bc39f5185\` FOREIGN KEY (\`storageAggregatorId\`) REFERENCES \`storage_aggregator\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD CONSTRAINT \`FK_43559aeadc1a5169d17e81b3d45\` FOREIGN KEY (\`libraryId\`) REFERENCES \`templates_set\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`space\` ADD CONSTRAINT \`FK_6b1efee39d076d9f7ecb8fef4cd\` FOREIGN KEY (\`defaultsId\`) REFERENCES \`space_defaults\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`ai_persona\` ADD CONSTRAINT \`FK_293f0d3ef60cb0ca0006044ecfd\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` ADD CONSTRAINT \`FK_e2eaa2213ac4454039cd8abc07d\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` ADD CONSTRAINT \`FK_4504c37764f6962ccbd165a21de\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` ADD CONSTRAINT \`FK_a8890dcd65b8c3ee6e160d33f3a\` FOREIGN KEY (\`agentId\`) REFERENCES \`agent\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` ADD CONSTRAINT \`FK_7a962c9b04b0d197bc3c93262a7\` FOREIGN KEY (\`accountId\`) REFERENCES \`account\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` ADD CONSTRAINT \`FK_55b8101bdf4f566645e928c26e3\` FOREIGN KEY (\`aiPersonaId\`) REFERENCES \`ai_persona\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_pack\` ADD CONSTRAINT \`FK_8af8122897b05315e7eb8925253\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_pack\` ADD CONSTRAINT \`FK_5facd6d188068a5a1c5b6f07fc3\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_pack\` ADD CONSTRAINT \`FK_51014590f9644e6ff9e0536f40f\` FOREIGN KEY (\`accountId\`) REFERENCES \`account\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_pack\` ADD CONSTRAINT \`FK_a1441e46c8d36090e1f6477cea5\` FOREIGN KEY (\`templatesSetId\`) REFERENCES \`templates_set\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`account\` ADD CONSTRAINT \`FK_91a165c1091a6959cc19d522399\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`account\` ADD CONSTRAINT \`FK_833582df0c439ab8c9adc5240d1\` FOREIGN KEY (\`agentId\`) REFERENCES \`agent\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`account\` ADD CONSTRAINT \`FK_950221e932175dc7cf7c0064887\` FOREIGN KEY (\`storageAggregatorId\`) REFERENCES \`storage_aggregator\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_hub\` ADD CONSTRAINT \`FK_b411e4f27d77a96eccdabbf4b45\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_hub\` ADD CONSTRAINT \`FK_36c8905c2c6c59467c60d94fd8a\` FOREIGN KEY (\`profileId\`) REFERENCES \`profile\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_hub\` ADD CONSTRAINT \`FK_156fd30246eb151b9d17716abf5\` FOREIGN KEY (\`accountId\`) REFERENCES \`account\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`ai_server\` ADD CONSTRAINT \`FK_9d520fa5fed56042918e48fc4b5\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`ai_persona_service\` ADD CONSTRAINT \`FK_79206feb0038b1c5597668dc4b5\` FOREIGN KEY (\`authorizationId\`) REFERENCES \`authorization_policy\`(\`id\`) ON DELETE SET NULL ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`ai_persona_service\` ADD CONSTRAINT \`FK_b9f20da98058d7bd474152ed6ce\` FOREIGN KEY (\`aiServerId\`) REFERENCES \`ai_server\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION` + ); + await queryRunner.query( + `ALTER TABLE \`application_questions\` ADD CONSTRAINT \`FK_8495fae86f13836b0745642baa8\` FOREIGN KEY (\`applicationId\`) REFERENCES \`application\`(\`id\`) ON DELETE CASCADE ON UPDATE CASCADE` + ); + await queryRunner.query( + `ALTER TABLE \`application_questions\` ADD CONSTRAINT \`FK_fe50118fd82e7fe2f74f986a195\` FOREIGN KEY (\`nvpId\`) REFERENCES \`nvp\`(\`id\`) ON DELETE CASCADE ON UPDATE CASCADE` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE \`application_questions\` DROP FOREIGN KEY \`FK_fe50118fd82e7fe2f74f986a195\`` + ); + await queryRunner.query( + `ALTER TABLE \`application_questions\` DROP FOREIGN KEY \`FK_8495fae86f13836b0745642baa8\`` + ); + await queryRunner.query( + `ALTER TABLE \`ai_persona_service\` DROP FOREIGN KEY \`FK_b9f20da98058d7bd474152ed6ce\`` + ); + await queryRunner.query( + `ALTER TABLE \`ai_persona_service\` DROP FOREIGN KEY \`FK_79206feb0038b1c5597668dc4b5\`` + ); + await queryRunner.query( + `ALTER TABLE \`ai_server\` DROP FOREIGN KEY \`FK_9d520fa5fed56042918e48fc4b5\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_hub\` DROP FOREIGN KEY \`FK_156fd30246eb151b9d17716abf5\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_hub\` DROP FOREIGN KEY \`FK_36c8905c2c6c59467c60d94fd8a\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_hub\` DROP FOREIGN KEY \`FK_b411e4f27d77a96eccdabbf4b45\`` + ); + await queryRunner.query( + `ALTER TABLE \`account\` DROP FOREIGN KEY \`FK_950221e932175dc7cf7c0064887\`` + ); + await queryRunner.query( + `ALTER TABLE \`account\` DROP FOREIGN KEY \`FK_833582df0c439ab8c9adc5240d1\`` + ); + await queryRunner.query( + `ALTER TABLE \`account\` DROP FOREIGN KEY \`FK_91a165c1091a6959cc19d522399\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_pack\` DROP FOREIGN KEY \`FK_a1441e46c8d36090e1f6477cea5\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_pack\` DROP FOREIGN KEY \`FK_51014590f9644e6ff9e0536f40f\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_pack\` DROP FOREIGN KEY \`FK_5facd6d188068a5a1c5b6f07fc3\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_pack\` DROP FOREIGN KEY \`FK_8af8122897b05315e7eb8925253\`` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` DROP FOREIGN KEY \`FK_55b8101bdf4f566645e928c26e3\`` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` DROP FOREIGN KEY \`FK_7a962c9b04b0d197bc3c93262a7\`` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` DROP FOREIGN KEY \`FK_a8890dcd65b8c3ee6e160d33f3a\`` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` DROP FOREIGN KEY \`FK_4504c37764f6962ccbd165a21de\`` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` DROP FOREIGN KEY \`FK_e2eaa2213ac4454039cd8abc07d\`` + ); + await queryRunner.query( + `ALTER TABLE \`ai_persona\` DROP FOREIGN KEY \`FK_293f0d3ef60cb0ca0006044ecfd\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP FOREIGN KEY \`FK_6b1efee39d076d9f7ecb8fef4cd\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP FOREIGN KEY \`FK_43559aeadc1a5169d17e81b3d45\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP FOREIGN KEY \`FK_980c4643d7d9de1b97bc39f5185\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP FOREIGN KEY \`FK_9c664d684f987a735678b0ba825\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP FOREIGN KEY \`FK_68fa2c2b00cc1ed77e7c225e8ba\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP FOREIGN KEY \`FK_cc0b08eb9679d3daa95153c2af5\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP FOREIGN KEY \`FK_ea06eb8894469a0f262d929bf06\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP FOREIGN KEY \`FK_6bdeffaf6ea6159b4672a2aed70\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP FOREIGN KEY \`FK_ef1ff4ac7f613cc0677e2295b30\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP FOREIGN KEY \`FK_b4250035291aac1329d59224a96\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP FOREIGN KEY \`FK_8d03fd2c8e8411ec9192c79cd99\`` + ); + await queryRunner.query( + `ALTER TABLE \`space_defaults\` DROP FOREIGN KEY \`FK_592a23e68922853bae6ebecd85e\`` + ); + await queryRunner.query( + `ALTER TABLE \`space_defaults\` DROP FOREIGN KEY \`FK_413ba75964e5a534e4bfa54846e\`` + ); + await queryRunner.query( + `ALTER TABLE \`templates_set\` DROP FOREIGN KEY \`FK_eb0176ef4b98c143322aa6f8090\`` + ); + await queryRunner.query( + `ALTER TABLE \`template\` DROP FOREIGN KEY \`FK_21fdaf6dc88bdd6e8839e29b0bd\`` + ); + await queryRunner.query( + `ALTER TABLE \`template\` DROP FOREIGN KEY \`FK_f09090a77e07377eefb3f731d9f\`` + ); + await queryRunner.query( + `ALTER TABLE \`template\` DROP FOREIGN KEY \`FK_c6e4d1a07781a809ad3b3ee8265\`` + ); + await queryRunner.query( + `ALTER TABLE \`template\` DROP FOREIGN KEY \`FK_eedeae5e63f9a9c3a0161541e98\`` + ); + await queryRunner.query( + `ALTER TABLE \`template\` DROP FOREIGN KEY \`FK_45cf273f30c1fa509456b6b0ddf\`` + ); + await queryRunner.query( + `ALTER TABLE \`template\` DROP FOREIGN KEY \`FK_c7f54e6269c013d9c273f025edd\`` + ); + await queryRunner.query( + `ALTER TABLE \`template\` DROP FOREIGN KEY \`FK_f58c3b144b6e010969e199beeff\`` + ); + await queryRunner.query( + `ALTER TABLE \`template\` DROP FOREIGN KEY \`FK_4318f97beabd362a8a09e9d3203\`` + ); + await queryRunner.query( + `ALTER TABLE \`context\` DROP FOREIGN KEY \`FK_a03169c3f86480ba3863924f4d7\`` + ); + await queryRunner.query( + `ALTER TABLE \`context\` DROP FOREIGN KEY \`FK_5f0dbc3b097ef297bd5f4ddb1a9\`` + ); + await queryRunner.query( + `ALTER TABLE \`ecosystem_model\` DROP FOREIGN KEY \`FK_658580aea4e1a892227e27db902\`` + ); + await queryRunner.query( + `ALTER TABLE \`actor_group\` DROP FOREIGN KEY \`FK_cbb1d7afa052a184471723d3297\`` + ); + await queryRunner.query( + `ALTER TABLE \`actor_group\` DROP FOREIGN KEY \`FK_bde98d59e8984e7d17034c3b937\`` + ); + await queryRunner.query( + `ALTER TABLE \`actor\` DROP FOREIGN KEY \`FK_0f9d41ee193d631a5439bb4f404\`` + ); + await queryRunner.query( + `ALTER TABLE \`actor\` DROP FOREIGN KEY \`FK_a2afa3851ea733de932251b3a1f\`` + ); + await queryRunner.query( + `ALTER TABLE \`community\` DROP FOREIGN KEY \`FK_3b8f390d76263ef5996869da071\`` + ); + await queryRunner.query( + `ALTER TABLE \`community\` DROP FOREIGN KEY \`FK_2e7dd2fa8c829352cfbecb2cc93\`` + ); + await queryRunner.query( + `ALTER TABLE \`community\` DROP FOREIGN KEY \`FK_7fbe50fa78a37776ad962cb7643\`` + ); + await queryRunner.query( + `ALTER TABLE \`community\` DROP FOREIGN KEY \`FK_6e7584bfb417bd0f8e8696ab585\`` + ); + await queryRunner.query( + `ALTER TABLE \`role_set\` DROP FOREIGN KEY \`FK_86acc254af20d20c9d87c3503d5\`` + ); + await queryRunner.query( + `ALTER TABLE \`role_set\` DROP FOREIGN KEY \`FK_00905b142498f63e76d38fb254e\`` + ); + await queryRunner.query( + `ALTER TABLE \`role_set\` DROP FOREIGN KEY \`FK_b038f74c8d4eadb839e78b99ce5\`` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` DROP FOREIGN KEY \`FK_6a3b86c6db10582baae7058f5b9\`` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` DROP FOREIGN KEY \`FK_b0c80ccf319a1c7a7af12b39987\`` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` DROP FOREIGN KEY \`FK_b132226941570cb650a4023d493\`` + ); + await queryRunner.query( + `ALTER TABLE \`application\` DROP FOREIGN KEY \`FK_8fb220ad1ac1f9c86ec39d134e4\`` + ); + await queryRunner.query( + `ALTER TABLE \`application\` DROP FOREIGN KEY \`FK_b4ae3fea4a24b4be1a86dacf8a2\`` + ); + await queryRunner.query( + `ALTER TABLE \`application\` DROP FOREIGN KEY \`FK_7ec2857c7d8d16432ffca1cb3d9\`` + ); + await queryRunner.query( + `ALTER TABLE \`application\` DROP FOREIGN KEY \`FK_56f5614fff0028d403704995822\`` + ); + await queryRunner.query( + `ALTER TABLE \`user\` DROP FOREIGN KEY \`FK_10458c50c10436b6d589b40e5ca\`` + ); + await queryRunner.query( + `ALTER TABLE \`user\` DROP FOREIGN KEY \`FK_028322b763dc94242dc9f638f9b\`` + ); + await queryRunner.query( + `ALTER TABLE \`user\` DROP FOREIGN KEY \`FK_b61c694cacfab25533bd23d9add\`` + ); + await queryRunner.query( + `ALTER TABLE \`user\` DROP FOREIGN KEY \`FK_9466682df91534dd95e4dbaa616\`` + ); + await queryRunner.query( + `ALTER TABLE \`user\` DROP FOREIGN KEY \`FK_09f909622aa177a097256b7cc22\`` + ); + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` DROP FOREIGN KEY \`FK_809c1e6cf3ef6be03a0a1db3f70\`` + ); + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` DROP FOREIGN KEY \`FK_562dce4a08bb214f08107b3631e\`` + ); + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` DROP FOREIGN KEY \`FK_c0448d2c992a62c9c11bd0f1422\`` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` DROP FOREIGN KEY \`FK_425bbb4b951f7f4629710763fc0\`` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` DROP FOREIGN KEY \`FK_f516dd9a46616999c7e9a6adc15\`` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` DROP FOREIGN KEY \`FK_ca469f5ec53a7719d155d60aca1\`` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` DROP FOREIGN KEY \`FK_dd88d373c64b04e24705d575c99\`` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` DROP FOREIGN KEY \`FK_9f621c51dd854634d8766a9cfaf\`` + ); + await queryRunner.query( + `ALTER TABLE \`forum\` DROP FOREIGN KEY \`FK_3b0c92945f36d06f37de80285dd\`` + ); + await queryRunner.query( + `ALTER TABLE \`discussion\` DROP FOREIGN KEY \`FK_0de78853c1ee793f61bda7eff79\`` + ); + await queryRunner.query( + `ALTER TABLE \`discussion\` DROP FOREIGN KEY \`FK_5337074c9b818bb63e6f314c808\`` + ); + await queryRunner.query( + `ALTER TABLE \`discussion\` DROP FOREIGN KEY \`FK_2d8a3ca181c3f0346817685d21d\`` + ); + await queryRunner.query( + `ALTER TABLE \`discussion\` DROP FOREIGN KEY \`FK_4555dccdda9ba57d8e3a634cd0d\`` + ); + await queryRunner.query( + `ALTER TABLE \`licensing\` DROP FOREIGN KEY \`FK_a5dae5a376dd49c7c076893d40b\`` + ); + await queryRunner.query( + `ALTER TABLE \`licensing\` DROP FOREIGN KEY \`FK_0c6a4d0a6c13a3f5df6ac015096\`` + ); + await queryRunner.query( + `ALTER TABLE \`license_plan\` DROP FOREIGN KEY \`FK_3030904030f5d30f483b49905d1\`` + ); + await queryRunner.query( + `ALTER TABLE \`license_policy\` DROP FOREIGN KEY \`FK_23d4d78ea8db637df031f86f030\`` + ); + await queryRunner.query( + `ALTER TABLE \`library\` DROP FOREIGN KEY \`FK_3879db652f2421337691219ace8\`` + ); + await queryRunner.query( + `ALTER TABLE \`role\` DROP FOREIGN KEY \`FK_66d695b73839e9b66ff1350d34f\`` + ); + await queryRunner.query( + `ALTER TABLE \`community_guidelines\` DROP FOREIGN KEY \`FK_3d60fe4fa40d54bad7d51bb4bd1\`` + ); + await queryRunner.query( + `ALTER TABLE \`community_guidelines\` DROP FOREIGN KEY \`FK_684b272e6f7459439d41d2879ee\`` + ); + await queryRunner.query( + `ALTER TABLE \`communication\` DROP FOREIGN KEY \`FK_eb99e588873c788a68a035478ab\`` + ); + await queryRunner.query( + `ALTER TABLE \`communication\` DROP FOREIGN KEY \`FK_a20c5901817dd09d5906537e087\`` + ); + await queryRunner.query( + `ALTER TABLE \`user_group\` DROP FOREIGN KEY \`FK_9fcc131f256e969d773327f07cb\`` + ); + await queryRunner.query( + `ALTER TABLE \`user_group\` DROP FOREIGN KEY \`FK_694ebec955a90e999d9926b7da8\`` + ); + await queryRunner.query( + `ALTER TABLE \`user_group\` DROP FOREIGN KEY \`FK_9912e4cfc1e09848a392a651514\`` + ); + await queryRunner.query( + `ALTER TABLE \`user_group\` DROP FOREIGN KEY \`FK_e8e32f1e59c349b406a4752e545\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` DROP FOREIGN KEY \`FK_395aa74996a1f978b4969d114b1\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` DROP FOREIGN KEY \`FK_58fd47c4a6ac8df9fe2bcaed874\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` DROP FOREIGN KEY \`FK_5a72d5b37312bac2e0a01157185\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` DROP FOREIGN KEY \`FK_7f1bec8979b57ed7ebd392a2ca9\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` DROP FOREIGN KEY \`FK_d2cb77c14644156ec8e865608e0\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` DROP FOREIGN KEY \`FK_e0e150e4f11d906b931b46a2d89\`` + ); + await queryRunner.query( + `ALTER TABLE \`agent\` DROP FOREIGN KEY \`FK_8ed9d1af584fa62f1ad3405b33b\`` + ); + await queryRunner.query( + `ALTER TABLE \`credential\` DROP FOREIGN KEY \`FK_dbe0929355f82e5995f0b7fd5e2\`` + ); + await queryRunner.query( + `ALTER TABLE \`preference_set\` DROP FOREIGN KEY \`FK_8e76dcf171c45875c44febb1d8d\`` + ); + await queryRunner.query( + `ALTER TABLE \`preference\` DROP FOREIGN KEY \`FK_f4b5742f589e2ac8bfe48b708c0\`` + ); + await queryRunner.query( + `ALTER TABLE \`preference\` DROP FOREIGN KEY \`FK_46d60bf133073f749b8f07e534c\`` + ); + await queryRunner.query( + `ALTER TABLE \`preference\` DROP FOREIGN KEY \`FK_b4cf0f96bf08cf396f683555229\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization_verification\` DROP FOREIGN KEY \`FK_1cc3b275fc2a9d9d9b0ae33b310\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization_verification\` DROP FOREIGN KEY \`FK_c66eddab0caacb1ef8d46bcafdb\`` + ); + await queryRunner.query( + `ALTER TABLE \`collaboration\` DROP FOREIGN KEY \`FK_35c6b1de6d4d89dfe8e9c85d771\`` + ); + await queryRunner.query( + `ALTER TABLE \`collaboration\` DROP FOREIGN KEY \`FK_f67a2d25c945269d602c182fbc0\`` + ); + await queryRunner.query( + `ALTER TABLE \`collaboration\` DROP FOREIGN KEY \`FK_b7ece56376ac7ca0b9a56c33b3a\`` + ); + await queryRunner.query( + `ALTER TABLE \`collaboration\` DROP FOREIGN KEY \`FK_262ecf3f5d70b82a48336184251\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_flow\` DROP FOREIGN KEY \`FK_96a8cbe1706f459fd7d883be9bd\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_flow\` DROP FOREIGN KEY \`FK_a6e050daa4c7a3ab1e411c36517\`` + ); + await queryRunner.query( + `ALTER TABLE \`timeline\` DROP FOREIGN KEY \`FK_56aae15a664b2889a1a11c2cf82\`` + ); + await queryRunner.query( + `ALTER TABLE \`timeline\` DROP FOREIGN KEY \`FK_5fe58ece01b48496aebc04733da\`` + ); + await queryRunner.query( + `ALTER TABLE \`calendar\` DROP FOREIGN KEY \`FK_6e74d59afda096b68d12a699691\`` + ); + await queryRunner.query( + `ALTER TABLE \`calendar_event\` DROP FOREIGN KEY \`FK_80ab7835e1749581a27462eb87f\`` + ); + await queryRunner.query( + `ALTER TABLE \`calendar_event\` DROP FOREIGN KEY \`FK_b5069b11030e9608ee4468f850d\`` + ); + await queryRunner.query( + `ALTER TABLE \`calendar_event\` DROP FOREIGN KEY \`FK_9349e137959f3ca5818c2e62b3f\`` + ); + await queryRunner.query( + `ALTER TABLE \`calendar_event\` DROP FOREIGN KEY \`FK_8ee86afa2808a4ab523b9ee6c5e\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` DROP FOREIGN KEY \`FK_9b1c5ee044611ac78249194ec35\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` DROP FOREIGN KEY \`FK_62ed316cda7b75735b20307b47e\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` DROP FOREIGN KEY \`FK_36b0da55acff774d0845aeb55f2\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` DROP FOREIGN KEY \`FK_1e740008a7e1512966e3b084148\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` DROP FOREIGN KEY \`FK_cf776244b01436d8ca5cc762848\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` DROP FOREIGN KEY \`FK_6289dee12effb51320051c6f1fc\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` DROP FOREIGN KEY \`FK_7370de8eb79ed00b0d403f2299a\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` DROP FOREIGN KEY \`FK_bdf2d0eced5c95968a85caaaaee\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` DROP FOREIGN KEY \`FK_97fefc97fb254c30577696e1c0a\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` DROP FOREIGN KEY \`FK_5e34f9a356f6254b8da24f8947b\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` DROP FOREIGN KEY \`FK_dfa86c46f509a61c6510536cd9a\`` + ); + await queryRunner.query( + `ALTER TABLE \`link\` DROP FOREIGN KEY \`FK_3bfc8c1aaec1395cc148268d3cd\`` + ); + await queryRunner.query( + `ALTER TABLE \`link\` DROP FOREIGN KEY \`FK_07f249ac87502495710a62c5c01\`` + ); + await queryRunner.query( + `ALTER TABLE \`post\` DROP FOREIGN KEY \`FK_042b9825d770d6b3009ae206c2f\`` + ); + await queryRunner.query( + `ALTER TABLE \`post\` DROP FOREIGN KEY \`FK_970844fcd10c2b6df7c1b49eacf\`` + ); + await queryRunner.query( + `ALTER TABLE \`post\` DROP FOREIGN KEY \`FK_390343b22abec869bf800419333\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout_framing\` DROP FOREIGN KEY \`FK_8bc0e1f40be5816d3a593cbf7fa\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout_framing\` DROP FOREIGN KEY \`FK_f53e2d266432e58e538a366705d\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout_framing\` DROP FOREIGN KEY \`FK_c9d7c2c4eb8a1d012ddc6605da9\`` + ); + await queryRunner.query( + `ALTER TABLE \`whiteboard\` DROP FOREIGN KEY \`FK_3f9e9e2798d2a4d84b16ee8477c\`` + ); + await queryRunner.query( + `ALTER TABLE \`whiteboard\` DROP FOREIGN KEY \`FK_d3b86160bb7d704212382b0ca44\`` + ); + await queryRunner.query( + `ALTER TABLE \`room\` DROP FOREIGN KEY \`FK_d1d94dd8e0c417b4188a05ccbca\`` + ); + await queryRunner.query( + `ALTER TABLE \`vc_interaction\` DROP FOREIGN KEY \`FK_d6f78c95ff41cdd30e505a4ebbb\`` + ); + await queryRunner.query( + `ALTER TABLE \`reference\` DROP FOREIGN KEY \`FK_2f46c698fc4c19a8cc233c5f255\`` + ); + await queryRunner.query( + `ALTER TABLE \`reference\` DROP FOREIGN KEY \`FK_73e8ae665a49366ca7e2866a45d\`` + ); + await queryRunner.query( + `ALTER TABLE \`profile\` DROP FOREIGN KEY \`FK_4a1c74fd2a61b32d9d9500e0650\`` + ); + await queryRunner.query( + `ALTER TABLE \`profile\` DROP FOREIGN KEY \`FK_432056041df0e4337b17ff7b09d\`` + ); + await queryRunner.query( + `ALTER TABLE \`profile\` DROP FOREIGN KEY \`FK_a96475631aba7dce41db03cc8b2\`` + ); + await queryRunner.query( + `ALTER TABLE \`storage_bucket\` DROP FOREIGN KEY \`FK_11d0ed50a26da5513f7e4347847\`` + ); + await queryRunner.query( + `ALTER TABLE \`storage_bucket\` DROP FOREIGN KEY \`FK_f2f48b57269987b13b415a00587\`` + ); + await queryRunner.query( + `ALTER TABLE \`storage_aggregator\` DROP FOREIGN KEY \`FK_0647707288c243e60091c8d8620\`` + ); + await queryRunner.query( + `ALTER TABLE \`storage_aggregator\` DROP FOREIGN KEY \`FK_b80c28f5335ab5442f63c644d94\`` + ); + await queryRunner.query( + `ALTER TABLE \`storage_aggregator\` DROP FOREIGN KEY \`FK_f3b4d59c0b805c9c1ecb0070e16\`` + ); + await queryRunner.query( + `ALTER TABLE \`document\` DROP FOREIGN KEY \`FK_9fb9257b14ec21daf5bc9aa4c8e\`` + ); + await queryRunner.query( + `ALTER TABLE \`document\` DROP FOREIGN KEY \`FK_851e50ec4be7c62a1f9b9a430bf\`` + ); + await queryRunner.query( + `ALTER TABLE \`document\` DROP FOREIGN KEY \`FK_d9e2dfcccf59233c17cc6bc6418\`` + ); + await queryRunner.query( + `ALTER TABLE \`visual\` DROP FOREIGN KEY \`FK_1104f3ef8497ca40d99b9f46b87\`` + ); + await queryRunner.query( + `ALTER TABLE \`visual\` DROP FOREIGN KEY \`FK_4fbd109f9bb84f58b7a3c60649c\`` + ); + await queryRunner.query( + `ALTER TABLE \`tagset\` DROP FOREIGN KEY \`FK_644155610ddc40dc4e19781c8f0\`` + ); + await queryRunner.query( + `ALTER TABLE \`tagset\` DROP FOREIGN KEY \`FK_81fc213b2d9ad0cddeab1a9ce64\`` + ); + await queryRunner.query( + `ALTER TABLE \`tagset\` DROP FOREIGN KEY \`FK_eb59b98ee6ef26c993d0d75c83c\`` + ); + await queryRunner.query( + `ALTER TABLE \`tagset_template\` DROP FOREIGN KEY \`FK_96f23f044acf305c1699e0319d2\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_fe50118fd82e7fe2f74f986a19\` ON \`application_questions\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_8495fae86f13836b0745642baa\` ON \`application_questions\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_79206feb0038b1c5597668dc4b\` ON \`ai_persona_service\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_9d520fa5fed56042918e48fc4b\` ON \`ai_server\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_36c8905c2c6c59467c60d94fd8\` ON \`innovation_hub\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_b411e4f27d77a96eccdabbf4b4\` ON \`innovation_hub\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_950221e932175dc7cf7c006488\` ON \`account\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_833582df0c439ab8c9adc5240d\` ON \`account\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_91a165c1091a6959cc19d52239\` ON \`account\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_a1441e46c8d36090e1f6477cea\` ON \`innovation_pack\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_5facd6d188068a5a1c5b6f07fc\` ON \`innovation_pack\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_8af8122897b05315e7eb892525\` ON \`innovation_pack\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_55b8101bdf4f566645e928c26e\` ON \`virtual_contributor\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_a8890dcd65b8c3ee6e160d33f3\` ON \`virtual_contributor\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_4504c37764f6962ccbd165a21d\` ON \`virtual_contributor\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_e2eaa2213ac4454039cd8abc07\` ON \`virtual_contributor\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_293f0d3ef60cb0ca0006044ecf\` ON \`ai_persona\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_6b1efee39d076d9f7ecb8fef4c\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_43559aeadc1a5169d17e81b3d4\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_980c4643d7d9de1b97bc39f518\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_9c664d684f987a735678b0ba82\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_68fa2c2b00cc1ed77e7c225e8b\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_cc0b08eb9679d3daa95153c2af\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_ea06eb8894469a0f262d929bf0\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_b4250035291aac1329d59224a9\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_8d03fd2c8e8411ec9192c79cd9\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_592a23e68922853bae6ebecd85\` ON \`space_defaults\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_413ba75964e5a534e4bfa54846\` ON \`space_defaults\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_eb0176ef4b98c143322aa6f809\` ON \`templates_set\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_21fdaf6dc88bdd6e8839e29b0b\` ON \`template\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_f09090a77e07377eefb3f731d9\` ON \`template\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_c6e4d1a07781a809ad3b3ee826\` ON \`template\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_eedeae5e63f9a9c3a0161541e9\` ON \`template\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_45cf273f30c1fa509456b6b0dd\` ON \`template\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_f58c3b144b6e010969e199beef\` ON \`template\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_4318f97beabd362a8a09e9d320\` ON \`template\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_a03169c3f86480ba3863924f4d\` ON \`context\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_5f0dbc3b097ef297bd5f4ddb1a\` ON \`context\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_658580aea4e1a892227e27db90\` ON \`ecosystem_model\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_bde98d59e8984e7d17034c3b93\` ON \`actor_group\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_a2afa3851ea733de932251b3a1\` ON \`actor\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_3b8f390d76263ef5996869da07\` ON \`community\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_2e7dd2fa8c829352cfbecb2cc9\` ON \`community\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_7fbe50fa78a37776ad962cb764\` ON \`community\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_6e7584bfb417bd0f8e8696ab58\` ON \`community\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_00905b142498f63e76d38fb254\` ON \`role_set\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_b038f74c8d4eadb839e78b99ce\` ON \`role_set\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_b0c80ccf319a1c7a7af12b3998\` ON \`invitation\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_b132226941570cb650a4023d49\` ON \`invitation\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_7ec2857c7d8d16432ffca1cb3d\` ON \`application\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_56f5614fff0028d40370499582\` ON \`application\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_10458c50c10436b6d589b40e5c\` ON \`user\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_028322b763dc94242dc9f638f9\` ON \`user\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_b61c694cacfab25533bd23d9ad\` ON \`user\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_9466682df91534dd95e4dbaa61\` ON \`user\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_09f909622aa177a097256b7cc2\` ON \`user\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_c0448d2c992a62c9c11bd0f142\` ON \`platform_invitation\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_425bbb4b951f7f4629710763fc\` ON \`platform\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_f516dd9a46616999c7e9a6adc1\` ON \`platform\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_ca469f5ec53a7719d155d60aca\` ON \`platform\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_dd88d373c64b04e24705d575c9\` ON \`platform\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_9f621c51dd854634d8766a9cfa\` ON \`platform\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_3b0c92945f36d06f37de80285d\` ON \`forum\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_5337074c9b818bb63e6f314c80\` ON \`discussion\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_2d8a3ca181c3f0346817685d21\` ON \`discussion\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_4555dccdda9ba57d8e3a634cd0\` ON \`discussion\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_a5dae5a376dd49c7c076893d40\` ON \`licensing\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_0c6a4d0a6c13a3f5df6ac01509\` ON \`licensing\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_23d4d78ea8db637df031f86f03\` ON \`license_policy\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_3879db652f2421337691219ace\` ON \`library\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_3d60fe4fa40d54bad7d51bb4bd\` ON \`community_guidelines\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_684b272e6f7459439d41d2879e\` ON \`community_guidelines\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_eb99e588873c788a68a035478a\` ON \`communication\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_a20c5901817dd09d5906537e08\` ON \`communication\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_9912e4cfc1e09848a392a65151\` ON \`user_group\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_e8e32f1e59c349b406a4752e54\` ON \`user_group\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_395aa74996a1f978b4969d114b\` ON \`organization\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_58fd47c4a6ac8df9fe2bcaed87\` ON \`organization\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_5a72d5b37312bac2e0a0115718\` ON \`organization\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_7f1bec8979b57ed7ebd392a2ca\` ON \`organization\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_d2cb77c14644156ec8e865608e\` ON \`organization\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_e0e150e4f11d906b931b46a2d8\` ON \`organization\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_8ed9d1af584fa62f1ad3405b33\` ON \`agent\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_8e76dcf171c45875c44febb1d8\` ON \`preference_set\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_b4cf0f96bf08cf396f68355522\` ON \`preference\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_1cc3b275fc2a9d9d9b0ae33b31\` ON \`organization_verification\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_c66eddab0caacb1ef8d46bcafd\` ON \`organization_verification\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_35c6b1de6d4d89dfe8e9c85d77\` ON \`collaboration\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_f67a2d25c945269d602c182fbc\` ON \`collaboration\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_b7ece56376ac7ca0b9a56c33b3\` ON \`collaboration\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_262ecf3f5d70b82a4833618425\` ON \`collaboration\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_96a8cbe1706f459fd7d883be9b\` ON \`innovation_flow\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_a6e050daa4c7a3ab1e411c3651\` ON \`innovation_flow\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_56aae15a664b2889a1a11c2cf8\` ON \`timeline\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_5fe58ece01b48496aebc04733d\` ON \`timeline\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_6e74d59afda096b68d12a69969\` ON \`calendar\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_b5069b11030e9608ee4468f850\` ON \`calendar_event\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_9349e137959f3ca5818c2e62b3\` ON \`calendar_event\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_8ee86afa2808a4ab523b9ee6c5\` ON \`calendar_event\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_62ed316cda7b75735b20307b47\` ON \`callout\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_36b0da55acff774d0845aeb55f\` ON \`callout\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_1e740008a7e1512966e3b08414\` ON \`callout\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_cf776244b01436d8ca5cc76284\` ON \`callout\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_6289dee12effb51320051c6f1f\` ON \`callout\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_bdf2d0eced5c95968a85caaaae\` ON \`callout_contribution\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_97fefc97fb254c30577696e1c0\` ON \`callout_contribution\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_5e34f9a356f6254b8da24f8947\` ON \`callout_contribution\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_dfa86c46f509a61c6510536cd9\` ON \`callout_contribution\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_3bfc8c1aaec1395cc148268d3c\` ON \`link\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_07f249ac87502495710a62c5c0\` ON \`link\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_042b9825d770d6b3009ae206c2\` ON \`post\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_970844fcd10c2b6df7c1b49eac\` ON \`post\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_390343b22abec869bf80041933\` ON \`post\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_8bc0e1f40be5816d3a593cbf7f\` ON \`callout_framing\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_f53e2d266432e58e538a366705\` ON \`callout_framing\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_c9d7c2c4eb8a1d012ddc6605da\` ON \`callout_framing\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_3f9e9e2798d2a4d84b16ee8477\` ON \`whiteboard\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_d3b86160bb7d704212382b0ca4\` ON \`whiteboard\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_d1d94dd8e0c417b4188a05ccbc\` ON \`room\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_73e8ae665a49366ca7e2866a45\` ON \`reference\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_4a1c74fd2a61b32d9d9500e065\` ON \`profile\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_432056041df0e4337b17ff7b09\` ON \`profile\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_a96475631aba7dce41db03cc8b\` ON \`profile\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_f2f48b57269987b13b415a0058\` ON \`storage_bucket\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_0647707288c243e60091c8d862\` ON \`storage_aggregator\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_f3b4d59c0b805c9c1ecb0070e1\` ON \`storage_aggregator\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_9fb9257b14ec21daf5bc9aa4c8\` ON \`document\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_d9e2dfcccf59233c17cc6bc641\` ON \`document\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_4fbd109f9bb84f58b7a3c60649\` ON \`visual\`` + ); + await queryRunner.query( + `DROP INDEX \`REL_eb59b98ee6ef26c993d0d75c83\` ON \`tagset\`` + ); + await queryRunner.query( + `ALTER TABLE \`ai_persona_service\` DROP INDEX \`IDX_79206feb0038b1c5597668dc4b\`` + ); + await queryRunner.query( + `ALTER TABLE \`ai_server\` DROP INDEX \`IDX_9d520fa5fed56042918e48fc4b\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_hub\` DROP INDEX \`IDX_36c8905c2c6c59467c60d94fd8\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_hub\` DROP INDEX \`IDX_b411e4f27d77a96eccdabbf4b4\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_hub\` DROP INDEX \`IDX_8f35d04d098bb6c7c57a9a83ac\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_hub\` DROP INDEX \`IDX_1d39dac2c6d2f17286d90c306b\`` + ); + await queryRunner.query( + `ALTER TABLE \`account\` DROP INDEX \`IDX_950221e932175dc7cf7c006488\`` + ); + await queryRunner.query( + `ALTER TABLE \`account\` DROP INDEX \`IDX_833582df0c439ab8c9adc5240d\`` + ); + await queryRunner.query( + `ALTER TABLE \`account\` DROP INDEX \`IDX_91a165c1091a6959cc19d52239\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_pack\` DROP INDEX \`IDX_a1441e46c8d36090e1f6477cea\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_pack\` DROP INDEX \`IDX_5facd6d188068a5a1c5b6f07fc\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_pack\` DROP INDEX \`IDX_8af8122897b05315e7eb892525\`` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` DROP INDEX \`IDX_55b8101bdf4f566645e928c26e\`` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` DROP INDEX \`IDX_a8890dcd65b8c3ee6e160d33f3\`` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` DROP INDEX \`IDX_4504c37764f6962ccbd165a21d\`` + ); + await queryRunner.query( + `ALTER TABLE \`virtual_contributor\` DROP INDEX \`IDX_e2eaa2213ac4454039cd8abc07\`` + ); + await queryRunner.query( + `ALTER TABLE \`ai_persona\` DROP INDEX \`IDX_293f0d3ef60cb0ca0006044ecf\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP INDEX \`IDX_6b1efee39d076d9f7ecb8fef4c\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP INDEX \`IDX_43559aeadc1a5169d17e81b3d4\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP INDEX \`IDX_980c4643d7d9de1b97bc39f518\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP INDEX \`IDX_9c664d684f987a735678b0ba82\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP INDEX \`IDX_68fa2c2b00cc1ed77e7c225e8b\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP INDEX \`IDX_cc0b08eb9679d3daa95153c2af\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP INDEX \`IDX_ea06eb8894469a0f262d929bf0\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP INDEX \`IDX_b4250035291aac1329d59224a9\`` + ); + await queryRunner.query( + `ALTER TABLE \`space\` DROP INDEX \`IDX_8d03fd2c8e8411ec9192c79cd9\`` + ); + await queryRunner.query( + `ALTER TABLE \`space_defaults\` DROP INDEX \`IDX_592a23e68922853bae6ebecd85\`` + ); + await queryRunner.query( + `ALTER TABLE \`space_defaults\` DROP INDEX \`IDX_413ba75964e5a534e4bfa54846\`` + ); + await queryRunner.query( + `ALTER TABLE \`templates_set\` DROP INDEX \`IDX_eb0176ef4b98c143322aa6f809\`` + ); + await queryRunner.query( + `ALTER TABLE \`template\` DROP INDEX \`IDX_21fdaf6dc88bdd6e8839e29b0b\`` + ); + await queryRunner.query( + `ALTER TABLE \`template\` DROP INDEX \`IDX_f09090a77e07377eefb3f731d9\`` + ); + await queryRunner.query( + `ALTER TABLE \`template\` DROP INDEX \`IDX_c6e4d1a07781a809ad3b3ee826\`` + ); + await queryRunner.query( + `ALTER TABLE \`template\` DROP INDEX \`IDX_eedeae5e63f9a9c3a0161541e9\`` + ); + await queryRunner.query( + `ALTER TABLE \`template\` DROP INDEX \`IDX_45cf273f30c1fa509456b6b0dd\`` + ); + await queryRunner.query( + `ALTER TABLE \`template\` DROP INDEX \`IDX_f58c3b144b6e010969e199beef\`` + ); + await queryRunner.query( + `ALTER TABLE \`template\` DROP INDEX \`IDX_4318f97beabd362a8a09e9d320\`` + ); + await queryRunner.query( + `ALTER TABLE \`context\` DROP INDEX \`IDX_a03169c3f86480ba3863924f4d\`` + ); + await queryRunner.query( + `ALTER TABLE \`context\` DROP INDEX \`IDX_5f0dbc3b097ef297bd5f4ddb1a\`` + ); + await queryRunner.query( + `ALTER TABLE \`ecosystem_model\` DROP INDEX \`IDX_658580aea4e1a892227e27db90\`` + ); + await queryRunner.query( + `ALTER TABLE \`actor_group\` DROP INDEX \`IDX_bde98d59e8984e7d17034c3b93\`` + ); + await queryRunner.query( + `ALTER TABLE \`actor\` DROP INDEX \`IDX_a2afa3851ea733de932251b3a1\`` + ); + await queryRunner.query( + `ALTER TABLE \`community\` DROP INDEX \`IDX_3b8f390d76263ef5996869da07\`` + ); + await queryRunner.query( + `ALTER TABLE \`community\` DROP INDEX \`IDX_2e7dd2fa8c829352cfbecb2cc9\`` + ); + await queryRunner.query( + `ALTER TABLE \`community\` DROP INDEX \`IDX_7fbe50fa78a37776ad962cb764\`` + ); + await queryRunner.query( + `ALTER TABLE \`community\` DROP INDEX \`IDX_6e7584bfb417bd0f8e8696ab58\`` + ); + await queryRunner.query( + `ALTER TABLE \`role_set\` DROP INDEX \`IDX_00905b142498f63e76d38fb254\`` + ); + await queryRunner.query( + `ALTER TABLE \`role_set\` DROP INDEX \`IDX_b038f74c8d4eadb839e78b99ce\`` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` DROP INDEX \`IDX_b0c80ccf319a1c7a7af12b3998\`` + ); + await queryRunner.query( + `ALTER TABLE \`invitation\` DROP INDEX \`IDX_b132226941570cb650a4023d49\`` + ); + await queryRunner.query( + `ALTER TABLE \`application\` DROP INDEX \`IDX_7ec2857c7d8d16432ffca1cb3d\`` + ); + await queryRunner.query( + `ALTER TABLE \`application\` DROP INDEX \`IDX_56f5614fff0028d40370499582\`` + ); + await queryRunner.query( + `ALTER TABLE \`user\` DROP INDEX \`IDX_10458c50c10436b6d589b40e5c\`` + ); + await queryRunner.query( + `ALTER TABLE \`user\` DROP INDEX \`IDX_028322b763dc94242dc9f638f9\`` + ); + await queryRunner.query( + `ALTER TABLE \`user\` DROP INDEX \`IDX_b61c694cacfab25533bd23d9ad\`` + ); + await queryRunner.query( + `ALTER TABLE \`user\` DROP INDEX \`IDX_9466682df91534dd95e4dbaa61\`` + ); + await queryRunner.query( + `ALTER TABLE \`user\` DROP INDEX \`IDX_09f909622aa177a097256b7cc2\`` + ); + await queryRunner.query( + `ALTER TABLE \`platform_invitation\` DROP INDEX \`IDX_c0448d2c992a62c9c11bd0f142\`` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` DROP INDEX \`IDX_425bbb4b951f7f4629710763fc\`` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` DROP INDEX \`IDX_f516dd9a46616999c7e9a6adc1\`` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` DROP INDEX \`IDX_ca469f5ec53a7719d155d60aca\`` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` DROP INDEX \`IDX_dd88d373c64b04e24705d575c9\`` + ); + await queryRunner.query( + `ALTER TABLE \`platform\` DROP INDEX \`IDX_9f621c51dd854634d8766a9cfa\`` + ); + await queryRunner.query( + `ALTER TABLE \`forum\` DROP INDEX \`IDX_3b0c92945f36d06f37de80285d\`` + ); + await queryRunner.query( + `ALTER TABLE \`discussion\` DROP INDEX \`IDX_5337074c9b818bb63e6f314c80\`` + ); + await queryRunner.query( + `ALTER TABLE \`discussion\` DROP INDEX \`IDX_2d8a3ca181c3f0346817685d21\`` + ); + await queryRunner.query( + `ALTER TABLE \`discussion\` DROP INDEX \`IDX_4555dccdda9ba57d8e3a634cd0\`` + ); + await queryRunner.query( + `ALTER TABLE \`licensing\` DROP INDEX \`IDX_a5dae5a376dd49c7c076893d40\`` + ); + await queryRunner.query( + `ALTER TABLE \`licensing\` DROP INDEX \`IDX_0c6a4d0a6c13a3f5df6ac01509\`` + ); + await queryRunner.query( + `ALTER TABLE \`license_policy\` DROP INDEX \`IDX_23d4d78ea8db637df031f86f03\`` + ); + await queryRunner.query( + `ALTER TABLE \`library\` DROP INDEX \`IDX_3879db652f2421337691219ace\`` + ); + await queryRunner.query( + `ALTER TABLE \`community_guidelines\` DROP INDEX \`IDX_3d60fe4fa40d54bad7d51bb4bd\`` + ); + await queryRunner.query( + `ALTER TABLE \`community_guidelines\` DROP INDEX \`IDX_684b272e6f7459439d41d2879e\`` + ); + await queryRunner.query( + `ALTER TABLE \`communication\` DROP INDEX \`IDX_eb99e588873c788a68a035478a\`` + ); + await queryRunner.query( + `ALTER TABLE \`communication\` DROP INDEX \`IDX_a20c5901817dd09d5906537e08\`` + ); + await queryRunner.query( + `ALTER TABLE \`user_group\` DROP INDEX \`IDX_9912e4cfc1e09848a392a65151\`` + ); + await queryRunner.query( + `ALTER TABLE \`user_group\` DROP INDEX \`IDX_e8e32f1e59c349b406a4752e54\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` DROP INDEX \`IDX_395aa74996a1f978b4969d114b\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` DROP INDEX \`IDX_58fd47c4a6ac8df9fe2bcaed87\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` DROP INDEX \`IDX_5a72d5b37312bac2e0a0115718\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` DROP INDEX \`IDX_7f1bec8979b57ed7ebd392a2ca\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` DROP INDEX \`IDX_d2cb77c14644156ec8e865608e\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization\` DROP INDEX \`IDX_e0e150e4f11d906b931b46a2d8\`` + ); + await queryRunner.query( + `ALTER TABLE \`agent\` DROP INDEX \`IDX_8ed9d1af584fa62f1ad3405b33\`` + ); + await queryRunner.query( + `ALTER TABLE \`preference_set\` DROP INDEX \`IDX_8e76dcf171c45875c44febb1d8\`` + ); + await queryRunner.query( + `ALTER TABLE \`preference\` DROP INDEX \`IDX_b4cf0f96bf08cf396f68355522\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization_verification\` DROP INDEX \`IDX_1cc3b275fc2a9d9d9b0ae33b31\`` + ); + await queryRunner.query( + `ALTER TABLE \`organization_verification\` DROP INDEX \`IDX_c66eddab0caacb1ef8d46bcafd\`` + ); + await queryRunner.query( + `ALTER TABLE \`collaboration\` DROP INDEX \`IDX_35c6b1de6d4d89dfe8e9c85d77\`` + ); + await queryRunner.query( + `ALTER TABLE \`collaboration\` DROP INDEX \`IDX_f67a2d25c945269d602c182fbc\`` + ); + await queryRunner.query( + `ALTER TABLE \`collaboration\` DROP INDEX \`IDX_b7ece56376ac7ca0b9a56c33b3\`` + ); + await queryRunner.query( + `ALTER TABLE \`collaboration\` DROP INDEX \`IDX_262ecf3f5d70b82a4833618425\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_flow\` DROP INDEX \`IDX_96a8cbe1706f459fd7d883be9b\`` + ); + await queryRunner.query( + `ALTER TABLE \`innovation_flow\` DROP INDEX \`IDX_a6e050daa4c7a3ab1e411c3651\`` + ); + await queryRunner.query( + `ALTER TABLE \`timeline\` DROP INDEX \`IDX_56aae15a664b2889a1a11c2cf8\`` + ); + await queryRunner.query( + `ALTER TABLE \`timeline\` DROP INDEX \`IDX_5fe58ece01b48496aebc04733d\`` + ); + await queryRunner.query( + `ALTER TABLE \`calendar\` DROP INDEX \`IDX_6e74d59afda096b68d12a69969\`` + ); + await queryRunner.query( + `ALTER TABLE \`calendar_event\` DROP INDEX \`IDX_b5069b11030e9608ee4468f850\`` + ); + await queryRunner.query( + `ALTER TABLE \`calendar_event\` DROP INDEX \`IDX_9349e137959f3ca5818c2e62b3\`` + ); + await queryRunner.query( + `ALTER TABLE \`calendar_event\` DROP INDEX \`IDX_8ee86afa2808a4ab523b9ee6c5\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` DROP INDEX \`IDX_62ed316cda7b75735b20307b47\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` DROP INDEX \`IDX_36b0da55acff774d0845aeb55f\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` DROP INDEX \`IDX_1e740008a7e1512966e3b08414\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` DROP INDEX \`IDX_cf776244b01436d8ca5cc76284\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout\` DROP INDEX \`IDX_6289dee12effb51320051c6f1f\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` DROP INDEX \`IDX_bdf2d0eced5c95968a85caaaae\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` DROP INDEX \`IDX_97fefc97fb254c30577696e1c0\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` DROP INDEX \`IDX_5e34f9a356f6254b8da24f8947\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout_contribution\` DROP INDEX \`IDX_dfa86c46f509a61c6510536cd9\`` + ); + await queryRunner.query( + `ALTER TABLE \`link\` DROP INDEX \`IDX_3bfc8c1aaec1395cc148268d3c\`` + ); + await queryRunner.query( + `ALTER TABLE \`link\` DROP INDEX \`IDX_07f249ac87502495710a62c5c0\`` + ); + await queryRunner.query( + `ALTER TABLE \`post\` DROP INDEX \`IDX_042b9825d770d6b3009ae206c2\`` + ); + await queryRunner.query( + `ALTER TABLE \`post\` DROP INDEX \`IDX_970844fcd10c2b6df7c1b49eac\`` + ); + await queryRunner.query( + `ALTER TABLE \`post\` DROP INDEX \`IDX_390343b22abec869bf80041933\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout_framing\` DROP INDEX \`IDX_8bc0e1f40be5816d3a593cbf7f\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout_framing\` DROP INDEX \`IDX_f53e2d266432e58e538a366705\`` + ); + await queryRunner.query( + `ALTER TABLE \`callout_framing\` DROP INDEX \`IDX_c9d7c2c4eb8a1d012ddc6605da\`` + ); + await queryRunner.query( + `ALTER TABLE \`whiteboard\` DROP INDEX \`IDX_3f9e9e2798d2a4d84b16ee8477\`` + ); + await queryRunner.query( + `ALTER TABLE \`whiteboard\` DROP INDEX \`IDX_d3b86160bb7d704212382b0ca4\`` + ); + await queryRunner.query( + `ALTER TABLE \`room\` DROP INDEX \`IDX_d1d94dd8e0c417b4188a05ccbc\`` + ); + await queryRunner.query( + `ALTER TABLE \`reference\` DROP INDEX \`IDX_73e8ae665a49366ca7e2866a45\`` + ); + await queryRunner.query( + `ALTER TABLE \`profile\` DROP INDEX \`IDX_4a1c74fd2a61b32d9d9500e065\`` + ); + await queryRunner.query( + `ALTER TABLE \`profile\` DROP INDEX \`IDX_432056041df0e4337b17ff7b09\`` + ); + await queryRunner.query( + `ALTER TABLE \`profile\` DROP INDEX \`IDX_a96475631aba7dce41db03cc8b\`` + ); + await queryRunner.query( + `ALTER TABLE \`storage_bucket\` DROP INDEX \`IDX_f2f48b57269987b13b415a0058\`` + ); + await queryRunner.query( + `ALTER TABLE \`storage_aggregator\` DROP INDEX \`IDX_0647707288c243e60091c8d862\`` + ); + await queryRunner.query( + `ALTER TABLE \`storage_aggregator\` DROP INDEX \`IDX_f3b4d59c0b805c9c1ecb0070e1\`` + ); + await queryRunner.query( + `ALTER TABLE \`document\` DROP INDEX \`IDX_9fb9257b14ec21daf5bc9aa4c8\`` + ); + await queryRunner.query( + `ALTER TABLE \`document\` DROP INDEX \`IDX_d9e2dfcccf59233c17cc6bc641\`` + ); + await queryRunner.query( + `ALTER TABLE \`visual\` DROP INDEX \`IDX_4fbd109f9bb84f58b7a3c60649\`` + ); + await queryRunner.query( + `ALTER TABLE \`tagset\` DROP INDEX \`IDX_eb59b98ee6ef26c993d0d75c83\`` + ); + } +} diff --git a/src/migrations/1728033296139-fixIndexesFKsRelations-2.ts b/src/migrations/1728033296139-fixIndexesFKsRelations-2.ts new file mode 100644 index 0000000000..c2813704db --- /dev/null +++ b/src/migrations/1728033296139-fixIndexesFKsRelations-2.ts @@ -0,0 +1,779 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FixIndexesFKsRelations1728033296139 implements MigrationInterface { + name = 'FixIndexesFKsRelations1728033296139'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `DROP INDEX \`IDX_eb59b98ee6ef26c993d0d75c83\` ON \`tagset\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_4fbd109f9bb84f58b7a3c60649\` ON \`visual\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_9fb9257b14ec21daf5bc9aa4c8\` ON \`document\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_d9e2dfcccf59233c17cc6bc641\` ON \`document\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_0647707288c243e60091c8d862\` ON \`storage_aggregator\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_f3b4d59c0b805c9c1ecb0070e1\` ON \`storage_aggregator\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_f2f48b57269987b13b415a0058\` ON \`storage_bucket\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_432056041df0e4337b17ff7b09\` ON \`profile\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_4a1c74fd2a61b32d9d9500e065\` ON \`profile\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_a96475631aba7dce41db03cc8b\` ON \`profile\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_73e8ae665a49366ca7e2866a45\` ON \`reference\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_d1d94dd8e0c417b4188a05ccbc\` ON \`room\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_3f9e9e2798d2a4d84b16ee8477\` ON \`whiteboard\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_d3b86160bb7d704212382b0ca4\` ON \`whiteboard\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_8bc0e1f40be5816d3a593cbf7f\` ON \`callout_framing\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_c9d7c2c4eb8a1d012ddc6605da\` ON \`callout_framing\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_f53e2d266432e58e538a366705\` ON \`callout_framing\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_042b9825d770d6b3009ae206c2\` ON \`post\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_390343b22abec869bf80041933\` ON \`post\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_970844fcd10c2b6df7c1b49eac\` ON \`post\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_07f249ac87502495710a62c5c0\` ON \`link\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_3bfc8c1aaec1395cc148268d3c\` ON \`link\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_5e34f9a356f6254b8da24f8947\` ON \`callout_contribution\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_97fefc97fb254c30577696e1c0\` ON \`callout_contribution\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_bdf2d0eced5c95968a85caaaae\` ON \`callout_contribution\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_dfa86c46f509a61c6510536cd9\` ON \`callout_contribution\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_1e740008a7e1512966e3b08414\` ON \`callout\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_36b0da55acff774d0845aeb55f\` ON \`callout\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_6289dee12effb51320051c6f1f\` ON \`callout\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_62ed316cda7b75735b20307b47\` ON \`callout\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_cf776244b01436d8ca5cc76284\` ON \`callout\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_8ee86afa2808a4ab523b9ee6c5\` ON \`calendar_event\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_9349e137959f3ca5818c2e62b3\` ON \`calendar_event\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_b5069b11030e9608ee4468f850\` ON \`calendar_event\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_6e74d59afda096b68d12a69969\` ON \`calendar\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_56aae15a664b2889a1a11c2cf8\` ON \`timeline\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_5fe58ece01b48496aebc04733d\` ON \`timeline\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_96a8cbe1706f459fd7d883be9b\` ON \`innovation_flow\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_a6e050daa4c7a3ab1e411c3651\` ON \`innovation_flow\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_262ecf3f5d70b82a4833618425\` ON \`collaboration\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_35c6b1de6d4d89dfe8e9c85d77\` ON \`collaboration\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_b7ece56376ac7ca0b9a56c33b3\` ON \`collaboration\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_f67a2d25c945269d602c182fbc\` ON \`collaboration\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_1cc3b275fc2a9d9d9b0ae33b31\` ON \`organization_verification\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_c66eddab0caacb1ef8d46bcafd\` ON \`organization_verification\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_b4cf0f96bf08cf396f68355522\` ON \`preference\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_8e76dcf171c45875c44febb1d8\` ON \`preference_set\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_8ed9d1af584fa62f1ad3405b33\` ON \`agent\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_395aa74996a1f978b4969d114b\` ON \`organization\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_58fd47c4a6ac8df9fe2bcaed87\` ON \`organization\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_5a72d5b37312bac2e0a0115718\` ON \`organization\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_7f1bec8979b57ed7ebd392a2ca\` ON \`organization\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_d2cb77c14644156ec8e865608e\` ON \`organization\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_e0e150e4f11d906b931b46a2d8\` ON \`organization\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_9912e4cfc1e09848a392a65151\` ON \`user_group\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_e8e32f1e59c349b406a4752e54\` ON \`user_group\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_a20c5901817dd09d5906537e08\` ON \`communication\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_eb99e588873c788a68a035478a\` ON \`communication\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_3d60fe4fa40d54bad7d51bb4bd\` ON \`community_guidelines\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_684b272e6f7459439d41d2879e\` ON \`community_guidelines\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_3879db652f2421337691219ace\` ON \`library\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_23d4d78ea8db637df031f86f03\` ON \`license_policy\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_0c6a4d0a6c13a3f5df6ac01509\` ON \`licensing\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_a5dae5a376dd49c7c076893d40\` ON \`licensing\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_2d8a3ca181c3f0346817685d21\` ON \`discussion\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_4555dccdda9ba57d8e3a634cd0\` ON \`discussion\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_5337074c9b818bb63e6f314c80\` ON \`discussion\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_3b0c92945f36d06f37de80285d\` ON \`forum\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_425bbb4b951f7f4629710763fc\` ON \`platform\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_9f621c51dd854634d8766a9cfa\` ON \`platform\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_ca469f5ec53a7719d155d60aca\` ON \`platform\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_dd88d373c64b04e24705d575c9\` ON \`platform\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_f516dd9a46616999c7e9a6adc1\` ON \`platform\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_c0448d2c992a62c9c11bd0f142\` ON \`platform_invitation\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_028322b763dc94242dc9f638f9\` ON \`user\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_09f909622aa177a097256b7cc2\` ON \`user\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_10458c50c10436b6d589b40e5c\` ON \`user\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_9466682df91534dd95e4dbaa61\` ON \`user\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_b61c694cacfab25533bd23d9ad\` ON \`user\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_56f5614fff0028d40370499582\` ON \`application\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_7ec2857c7d8d16432ffca1cb3d\` ON \`application\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_b0c80ccf319a1c7a7af12b3998\` ON \`invitation\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_b132226941570cb650a4023d49\` ON \`invitation\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_00905b142498f63e76d38fb254\` ON \`role_set\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_b038f74c8d4eadb839e78b99ce\` ON \`role_set\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_2e7dd2fa8c829352cfbecb2cc9\` ON \`community\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_3b8f390d76263ef5996869da07\` ON \`community\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_6e7584bfb417bd0f8e8696ab58\` ON \`community\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_7fbe50fa78a37776ad962cb764\` ON \`community\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_a2afa3851ea733de932251b3a1\` ON \`actor\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_bde98d59e8984e7d17034c3b93\` ON \`actor_group\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_658580aea4e1a892227e27db90\` ON \`ecosystem_model\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_5f0dbc3b097ef297bd5f4ddb1a\` ON \`context\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_a03169c3f86480ba3863924f4d\` ON \`context\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_21fdaf6dc88bdd6e8839e29b0b\` ON \`template\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_4318f97beabd362a8a09e9d320\` ON \`template\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_45cf273f30c1fa509456b6b0dd\` ON \`template\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_c6e4d1a07781a809ad3b3ee826\` ON \`template\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_eedeae5e63f9a9c3a0161541e9\` ON \`template\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_f09090a77e07377eefb3f731d9\` ON \`template\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_f58c3b144b6e010969e199beef\` ON \`template\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_eb0176ef4b98c143322aa6f809\` ON \`templates_set\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_413ba75964e5a534e4bfa54846\` ON \`space_defaults\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_592a23e68922853bae6ebecd85\` ON \`space_defaults\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_43559aeadc1a5169d17e81b3d4\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_68fa2c2b00cc1ed77e7c225e8b\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_6b1efee39d076d9f7ecb8fef4c\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_8d03fd2c8e8411ec9192c79cd9\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_980c4643d7d9de1b97bc39f518\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_9c664d684f987a735678b0ba82\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_b4250035291aac1329d59224a9\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_cc0b08eb9679d3daa95153c2af\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_ea06eb8894469a0f262d929bf0\` ON \`space\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_293f0d3ef60cb0ca0006044ecf\` ON \`ai_persona\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_4504c37764f6962ccbd165a21d\` ON \`virtual_contributor\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_55b8101bdf4f566645e928c26e\` ON \`virtual_contributor\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_a8890dcd65b8c3ee6e160d33f3\` ON \`virtual_contributor\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_e2eaa2213ac4454039cd8abc07\` ON \`virtual_contributor\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_5facd6d188068a5a1c5b6f07fc\` ON \`innovation_pack\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_8af8122897b05315e7eb892525\` ON \`innovation_pack\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_a1441e46c8d36090e1f6477cea\` ON \`innovation_pack\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_833582df0c439ab8c9adc5240d\` ON \`account\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_91a165c1091a6959cc19d52239\` ON \`account\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_950221e932175dc7cf7c006488\` ON \`account\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_36c8905c2c6c59467c60d94fd8\` ON \`innovation_hub\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_b411e4f27d77a96eccdabbf4b4\` ON \`innovation_hub\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_9d520fa5fed56042918e48fc4b\` ON \`ai_server\`` + ); + await queryRunner.query( + `DROP INDEX \`IDX_79206feb0038b1c5597668dc4b\` ON \`ai_persona_service\`` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_79206feb0038b1c5597668dc4b\` ON \`ai_persona_service\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_9d520fa5fed56042918e48fc4b\` ON \`ai_server\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_b411e4f27d77a96eccdabbf4b4\` ON \`innovation_hub\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_36c8905c2c6c59467c60d94fd8\` ON \`innovation_hub\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_950221e932175dc7cf7c006488\` ON \`account\` (\`storageAggregatorId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_91a165c1091a6959cc19d52239\` ON \`account\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_833582df0c439ab8c9adc5240d\` ON \`account\` (\`agentId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_a1441e46c8d36090e1f6477cea\` ON \`innovation_pack\` (\`templatesSetId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_8af8122897b05315e7eb892525\` ON \`innovation_pack\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_5facd6d188068a5a1c5b6f07fc\` ON \`innovation_pack\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_e2eaa2213ac4454039cd8abc07\` ON \`virtual_contributor\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_a8890dcd65b8c3ee6e160d33f3\` ON \`virtual_contributor\` (\`agentId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_55b8101bdf4f566645e928c26e\` ON \`virtual_contributor\` (\`aiPersonaId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_4504c37764f6962ccbd165a21d\` ON \`virtual_contributor\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_293f0d3ef60cb0ca0006044ecf\` ON \`ai_persona\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_ea06eb8894469a0f262d929bf0\` ON \`space\` (\`collaborationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_cc0b08eb9679d3daa95153c2af\` ON \`space\` (\`contextId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_b4250035291aac1329d59224a9\` ON \`space\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_9c664d684f987a735678b0ba82\` ON \`space\` (\`agentId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_980c4643d7d9de1b97bc39f518\` ON \`space\` (\`storageAggregatorId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_8d03fd2c8e8411ec9192c79cd9\` ON \`space\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_6b1efee39d076d9f7ecb8fef4c\` ON \`space\` (\`defaultsId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_68fa2c2b00cc1ed77e7c225e8b\` ON \`space\` (\`communityId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_43559aeadc1a5169d17e81b3d4\` ON \`space\` (\`libraryId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_592a23e68922853bae6ebecd85\` ON \`space_defaults\` (\`innovationFlowTemplateId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_413ba75964e5a534e4bfa54846\` ON \`space_defaults\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_eb0176ef4b98c143322aa6f809\` ON \`templates_set\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_f58c3b144b6e010969e199beef\` ON \`template\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_f09090a77e07377eefb3f731d9\` ON \`template\` (\`whiteboardId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_eedeae5e63f9a9c3a0161541e9\` ON \`template\` (\`communityGuidelinesId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_c6e4d1a07781a809ad3b3ee826\` ON \`template\` (\`calloutId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_45cf273f30c1fa509456b6b0dd\` ON \`template\` (\`innovationFlowId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_4318f97beabd362a8a09e9d320\` ON \`template\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_21fdaf6dc88bdd6e8839e29b0b\` ON \`template\` (\`collaborationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_a03169c3f86480ba3863924f4d\` ON \`context\` (\`ecosystemModelId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_5f0dbc3b097ef297bd5f4ddb1a\` ON \`context\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_658580aea4e1a892227e27db90\` ON \`ecosystem_model\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_bde98d59e8984e7d17034c3b93\` ON \`actor_group\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_a2afa3851ea733de932251b3a1\` ON \`actor\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_7fbe50fa78a37776ad962cb764\` ON \`community\` (\`communicationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_6e7584bfb417bd0f8e8696ab58\` ON \`community\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_3b8f390d76263ef5996869da07\` ON \`community\` (\`roleSetId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_2e7dd2fa8c829352cfbecb2cc9\` ON \`community\` (\`guidelinesId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_b038f74c8d4eadb839e78b99ce\` ON \`role_set\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_00905b142498f63e76d38fb254\` ON \`role_set\` (\`applicationFormId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_b132226941570cb650a4023d49\` ON \`invitation\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_b0c80ccf319a1c7a7af12b3998\` ON \`invitation\` (\`lifecycleId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_7ec2857c7d8d16432ffca1cb3d\` ON \`application\` (\`lifecycleId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_56f5614fff0028d40370499582\` ON \`application\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_b61c694cacfab25533bd23d9ad\` ON \`user\` (\`agentId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_9466682df91534dd95e4dbaa61\` ON \`user\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_10458c50c10436b6d589b40e5c\` ON \`user\` (\`storageAggregatorId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_09f909622aa177a097256b7cc2\` ON \`user\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_028322b763dc94242dc9f638f9\` ON \`user\` (\`preferenceSetId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_c0448d2c992a62c9c11bd0f142\` ON \`platform_invitation\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_f516dd9a46616999c7e9a6adc1\` ON \`platform\` (\`storageAggregatorId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_dd88d373c64b04e24705d575c9\` ON \`platform\` (\`forumId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_ca469f5ec53a7719d155d60aca\` ON \`platform\` (\`libraryId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_9f621c51dd854634d8766a9cfa\` ON \`platform\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_425bbb4b951f7f4629710763fc\` ON \`platform\` (\`licensingId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_3b0c92945f36d06f37de80285d\` ON \`forum\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_5337074c9b818bb63e6f314c80\` ON \`discussion\` (\`commentsId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_4555dccdda9ba57d8e3a634cd0\` ON \`discussion\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_2d8a3ca181c3f0346817685d21\` ON \`discussion\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_a5dae5a376dd49c7c076893d40\` ON \`licensing\` (\`licensePolicyId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_0c6a4d0a6c13a3f5df6ac01509\` ON \`licensing\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_23d4d78ea8db637df031f86f03\` ON \`license_policy\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_3879db652f2421337691219ace\` ON \`library\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_684b272e6f7459439d41d2879e\` ON \`community_guidelines\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_3d60fe4fa40d54bad7d51bb4bd\` ON \`community_guidelines\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_eb99e588873c788a68a035478a\` ON \`communication\` (\`updatesId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_a20c5901817dd09d5906537e08\` ON \`communication\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_e8e32f1e59c349b406a4752e54\` ON \`user_group\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_9912e4cfc1e09848a392a65151\` ON \`user_group\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_e0e150e4f11d906b931b46a2d8\` ON \`organization\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_d2cb77c14644156ec8e865608e\` ON \`organization\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_7f1bec8979b57ed7ebd392a2ca\` ON \`organization\` (\`agentId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_5a72d5b37312bac2e0a0115718\` ON \`organization\` (\`verificationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_58fd47c4a6ac8df9fe2bcaed87\` ON \`organization\` (\`preferenceSetId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_395aa74996a1f978b4969d114b\` ON \`organization\` (\`storageAggregatorId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_8ed9d1af584fa62f1ad3405b33\` ON \`agent\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_8e76dcf171c45875c44febb1d8\` ON \`preference_set\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_b4cf0f96bf08cf396f68355522\` ON \`preference\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_c66eddab0caacb1ef8d46bcafd\` ON \`organization_verification\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_1cc3b275fc2a9d9d9b0ae33b31\` ON \`organization_verification\` (\`lifecycleId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_f67a2d25c945269d602c182fbc\` ON \`collaboration\` (\`timelineId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_b7ece56376ac7ca0b9a56c33b3\` ON \`collaboration\` (\`tagsetTemplateSetId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_35c6b1de6d4d89dfe8e9c85d77\` ON \`collaboration\` (\`innovationFlowId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_262ecf3f5d70b82a4833618425\` ON \`collaboration\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_a6e050daa4c7a3ab1e411c3651\` ON \`innovation_flow\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_96a8cbe1706f459fd7d883be9b\` ON \`innovation_flow\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_5fe58ece01b48496aebc04733d\` ON \`timeline\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_56aae15a664b2889a1a11c2cf8\` ON \`timeline\` (\`calendarId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_6e74d59afda096b68d12a69969\` ON \`calendar\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_b5069b11030e9608ee4468f850\` ON \`calendar_event\` (\`commentsId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_9349e137959f3ca5818c2e62b3\` ON \`calendar_event\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_8ee86afa2808a4ab523b9ee6c5\` ON \`calendar_event\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_cf776244b01436d8ca5cc76284\` ON \`callout\` (\`framingId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_62ed316cda7b75735b20307b47\` ON \`callout\` (\`commentsId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_6289dee12effb51320051c6f1f\` ON \`callout\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_36b0da55acff774d0845aeb55f\` ON \`callout\` (\`contributionDefaultsId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_1e740008a7e1512966e3b08414\` ON \`callout\` (\`contributionPolicyId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_dfa86c46f509a61c6510536cd9\` ON \`callout_contribution\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_bdf2d0eced5c95968a85caaaae\` ON \`callout_contribution\` (\`linkId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_97fefc97fb254c30577696e1c0\` ON \`callout_contribution\` (\`postId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_5e34f9a356f6254b8da24f8947\` ON \`callout_contribution\` (\`whiteboardId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_3bfc8c1aaec1395cc148268d3c\` ON \`link\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_07f249ac87502495710a62c5c0\` ON \`link\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_970844fcd10c2b6df7c1b49eac\` ON \`post\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_390343b22abec869bf80041933\` ON \`post\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_042b9825d770d6b3009ae206c2\` ON \`post\` (\`commentsId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_f53e2d266432e58e538a366705\` ON \`callout_framing\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_c9d7c2c4eb8a1d012ddc6605da\` ON \`callout_framing\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_8bc0e1f40be5816d3a593cbf7f\` ON \`callout_framing\` (\`whiteboardId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_d3b86160bb7d704212382b0ca4\` ON \`whiteboard\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_3f9e9e2798d2a4d84b16ee8477\` ON \`whiteboard\` (\`profileId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_d1d94dd8e0c417b4188a05ccbc\` ON \`room\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_73e8ae665a49366ca7e2866a45\` ON \`reference\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_a96475631aba7dce41db03cc8b\` ON \`profile\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_4a1c74fd2a61b32d9d9500e065\` ON \`profile\` (\`storageBucketId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_432056041df0e4337b17ff7b09\` ON \`profile\` (\`locationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_f2f48b57269987b13b415a0058\` ON \`storage_bucket\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_f3b4d59c0b805c9c1ecb0070e1\` ON \`storage_aggregator\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_0647707288c243e60091c8d862\` ON \`storage_aggregator\` (\`directStorageId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_d9e2dfcccf59233c17cc6bc641\` ON \`document\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_9fb9257b14ec21daf5bc9aa4c8\` ON \`document\` (\`tagsetId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_4fbd109f9bb84f58b7a3c60649\` ON \`visual\` (\`authorizationId\`)` + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_eb59b98ee6ef26c993d0d75c83\` ON \`tagset\` (\`authorizationId\`)` + ); + } +} From 131ada80086af3374b2ee291f6815c366a221e0d Mon Sep 17 00:00:00 2001 From: Svetoslav Petkov Date: Fri, 4 Oct 2024 13:16:35 +0300 Subject: [PATCH 72/78] Organization verified status filter (#4592) --- .../organization/organization.resolver.queries.ts | 10 +++++++++- .../community/organization/organization.service.ts | 11 ++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/domain/community/organization/organization.resolver.queries.ts b/src/domain/community/organization/organization.resolver.queries.ts index 8889616a4f..72ea97bfcf 100644 --- a/src/domain/community/organization/organization.resolver.queries.ts +++ b/src/domain/community/organization/organization.resolver.queries.ts @@ -9,6 +9,7 @@ import { OrganizationFilterInput } from '@core/filtering'; import { UseGuards } from '@nestjs/common'; import { PaginatedOrganization } from '@core/pagination/paginated.organization'; import { ContributorQueryArgs } from '../contributor/dto/contributor.query.args'; +import { OrganizationVerificationEnum } from '@common/enums/organization.verification'; @Resolver() export class OrganizationResolverQueries { @@ -44,11 +45,18 @@ export class OrganizationResolverQueries { @Profiling.api async organizationsPaginated( @Args() pagination: PaginationArgs, + @Args('status', { + nullable: true, + description: 'Return only Organizations with this verification status', + type: () => OrganizationVerificationEnum, + }) + status?: OrganizationVerificationEnum, @Args('filter', { nullable: true }) filter?: OrganizationFilterInput ): Promise { return this.organizationService.getPaginatedOrganizations( pagination, - filter + filter, + status ); } } diff --git a/src/domain/community/organization/organization.service.ts b/src/domain/community/organization/organization.service.ts index fcca0f754d..a646fa324a 100644 --- a/src/domain/community/organization/organization.service.ts +++ b/src/domain/community/organization/organization.service.ts @@ -55,6 +55,7 @@ import { AgentType } from '@common/enums/agent.type'; import { ContributorService } from '../contributor/contributor.service'; import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type'; import { AccountType } from '@common/enums/account.type'; +import { OrganizationVerificationEnum } from '@common/enums/organization.verification'; @Injectable() export class OrganizationService { @@ -401,11 +402,19 @@ export class OrganizationService { async getPaginatedOrganizations( paginationArgs: PaginationArgs, - filter?: OrganizationFilterInput + filter?: OrganizationFilterInput, + status?: OrganizationVerificationEnum ): Promise> { const qb = this.organizationRepository.createQueryBuilder('organization'); qb.leftJoinAndSelect('organization.authorization', 'authorization_policy'); + if (status) { + qb.leftJoin('organization.verification', 'verification').where( + 'verification.status = :status', + { status: status } + ); + } + if (filter) { applyOrganizationFilter(qb, filter); } From 6dde27e6e57d59d4711ed50490a5f4d86645a6dd Mon Sep 17 00:00:00 2001 From: Valentin Yanakiev Date: Fri, 4 Oct 2024 16:02:33 +0300 Subject: [PATCH 73/78] Fixed logic to work for all users --- .../authentication.agent.info/agent.info.ts | 2 - .../authentication/authentication.service.ts | 25 ---- .../community/user/user.resolver.fields.ts | 8 +- src/domain/community/user/user.service.ts | 111 +++++++++++++++++- 4 files changed, 114 insertions(+), 32 deletions(-) diff --git a/src/core/authentication.agent.info/agent.info.ts b/src/core/authentication.agent.info/agent.info.ts index dcf68b4c60..ec0a756743 100644 --- a/src/core/authentication.agent.info/agent.info.ts +++ b/src/core/authentication.agent.info/agent.info.ts @@ -1,4 +1,3 @@ -import { AuthenticationType } from '@common/enums/authentication.type'; import { ICredential } from '@domain/agent/credential'; import { IVerifiedCredential } from '@domain/agent/verified-credential/verified.credential.interface'; export class AgentInfo { @@ -13,5 +12,4 @@ export class AgentInfo { agentID = ''; avatarURL = ''; expiry?: number = undefined; - authenticationType?: AuthenticationType; } diff --git a/src/core/authentication/authentication.service.ts b/src/core/authentication/authentication.service.ts index 5c0b2236a4..666366939e 100644 --- a/src/core/authentication/authentication.service.ts +++ b/src/core/authentication/authentication.service.ts @@ -23,7 +23,6 @@ import { AgentInfoCacheService } from '@core/authentication.agent.info/agent.inf import { getSession } from '@common/utils'; import ConfigUtils from '@config/config.utils'; import { AlkemioConfig } from '@src/types'; -import { AuthenticationType } from '@common/enums/authentication.type'; import { AgentInfoMetadata } from '@core/authentication.agent.info/agent.info.metadata'; @Injectable() @@ -122,7 +121,6 @@ export class AuthenticationService { if (cachedAgentInfo) return cachedAgentInfo; const agentInfo = this.buildBasicAgentInfo(oryIdentity, session); - agentInfo.authenticationType = this.mapAuthenticationType(session); const agentInfoMetadata = await this.getAgentInfoMetadata(agentInfo.email); if (!agentInfoMetadata) return agentInfo; @@ -196,29 +194,6 @@ export class AuthenticationService { return agentInfo; } - /** - * Maps the authentication type based on the provided session information. - * - * @param session - The session object containing authentication methods. - * @returns The corresponding `AuthenticationType` based on the provider and method. - * - * - If the provider is 'microsoft', returns `AuthenticationType.MICROSOFT`. - * - If the provider is 'linkedin', returns `AuthenticationType.LINKEDIN`. - * - If the method is 'password', returns `AuthenticationType.EMAIL`. - * - Otherwise, returns `AuthenticationType.UNKNOWN`. - */ - private mapAuthenticationType(session?: Session): AuthenticationType { - const authenticationMethod = session?.authentication_methods?.[0]; - const provider = authenticationMethod?.provider; - const method = authenticationMethod?.method; - - if (provider === 'microsoft') return AuthenticationType.MICROSOFT; - if (provider === 'linkedin') return AuthenticationType.LINKEDIN; - if (method === 'password') return AuthenticationType.EMAIL; - - return AuthenticationType.UNKNOWN; - } - /** * Retrieves the agent information metadata for a given email. * diff --git a/src/domain/community/user/user.resolver.fields.ts b/src/domain/community/user/user.resolver.fields.ts index a15219d0ae..560ed0a640 100644 --- a/src/domain/community/user/user.resolver.fields.ts +++ b/src/domain/community/user/user.resolver.fields.ts @@ -253,10 +253,10 @@ export class UserResolverFields { 'The Authentication Method used for this User. One of email, linkedin, microsoft, or unknown', }) @UseGuards(GraphqlGuard) - authenticationMethod( - @CurrentUser() agentInfo: AgentInfo - ): AuthenticationType { - return agentInfo.authenticationType ?? AuthenticationType.UNKNOWN; + async authenticationMethod( + @Parent() user: IUser + ): Promise { + return this.userService.getAuthenticationTypeByEmail(user.email); } private async isAccessGranted( diff --git a/src/domain/community/user/user.service.ts b/src/domain/community/user/user.service.ts index 87b501f5b2..6ea45d765a 100644 --- a/src/domain/community/user/user.service.ts +++ b/src/domain/community/user/user.service.ts @@ -63,10 +63,15 @@ import { AgentType } from '@common/enums/agent.type'; import { ContributorService } from '../contributor/contributor.service'; import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type'; import { AccountType } from '@common/enums/account.type'; +import { IdentityApi, Configuration, Identity } from '@ory/kratos-client'; +import { ConfigService } from '@nestjs/config'; +import { AlkemioConfig } from '@src/types'; +import { AuthenticationType } from '@common/enums/authentication.type'; @Injectable() export class UserService { cacheOptions: CachingConfig = { ttl: 300 }; + private readonly kratosIdentityClient: IdentityApi; constructor( private profileService: ProfileService, @@ -79,12 +84,116 @@ export class UserService { private storageAggregatorService: StorageAggregatorService, private accountHostService: AccountHostService, private contributorService: ContributorService, + private configService: ConfigService, @InjectRepository(User) private userRepository: Repository, @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService, @Inject(CACHE_MANAGER) private readonly cacheManager: Cache - ) {} + ) { + const { kratos_admin_base_url_server } = this.configService.get( + 'identity.authentication.providers.ory', + { + infer: true, + } + ); + const kratosAdminUrlServer = kratos_admin_base_url_server; + this.kratosIdentityClient = new IdentityApi( + new Configuration({ + basePath: kratosAdminUrlServer, + }) + ); + } + + /** + * Retrieves the authentication type associated with a given email. + * + * @param email - The email address to look up the authentication type for. + * @returns A promise that resolves to the authentication type. + */ + public async getAuthenticationTypeByEmail( + email: string + ): Promise { + const identity = await this.getIdentityByEmail(email); + if (!identity) return AuthenticationType.UNKNOWN; + return this.mapAuthenticationType(identity); + } + + /** + * Retrieves an identity by email. + * + * @param email - The email address to search for the identity. + * @returns A promise that resolves to the identity if found, or undefined if not found. + * + * @remarks + * This method uses the `kratosIdentityClient` to list identities with the specified email. + * If no identity is found, a warning is logged. + * + * @example + * ```typescript + * const identity = await getIdentityByEmail('example@example.com'); + * if (identity) { + * console.log('Identity found:', identity); + * } else { + * console.log('Identity not found.'); + * } + * ``` + */ + private async getIdentityByEmail( + email: string + ): Promise { + const { data: identity } = await this.kratosIdentityClient.listIdentities({ + credentialsIdentifier: email, + includeCredential: ['password', 'oidc'], + }); + if (!identity) { + this.logger.warn( + `Identity with email ${email} not found.`, + LogContext.USER + ); + return undefined; + } + if (identity.length === 0) { + this.logger.warn( + `Identity with email ${email} not found.`, + LogContext.USER + ); + return undefined; + } + return identity[0]; + } + + /** + * Maps the provided identity to an authentication type. + * + * @param identity - The identity object containing credentials. + * @returns The corresponding authentication type based on the identity's credentials. + * + * The function checks the following conditions in order: + * - If the identity has OIDC credentials, it examines the identifiers: + * - If the identifier starts with 'microsoft', it returns `AuthenticationType.MICROSOFT`. + * - If the identifier starts with 'linkedin', it returns `AuthenticationType.LINKEDIN`. + * - If the identity has password credentials, it returns `AuthenticationType.EMAIL`. + * - If none of the above conditions are met, it returns `AuthenticationType.UNKNOWN`. + */ + private mapAuthenticationType(identity: Identity): AuthenticationType { + if (identity.credentials) { + if (identity.credentials.oidc) { + const identifiers = identity.credentials.oidc.identifiers; + if (!identifiers) return AuthenticationType.UNKNOWN; + const identifier = identifiers[0]; + if (!identifier) return AuthenticationType.UNKNOWN; + if (identifier.startsWith('microsoft')) + return AuthenticationType.MICROSOFT; + if (identifier.startsWith('linkedin')) + return AuthenticationType.LINKEDIN; + } else { + if (identity.credentials.password) return AuthenticationType.EMAIL; + } + } + + return AuthenticationType.UNKNOWN; + } private getUserCommunicationIdCacheKey(communicationId: string): string { return `@user:communicationId:${communicationId}`; From 36875167c5f461c2c94f35fc2eeb7e822f189be1 Mon Sep 17 00:00:00 2001 From: Valentin Yanakiev Date: Fri, 4 Oct 2024 16:30:11 +0300 Subject: [PATCH 74/78] Fixed tests --- src/domain/community/user/user.service.spec.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/domain/community/user/user.service.spec.ts b/src/domain/community/user/user.service.spec.ts index be02fed9d3..dddba97634 100644 --- a/src/domain/community/user/user.service.spec.ts +++ b/src/domain/community/user/user.service.spec.ts @@ -5,6 +5,7 @@ import { MockCacheManager } from '@test/mocks/cache-manager.mock'; import { MockWinstonProvider } from '@test/mocks/winston.provider.mock'; import { defaultMockerFactory } from '@test/utils/default.mocker.factory'; import { repositoryProviderMockFactory } from '@test/utils/repository.provider.mock.factory'; +import { ConfigService } from '@nestjs/config'; describe('UserService', () => { let service: UserService; @@ -18,7 +19,13 @@ describe('UserService', () => { MockWinstonProvider, ], }) - .useMocker(defaultMockerFactory) + .useMocker(token => { + if (token === ConfigService) { + return ConfigServiceMock; + } + + return defaultMockerFactory(token); + }) .compile(); service = module.get(UserService); @@ -28,3 +35,9 @@ describe('UserService', () => { expect(service).toBeDefined(); }); }); + +const ConfigServiceMock = { + get: jest.fn().mockReturnValue({ + kratos_admin_base_url_server: 'mockUrl', + }), +}; From dd02655a49642244a13ea87afdafb18556a65bad Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Fri, 4 Oct 2024 16:25:25 +0200 Subject: [PATCH 75/78] split out kratos interaction to a separate module, will need it also to delete users later; made field protected --- src/common/enums/logging.context.ts | 1 + src/domain/community/user/user.module.ts | 4 +- .../community/user/user.resolver.fields.ts | 27 +++-- src/domain/community/user/user.service.ts | 102 +----------------- .../kratos-adapter/kratos.adapter.module.ts | 10 ++ .../adapters/kratos-adapter/kratos.adapter.ts | 100 +++++++++++++++++ 6 files changed, 131 insertions(+), 113 deletions(-) create mode 100644 src/services/adapters/kratos-adapter/kratos.adapter.module.ts create mode 100644 src/services/adapters/kratos-adapter/kratos.adapter.ts diff --git a/src/common/enums/logging.context.ts b/src/common/enums/logging.context.ts index 07c7c9e04b..9663b5c048 100644 --- a/src/common/enums/logging.context.ts +++ b/src/common/enums/logging.context.ts @@ -71,4 +71,5 @@ export enum LogContext { AI_PERSONA_SERVICE_ENGINE = 'ai-persona-service-engine', AI_SERVER_EVENT_BUS = 'ai-server-event-bus', SUBSCRIPTION_PUBLISH = 'subscription-publish', + KRATOS = 'kratos', } diff --git a/src/domain/community/user/user.module.ts b/src/domain/community/user/user.module.ts index f745e4e7f6..05498e7e72 100644 --- a/src/domain/community/user/user.module.ts +++ b/src/domain/community/user/user.module.ts @@ -14,7 +14,6 @@ import { AuthorizationPolicyModule } from '@domain/common/authorization-policy/a import { CommunicationAdapterModule } from '@services/adapters/communication-adapter/communication-adapter.module'; import { RoomModule } from '@domain/communication/room/room.module'; import { MicroservicesModule } from '@core/microservices/microservices.module'; -import { KonfigModule } from '@src/platform/configuration/config/config.module'; import { PreferenceSetModule } from '@domain/common/preference-set/preference.set.module'; import { PreferenceModule } from '@domain/common/preference'; import { PlatformAuthorizationPolicyModule } from '@src/platform/authorization/platform.authorization.policy.module'; @@ -31,6 +30,7 @@ import { DocumentModule } from '@domain/storage/document/document.module'; import { StorageBucketModule } from '@domain/storage/storage-bucket/storage.bucket.module'; import { ContributorModule } from '../contributor/contributor.module'; import { AccountHostModule } from '@domain/space/account.host/account.host.module'; +import { KratosAdapterModule } from '@services/adapters/kratos-adapter/kratos.adapter.module'; @Module({ imports: [ @@ -48,11 +48,11 @@ import { AccountHostModule } from '@domain/space/account.host/account.host.modul PlatformAuthorizationPolicyModule, PreferenceModule, PreferenceSetModule, - KonfigModule, MessagingModule, StorageAggregatorModule, StorageBucketModule, DocumentModule, + KratosAdapterModule, ContributorModule, TypeOrmModule.forFeature([User]), ], diff --git a/src/domain/community/user/user.resolver.fields.ts b/src/domain/community/user/user.resolver.fields.ts index 560ed0a640..1e734a7242 100644 --- a/src/domain/community/user/user.resolver.fields.ts +++ b/src/domain/community/user/user.resolver.fields.ts @@ -1,8 +1,4 @@ -import { - AuthorizationAgentPrivilege, - CurrentUser, - Profiling, -} from '@common/decorators'; +import { AuthorizationAgentPrivilege, CurrentUser } from '@common/decorators'; import { AuthorizationCredential, AuthorizationPrivilege } from '@common/enums'; import { AgentInfo } from '@core/authentication.agent.info/agent.info'; import { GraphqlGuard } from '@core/authorization'; @@ -72,7 +68,6 @@ export class UserResolverFields { nullable: false, description: 'The Agent representing this User.', }) - @Profiling.api async agent( @Parent() user: User, @Loader(AgentLoaderCreator, { parentClassRef: User }) @@ -86,7 +81,6 @@ export class UserResolverFields { nullable: false, description: 'The Authorization for this User.', }) - @Profiling.api async authorization( @Parent() user: User, @Loader(AuthorizationLoaderCreator, { parentClassRef: User }) @@ -134,7 +128,6 @@ export class UserResolverFields { nullable: true, description: 'The direct rooms this user is a member of', }) - @Profiling.api async directRooms(@Parent() user: User): Promise { return this.userService.getDirectRooms(user); } @@ -144,7 +137,6 @@ export class UserResolverFields { nullable: false, description: 'The email address for this User.', }) - @Profiling.api async email( @Parent() user: User, @CurrentUser() agentInfo: AgentInfo @@ -166,7 +158,6 @@ export class UserResolverFields { nullable: true, description: 'The phone number for this User.', }) - @Profiling.api async phone( @Parent() user: User, @CurrentUser() agentInfo: AgentInfo @@ -210,7 +201,6 @@ export class UserResolverFields { nullable: false, description: 'Can a message be sent to this User.', }) - @Profiling.api async isContactable( @Parent() user: User, @CurrentUser() agentInfo: AgentInfo @@ -246,7 +236,6 @@ export class UserResolverFields { return loader.load(user.id); } - @AuthorizationAgentPrivilege(AuthorizationPrivilege.READ) @ResolveField('authenticationMethod', () => AuthenticationType, { nullable: true, description: @@ -254,9 +243,19 @@ export class UserResolverFields { }) @UseGuards(GraphqlGuard) async authenticationMethod( - @Parent() user: IUser + @Parent() user: IUser, + @CurrentUser() agentInfo: AgentInfo ): Promise { - return this.userService.getAuthenticationTypeByEmail(user.email); + const isCurrentUser = user.id === agentInfo.userID; + const platformAccessGranted = this.authorizationService.isAccessGranted( + agentInfo, + await this.platformAuthorizationService.getPlatformAuthorizationPolicy(), + AuthorizationPrivilege.PLATFORM_ADMIN + ); + if (isCurrentUser || platformAccessGranted) { + return this.userService.getAuthenticationTypeByEmail(user.email); + } + return AuthenticationType.UNKNOWN; } private async isAccessGranted( diff --git a/src/domain/community/user/user.service.ts b/src/domain/community/user/user.service.ts index 6ea45d765a..afcfa963a0 100644 --- a/src/domain/community/user/user.service.ts +++ b/src/domain/community/user/user.service.ts @@ -63,15 +63,12 @@ import { AgentType } from '@common/enums/agent.type'; import { ContributorService } from '../contributor/contributor.service'; import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type'; import { AccountType } from '@common/enums/account.type'; -import { IdentityApi, Configuration, Identity } from '@ory/kratos-client'; -import { ConfigService } from '@nestjs/config'; -import { AlkemioConfig } from '@src/types'; import { AuthenticationType } from '@common/enums/authentication.type'; +import { KratosAdapter } from '@services/adapters/kratos-adapter/kratos.adapter'; @Injectable() export class UserService { cacheOptions: CachingConfig = { ttl: 300 }; - private readonly kratosIdentityClient: IdentityApi; constructor( private profileService: ProfileService, @@ -84,26 +81,13 @@ export class UserService { private storageAggregatorService: StorageAggregatorService, private accountHostService: AccountHostService, private contributorService: ContributorService, - private configService: ConfigService, + private kratosAdapter: KratosAdapter, @InjectRepository(User) private userRepository: Repository, @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService, @Inject(CACHE_MANAGER) private readonly cacheManager: Cache - ) { - const { kratos_admin_base_url_server } = this.configService.get( - 'identity.authentication.providers.ory', - { - infer: true, - } - ); - const kratosAdminUrlServer = kratos_admin_base_url_server; - this.kratosIdentityClient = new IdentityApi( - new Configuration({ - basePath: kratosAdminUrlServer, - }) - ); - } + ) {} /** * Retrieves the authentication type associated with a given email. @@ -114,85 +98,9 @@ export class UserService { public async getAuthenticationTypeByEmail( email: string ): Promise { - const identity = await this.getIdentityByEmail(email); + const identity = await this.kratosAdapter.getIdentityByEmail(email); if (!identity) return AuthenticationType.UNKNOWN; - return this.mapAuthenticationType(identity); - } - - /** - * Retrieves an identity by email. - * - * @param email - The email address to search for the identity. - * @returns A promise that resolves to the identity if found, or undefined if not found. - * - * @remarks - * This method uses the `kratosIdentityClient` to list identities with the specified email. - * If no identity is found, a warning is logged. - * - * @example - * ```typescript - * const identity = await getIdentityByEmail('example@example.com'); - * if (identity) { - * console.log('Identity found:', identity); - * } else { - * console.log('Identity not found.'); - * } - * ``` - */ - private async getIdentityByEmail( - email: string - ): Promise { - const { data: identity } = await this.kratosIdentityClient.listIdentities({ - credentialsIdentifier: email, - includeCredential: ['password', 'oidc'], - }); - if (!identity) { - this.logger.warn( - `Identity with email ${email} not found.`, - LogContext.USER - ); - return undefined; - } - if (identity.length === 0) { - this.logger.warn( - `Identity with email ${email} not found.`, - LogContext.USER - ); - return undefined; - } - return identity[0]; - } - - /** - * Maps the provided identity to an authentication type. - * - * @param identity - The identity object containing credentials. - * @returns The corresponding authentication type based on the identity's credentials. - * - * The function checks the following conditions in order: - * - If the identity has OIDC credentials, it examines the identifiers: - * - If the identifier starts with 'microsoft', it returns `AuthenticationType.MICROSOFT`. - * - If the identifier starts with 'linkedin', it returns `AuthenticationType.LINKEDIN`. - * - If the identity has password credentials, it returns `AuthenticationType.EMAIL`. - * - If none of the above conditions are met, it returns `AuthenticationType.UNKNOWN`. - */ - private mapAuthenticationType(identity: Identity): AuthenticationType { - if (identity.credentials) { - if (identity.credentials.oidc) { - const identifiers = identity.credentials.oidc.identifiers; - if (!identifiers) return AuthenticationType.UNKNOWN; - const identifier = identifiers[0]; - if (!identifier) return AuthenticationType.UNKNOWN; - if (identifier.startsWith('microsoft')) - return AuthenticationType.MICROSOFT; - if (identifier.startsWith('linkedin')) - return AuthenticationType.LINKEDIN; - } else { - if (identity.credentials.password) return AuthenticationType.EMAIL; - } - } - - return AuthenticationType.UNKNOWN; + return this.kratosAdapter.mapAuthenticationType(identity); } private getUserCommunicationIdCacheKey(communicationId: string): string { diff --git a/src/services/adapters/kratos-adapter/kratos.adapter.module.ts b/src/services/adapters/kratos-adapter/kratos.adapter.module.ts new file mode 100644 index 0000000000..f1daff9a21 --- /dev/null +++ b/src/services/adapters/kratos-adapter/kratos.adapter.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { KratosAdapter } from './kratos.adapter'; +import { KonfigModule } from '@platform/configuration/config/config.module'; + +@Module({ + imports: [KonfigModule], + providers: [KratosAdapter], + exports: [KratosAdapter], +}) +export class KratosAdapterModule {} diff --git a/src/services/adapters/kratos-adapter/kratos.adapter.ts b/src/services/adapters/kratos-adapter/kratos.adapter.ts new file mode 100644 index 0000000000..e9ea971020 --- /dev/null +++ b/src/services/adapters/kratos-adapter/kratos.adapter.ts @@ -0,0 +1,100 @@ +import { AuthenticationType } from '@common/enums/authentication.type'; +import { Inject, Injectable, LoggerService } from '@nestjs/common'; +import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; +import { IdentityApi, Configuration, Identity } from '@ory/kratos-client'; +import { AlkemioConfig } from '@src/types/alkemio.config'; +import { ConfigService } from '@nestjs/config'; +import { LogContext } from '@common/enums'; + +@Injectable() +export class KratosAdapter { + private readonly kratosIdentityClient: IdentityApi; + + constructor( + private configService: ConfigService, + @Inject(WINSTON_MODULE_NEST_PROVIDER) + private readonly logger: LoggerService + ) { + const { kratos_admin_base_url_server } = this.configService.get( + 'identity.authentication.providers.ory', + { + infer: true, + } + ); + const kratosAdminUrlServer = kratos_admin_base_url_server; + this.kratosIdentityClient = new IdentityApi( + new Configuration({ + basePath: kratosAdminUrlServer, + }) + ); + } + + /** + * Maps the provided identity to an authentication type. + * + * @param identity - The identity object containing credentials. + * @returns The corresponding authentication type based on the identity's credentials. + * + * The function checks the following conditions in order: + * - If the identity has OIDC credentials, it examines the identifiers: + * - If the identifier starts with 'microsoft', it returns `AuthenticationType.MICROSOFT`. + * - If the identifier starts with 'linkedin', it returns `AuthenticationType.LINKEDIN`. + * - If the identity has password credentials, it returns `AuthenticationType.EMAIL`. + * - If none of the above conditions are met, it returns `AuthenticationType.UNKNOWN`. + */ + public mapAuthenticationType(identity: Identity): AuthenticationType { + if (identity.credentials) { + if (identity.credentials.oidc) { + const identifiers = identity.credentials.oidc.identifiers; + if (!identifiers) return AuthenticationType.UNKNOWN; + const identifier = identifiers[0]; + if (!identifier) return AuthenticationType.UNKNOWN; + if (identifier.startsWith('microsoft')) + return AuthenticationType.MICROSOFT; + if (identifier.startsWith('linkedin')) + return AuthenticationType.LINKEDIN; + } else { + if (identity.credentials.password) return AuthenticationType.EMAIL; + } + } + + return AuthenticationType.UNKNOWN; + } + + /** + * Retrieves an identity by email. + * + * @param email - The email address to search for the identity. + * @returns A promise that resolves to the identity if found, or undefined if not found. + * + * @remarks + * This method uses the `kratosIdentityClient` to list identities with the specified email. + * If no identity is found, a warning is logged. + * + * @example + * ```typescript + * const identity = await getIdentityByEmail('example@example.com'); + * if (identity) { + * console.log('Identity found:', identity); + * } else { + * console.log('Identity not found.'); + * } + * ``` + */ + public async getIdentityByEmail( + email: string + ): Promise { + const { data: identity } = await this.kratosIdentityClient.listIdentities({ + credentialsIdentifier: email, + includeCredential: ['password', 'oidc'], + }); + if (!identity || identity.length === 0) { + this.logger.warn( + `Identity with email ${email} not found.`, + LogContext.KRATOS + ); + return undefined; + } + return identity[0]; + } +} From d3395bc6d5160ee2bc82eee687dfb8d3f897694a Mon Sep 17 00:00:00 2001 From: Carlos Cano Date: Wed, 9 Oct 2024 10:31:05 +0300 Subject: [PATCH 76/78] Fix Callout Whiteboard template content not being updated (#4600) --- .../callout-framing/callout.framing.service.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/domain/collaboration/callout-framing/callout.framing.service.ts b/src/domain/collaboration/callout-framing/callout.framing.service.ts index b46b94ffa5..c1c647c021 100644 --- a/src/domain/collaboration/callout-framing/callout.framing.service.ts +++ b/src/domain/collaboration/callout-framing/callout.framing.service.ts @@ -113,6 +113,13 @@ export class CalloutFramingService { calloutFraming.whiteboard, calloutFramingData.whiteboard ); + if (calloutFramingData.whiteboard.content) { + calloutFraming.whiteboard = + await this.whiteboardService.updateWhiteboardContent( + calloutFraming.whiteboard.id, + calloutFramingData.whiteboard.content + ); + } } return calloutFraming; From ba439e393f6c29c801826f9907f6d8e8e818d6e2 Mon Sep 17 00:00:00 2001 From: Svetoslav Petkov Date: Thu, 10 Oct 2024 18:50:30 +0300 Subject: [PATCH 77/78] User paginated can be filtered by having tags (#4590) --- src/domain/community/user/user.resolver.queries.ts | 10 ++++++++-- src/domain/community/user/user.service.ts | 10 ++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/domain/community/user/user.resolver.queries.ts b/src/domain/community/user/user.resolver.queries.ts index 942732806b..fac2bfb067 100644 --- a/src/domain/community/user/user.resolver.queries.ts +++ b/src/domain/community/user/user.resolver.queries.ts @@ -54,16 +54,22 @@ export class UserResolverQueries { async usersPaginated( @CurrentUser() agentInfo: AgentInfo, @Args({ nullable: true }) pagination: PaginationArgs, + @Args({ + name: 'withTags', + nullable: true, + description: 'Return only users with tags', + }) + withTags?: boolean, @Args('filter', { nullable: true }) filter?: UserFilterInput ): Promise { - await this.authorizationService.grantAccessOrFail( + this.authorizationService.grantAccessOrFail( agentInfo, await this.platformAuthorizationService.getPlatformAuthorizationPolicy(), AuthorizationPrivilege.READ_USERS, `users query: ${agentInfo.email}` ); - return this.userService.getPaginatedUsers(pagination, filter); + return this.userService.getPaginatedUsers(pagination, withTags, filter); } @UseGuards(GraphqlGuard) diff --git a/src/domain/community/user/user.service.ts b/src/domain/community/user/user.service.ts index afcfa963a0..6fcd5ceba3 100644 --- a/src/domain/community/user/user.service.ts +++ b/src/domain/community/user/user.service.ts @@ -596,9 +596,19 @@ export class UserService { async getPaginatedUsers( paginationArgs: PaginationArgs, + withTags?: boolean, filter?: UserFilterInput ): Promise> { const qb = this.userRepository.createQueryBuilder('user'); + + if (withTags !== undefined) { + qb.leftJoin('user.profile', 'profile') + .leftJoin('tagset', 'tagset', 'profile.id = tagset.profileId') + // cannot use object or operators here + // because typeorm cannot construct the query properly + .where(`tagset.tags ${withTags ? '!=' : '='} ''`); + } + if (filter) { applyUserFilter(qb, filter); } From 612d88edfd607bf77782e29ea14c8b0471b0f863 Mon Sep 17 00:00:00 2001 From: Svetoslav Date: Fri, 11 Oct 2024 16:40:05 +0300 Subject: [PATCH 78/78] version bump --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a5956efbec..fcccb28bfa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "alkemio-server", - "version": "0.92.0", + "version": "0.93.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "alkemio-server", - "version": "0.92.0", + "version": "0.93.0", "license": "EUPL-1.2", "dependencies": { "@alkemio/matrix-adapter-lib": "^0.4.1", diff --git a/package.json b/package.json index 5e3cffd4b7..47359db592 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "alkemio-server", - "version": "0.92.0", + "version": "0.93.0", "description": "Alkemio server, responsible for managing the shared Alkemio platform", "author": "Alkemio Foundation", "private": false,