Skip to content

Commit

Permalink
Authorization related fixes (#4678)
Browse files Browse the repository at this point in the history
* made bootstrap pick up admin role directly, not store on service; ensure 3 singlton entities have authorization policy type set

* fixed issues with authorization reset controller; moved user auth reset to be via ID to make clear it is reloaded

* fixed typos

* fixed admin account not having auth policy properly set

---------

Co-authored-by: Valentin Yanakiev <[email protected]>
  • Loading branch information
techsmyth and valentinyanakiev authored Nov 6, 2024
1 parent abee5a5 commit a8e2e11
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 44 deletions.
3 changes: 3 additions & 0 deletions src/common/enums/authorization.policy.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export enum AuthorizationPolicyType {
COMMUNICATION = 'communication',
ROOM = 'room',
AI_PERSONA = 'ai-persona',
AI_SERVER = 'ai-server',
APPLICATION = 'application',
ROLE_SET = 'role-set',
COMMUNITY = 'community',
Expand Down Expand Up @@ -50,8 +51,10 @@ export enum AuthorizationPolicyType {
CALENDAR = 'calendar',
CALENDAR_EVENT = 'calendar-event',
TIMELINE = 'timeline',
LIBRARY = 'library',
IN_MEMORY = 'in-memory',
LICENSING = 'licensing',
LICENSE_POLICY = 'license-policy',
UNKNOWN = 'unknown',
AI_PERSONA_SERVICE = 'ai-persona-service',
}
Expand Down
56 changes: 32 additions & 24 deletions src/core/bootstrap/bootstrap.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,6 @@ import { bootstrapSpaceTutorialsCallouts } from './platform-template-definitions

@Injectable()
export class BootstrapService {
private adminAgentInfo?: AgentInfo;

constructor(
private accountService: AccountService,
private accountAuthorizationService: AccountAuthorizationService,
Expand Down Expand Up @@ -296,7 +294,7 @@ export class BootstrapService {
userData.email
);
if (!userExists) {
let user = await this.userService.createUser({
const user = await this.userService.createUser({
email: userData.email,
accountUpn: userData.email,
firstName: userData.firstName,
Expand All @@ -306,19 +304,11 @@ export class BootstrapService {
},
});

const credentialsData = userData.credentials;
for (const credentialData of credentialsData) {
await this.adminAuthorizationService.grantCredentialToUser({
userID: user.id,
type: credentialData.type,
resourceID: credentialData.resourceID,
});
}
user = await this.userAuthorizationService.grantCredentials(user);

// Once all is done, reset the user authorizations
const userAuthorizations =
await this.userAuthorizationService.applyAuthorizationPolicy(user);
await this.userAuthorizationService.applyAuthorizationPolicy(
user.id
);
await this.authorizationPolicyService.saveAll(userAuthorizations);

const account = await this.userService.getAccount(user);
Expand All @@ -327,16 +317,18 @@ export class BootstrapService {
account
);
await this.authorizationPolicyService.saveAll(accountAuthorizations);
if (!this.adminAgentInfo) {
this.adminAgentInfo = await this.createSystemAgentInfo(user);
}
} else {
if (!this.adminAgentInfo) {
const user = await this.userService.getUserByEmail(userData.email);
if (user) {
this.adminAgentInfo = await this.createSystemAgentInfo(user);
}

const credentialsData = userData.credentials;
for (const credentialData of credentialsData) {
await this.adminAuthorizationService.grantCredentialToUser({
userID: user.id,
type: credentialData.type,
resourceID: credentialData.resourceID,
});
}
await this.userAuthorizationService.grantCredentialsAllUsersReceive(
user.id
);
}
}
} catch (error: any) {
Expand Down Expand Up @@ -432,14 +424,15 @@ export class BootstrapService {
DEFAULT_HOST_ORG_NAMEID
);
if (!hostOrganization) {
const adminAgentInfo = await this.getAdminAgentInfo();
hostOrganization = await this.organizationService.createOrganization(
{
nameID: DEFAULT_HOST_ORG_NAMEID,
profileData: {
displayName: DEFAULT_HOST_ORG_DISPLAY_NAME,
},
},
this.adminAgentInfo
adminAgentInfo
);
const orgAuthorizations =
await this.organizationAuthorizationService.applyAuthorizationPolicy(
Expand All @@ -457,6 +450,21 @@ export class BootstrapService {
}
}

private async getAdminAgentInfo(): Promise<AgentInfo> {
const adminUserEmail = '[email protected]';
const adminUser = await this.userService.getUserByEmail(adminUserEmail, {
relations: {
agent: true,
},
});
if (!adminUser) {
throw new BootstrapException(
`Unable to load fixed admin user for creating organization: ${adminUserEmail}`
);
}
return this.createSystemAgentInfo(adminUser);
}

private async ensureSpaceSingleton() {
this.logger.verbose?.(
'=== Ensuring at least one Account with a space is present ===',
Expand Down
4 changes: 2 additions & 2 deletions src/domain/access/role-set/role.set.resolver.mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export class RoleSetResolverMutations {
// reset the user authorization policy so that their profile is visible to other community members
const user = await this.userService.getUserOrFail(roleData.contributorID);
const authorizations =
await this.userAuthorizationService.applyAuthorizationPolicy(user);
await this.userAuthorizationService.applyAuthorizationPolicy(user.id);
await this.authorizationPolicyService.saveAll(authorizations);
return await this.userService.getUserOrFail(roleData.contributorID);
}
Expand Down Expand Up @@ -249,7 +249,7 @@ export class RoleSetResolverMutations {
// to other community members
const user = await this.userService.getUserOrFail(roleData.contributorID);
const authorizations =
await this.userAuthorizationService.applyAuthorizationPolicy(user);
await this.userAuthorizationService.applyAuthorizationPolicy(user.id);
await this.authorizationPolicyService.saveAll(authorizations);
return await this.userService.getUserOrFail(roleData.contributorID);
}
Expand Down
2 changes: 1 addition & 1 deletion src/domain/community/user/user.resolver.mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export class UserResolverMutations {
`reset authorization definition on user: ${authorizationResetData.userID}`
);
const updatedAuthorizations =
await this.userAuthorizationService.applyAuthorizationPolicy(user);
await this.userAuthorizationService.applyAuthorizationPolicy(user.id);
await this.authorizationPolicyService.saveAll(updatedAuthorizations);

return await this.userService.getUserOrFail(user.id);
Expand Down
18 changes: 9 additions & 9 deletions src/domain/community/user/user.service.authorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ export class UserAuthorizationService {
) {}

async applyAuthorizationPolicy(
userInput: IUser
userID: string
): Promise<IAuthorizationPolicy[]> {
const user = await this.userService.getUserOrFail(userInput.id, {
const user = await this.userService.getUserOrFail(userID, {
loadEagerRelations: false,
relations: {
authorization: true,
Expand Down Expand Up @@ -169,20 +169,20 @@ export class UserAuthorizationService {
return updatedAuthorizations;
}

async grantCredentials(user: IUser): Promise<IUser> {
const agent = await this.userService.getAgent(user.id);
async grantCredentialsAllUsersReceive(userID: string): Promise<IUser> {
const agent = await this.userService.getAgent(userID);

user.agent = await this.agentService.grantCredential({
await this.agentService.grantCredential({
type: AuthorizationCredential.GLOBAL_REGISTERED,
agentID: agent.id,
});
user.agent = await this.agentService.grantCredential({
await this.agentService.grantCredential({
type: AuthorizationCredential.USER_SELF_MANAGEMENT,
agentID: agent.id,
resourceID: user.id,
resourceID: userID,
});
user.agent = await this.agentService.saveAgent(user.agent);
return user;

return await this.userService.getUserOrFail(userID);
}

private appendGlobalCredentialRules(
Expand Down
45 changes: 45 additions & 0 deletions src/migrations/1730877510629-authorizationPolicy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class AuthorizationPolicy1730877510629 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
// Get the authorization ID on the ai_server table
await this.updateAuthorizationPolicyTypeOnSingleton(
queryRunner,
'ai_server',
'ai-server'
);
await this.updateAuthorizationPolicyTypeOnSingleton(
queryRunner,
'license_policy',
'license-policy'
);
await this.updateAuthorizationPolicyTypeOnSingleton(
queryRunner,
'library',
'library'
);
}

public async down(queryRunner: QueryRunner): Promise<void> {}

private async updateAuthorizationPolicyTypeOnSingleton(
queryRunner: QueryRunner,
tableName: string,
type: string
) {
const entities: {
id: string;
authorizationId: string;
}[] = await queryRunner.query(
`SELECT id, authorizationId FROM ${tableName}`
);
if (entities.length !== 1) {
throw new Error(`Expected exactly one ${tableName} record`);
}
const entity = entities[0];
// update the authorization policy to include the new type
await queryRunner.query(
`UPDATE authorization_policy SET type = '${type}' WHERE id = '${entity.authorizationId}'`
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,12 @@ export class RegistrationResolverMutations {
agentInfo: AgentInfo
): Promise<IUser> {
const user =
await this.userAuthorizationService.grantCredentials(userInput);
await this.userAuthorizationService.grantCredentialsAllUsersReceive(
userInput.id
);

const userAuthorizations =
await this.userAuthorizationService.applyAuthorizationPolicy(user);
await this.userAuthorizationService.applyAuthorizationPolicy(user.id);
await this.authorizationPolicyService.saveAll(userAuthorizations);

const userAccount = await this.userService.getAccount(user);
Expand Down
17 changes: 12 additions & 5 deletions src/services/auth-reset/subscriber/auth-reset.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ export class AuthResetController {
const retryCount = originalMsg.properties.headers?.[RETRY_HEADER] ?? 0;

try {
await this.platformAuthorizationService.applyAuthorizationPolicy();
const authorizations =
await this.platformAuthorizationService.applyAuthorizationPolicy();
await this.authorizationPolicyService.saveAll(authorizations);
this.logger.verbose?.(
'Finished resetting authorization for platform.',
LogContext.AUTH_POLICY
Expand Down Expand Up @@ -146,7 +148,10 @@ export class AuthResetController {

try {
const user = await this.userService.getUserOrFail(payload.id);
await this.userAuthorizationService.applyAuthorizationPolicy(user);
const authorizations =
await this.userAuthorizationService.applyAuthorizationPolicy(user.id);
await this.authorizationPolicyService.saveAll(authorizations);

channel.ack(originalMsg);

const message = `Finished resetting authorization for user with id ${payload.id}.`;
Expand Down Expand Up @@ -195,9 +200,11 @@ export class AuthResetController {
const organization = await this.organizationService.getOrganizationOrFail(
payload.id
);
await this.organizationAuthorizationService.applyAuthorizationPolicy(
organization
);
const authorizations =
await this.organizationAuthorizationService.applyAuthorizationPolicy(
organization
);
await this.authorizationPolicyService.saveAll(authorizations);
channel.ack(originalMsg);

const message = `Finished resetting authorization for organization with id ${payload.id}.`;
Expand Down
2 changes: 1 addition & 1 deletion test/mocks/user.authorization.service.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const MockUserAuthorizationService: ValueProvider<
> = {
provide: UserAuthorizationService,
useValue: {
grantCredentials: jest.fn(),
grantCredentialsAllUsersReceive: jest.fn(),
applyAuthorizationPolicy: jest.fn(),
},
};

0 comments on commit a8e2e11

Please sign in to comment.