Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Optimize myPrivileges #4794

Merged
merged 10 commits into from
Dec 21, 2024
134 changes: 39 additions & 95 deletions src/core/authorization/authorization.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { IVerifiedCredential } from '@domain/agent/verified-credential/verified.
import { IAuthorizationPolicyRuleCredential } from './authorization.policy.rule.credential.interface';
import { IAuthorizationPolicyRuleVerifiedCredential } from './authorization.policy.rule.verified.credential.interface';
import { AuthorizationInvalidPolicyException } from '@common/exceptions/authorization.invalid.policy.exception';
import { IAuthorizationPolicyRulePrivilege } from './authorization.policy.rule.privilege.interface';
import { ForbiddenAuthorizationPolicyException } from '@common/exceptions/forbidden.authorization.policy.exception';

@Injectable()
Expand Down Expand Up @@ -87,7 +86,7 @@ export class AuthorizationService {
'Authorization: no definition provided',
LogContext.AUTH_POLICY
);
if (authorization.credentialRules === '') {
if (authorization.credentialRules.length === 0) {
throw new AuthorizationInvalidPolicyException(
`AuthorizationPolicy without credential rules provided: ${authorization.id}, type: ${authorization.type}`,
LogContext.AUTH
Expand Down Expand Up @@ -121,9 +120,7 @@ export class AuthorizationService {
// Keep track of all the granted privileges via Credential rules so can use with Privilege rules
const grantedPrivileges: AuthorizationPrivilege[] = [];

const credentialRules = this.convertCredentialRulesStr(
authorization.credentialRules
);
const credentialRules = authorization.credentialRules;
for (const rule of credentialRules) {
for (const credential of agentInfo.credentials) {
if (this.isCredentialMatch(credential, rule)) {
Expand All @@ -141,9 +138,7 @@ export class AuthorizationService {
}
}
const verifiedCredentialRules: IAuthorizationPolicyRuleVerifiedCredential[] =
this.convertVerifiedCredentialRulesStr(
authorization.verifiedCredentialRules
);
authorization.verifiedCredentialRules;
for (const rule of verifiedCredentialRules) {
for (const verifiedCredential of agentInfo.verifiedCredentials) {
const isMatch = this.isVerifiedCredentialMatch(
Expand All @@ -165,9 +160,7 @@ export class AuthorizationService {
}
}

const privilegeRules = this.convertPrivilegeRulesStr(
authorization.privilegeRules
);
const privilegeRules = authorization.privilegeRules;
for (const rule of privilegeRules) {
if (grantedPrivileges.includes(rule.sourcePrivilege)) {
if (rule.grantedPrivileges.includes(privilegeRequired)) {
Expand All @@ -186,55 +179,50 @@ export class AuthorizationService {
credentials: ICredential[],
verifiedCredentials: IVerifiedCredential[],
authorization: IAuthorizationPolicy
) {
const grantedPrivileges: AuthorizationPrivilege[] = [];
): AuthorizationPrivilege[] {
const grantedPrivileges = new Set<AuthorizationPrivilege>();

if (authorization.anonymousReadAccess) {
grantedPrivileges.push(AuthorizationPrivilege.READ);
grantedPrivileges.add(AuthorizationPrivilege.READ);
}

const credentialRules = this.convertCredentialRulesStr(
authorization.credentialRules
);
for (const rule of credentialRules) {
for (const credential of credentials) {
const credentialRules = authorization.credentialRules || [];

credentialRules.forEach(rule => {
credentials.forEach(credential => {
if (this.isCredentialMatch(credential, rule)) {
for (const privilege of rule.grantedPrivileges) {
grantedPrivileges.push(privilege);
}
rule.grantedPrivileges.forEach(privilege =>
grantedPrivileges.add(privilege)
);
}
}
}
});
});

const verifiedCredentialRules = this.convertVerifiedCredentialRulesStr(
authorization.verifiedCredentialRules
);
for (const rule of verifiedCredentialRules) {
for (const credential of verifiedCredentials) {
const verifiedCredentialRules = authorization.verifiedCredentialRules || [];

verifiedCredentialRules.forEach(rule => {
verifiedCredentials.forEach(credential => {
if (this.isVerifiedCredentialMatch(credential, rule)) {
for (const privilege of rule.grantedPrivileges) {
grantedPrivileges.push(privilege);
}
rule.grantedPrivileges.forEach(privilege =>
grantedPrivileges.add(privilege)
);
}
}
}
});
});

const privilegeRules = this.convertPrivilegeRulesStr(
authorization.privilegeRules
);
const initialGrantedPrivileges = Array.from(grantedPrivileges);

const privilegeRules = authorization.privilegeRules || [];
for (const rule of privilegeRules) {
if (grantedPrivileges.includes(rule.sourcePrivilege)) {
grantedPrivileges.push(...rule.grantedPrivileges);
if (initialGrantedPrivileges.includes(rule.sourcePrivilege)) {
for (const privilege of rule.grantedPrivileges) {
grantedPrivileges.add(privilege);
}
}
}

const uniquePrivileges = grantedPrivileges.filter(
(item, i, ar) => ar.indexOf(item) === i
);

return uniquePrivileges;
return Array.from(grantedPrivileges);
}

private isCredentialMatch(
credential: ICredential,
credentialRule: IAuthorizationPolicyRuleCredential
Expand All @@ -246,17 +234,12 @@ export class AuthorizationService {
LogContext.AUTH
);
}
for (const criteria of criterias) {
if (credential.type === criteria.type) {
if (
criteria.resourceID === '' ||
credential.resourceID === criteria.resourceID
) {
return true;
}
}
}
return false;
return criterias.some(
criteria =>
credential.type === criteria.type &&
(criteria.resourceID === '' ||
credential.resourceID === criteria.resourceID)
);
}

private isVerifiedCredentialMatch(
Expand All @@ -276,43 +259,4 @@ export class AuthorizationService {
}
return false;
}

convertCredentialRulesStr(
rulesStr: string
): IAuthorizationPolicyRuleCredential[] {
if (!rulesStr || rulesStr.length == 0) return [];
try {
return JSON.parse(rulesStr);
} catch (error: any) {
const msg = `Unable to convert rules to json: ${error}`;
this.logger.error(msg, error?.stack, LogContext.AUTH);
throw new ForbiddenException(msg, LogContext.AUTH);
}
}

convertVerifiedCredentialRulesStr(
rulesStr: string
): IAuthorizationPolicyRuleVerifiedCredential[] {
if (!rulesStr || rulesStr.length == 0) return [];
try {
return JSON.parse(rulesStr);
} catch (error: any) {
const msg = `Unable to convert rules to json: ${error}`;
this.logger.error(msg, error?.stack, LogContext.AUTH);
throw new ForbiddenException(msg, LogContext.AUTH);
}
}

convertPrivilegeRulesStr(
rulesStr: string
): IAuthorizationPolicyRulePrivilege[] {
if (!rulesStr || rulesStr.length == 0) return [];
try {
return JSON.parse(rulesStr);
} catch (error: any) {
const msg = `Unable to convert privilege rules to json: ${error}`;
this.logger.error(msg, error?.stack, LogContext.AUTH);
throw new ForbiddenException(msg, LogContext.AUTH);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ import { Column, Entity, ManyToOne } from 'typeorm';
import { IAuthorizationPolicy } from './authorization.policy.interface';
import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type';
import { ENUM_LENGTH } from '@common/constants';
import { AuthorizationPolicyRuleCredential } from '@core/authorization/authorization.policy.rule.credential';
import { AuthorizationPolicyRuleVerifiedCredential } from '@core/authorization/authorization.policy.rule.verified.credential';
import { AuthorizationPolicyRulePrivilege } from '@core/authorization/authorization.policy.rule.privilege';

@Entity()
export class AuthorizationPolicy
extends BaseAlkemioEntity
implements IAuthorizationPolicy
{
@Column('text')
credentialRules: string;
@Column({ type: 'json', nullable: false })
credentialRules: AuthorizationPolicyRuleCredential[];

@Column('text')
privilegeRules: string;
@Column({ type: 'json', nullable: false })
privilegeRules: AuthorizationPolicyRulePrivilege[];

@Column('text')
verifiedCredentialRules: string;
@Column({ type: 'json', nullable: false })
verifiedCredentialRules: AuthorizationPolicyRuleVerifiedCredential[];

@Column()
anonymousReadAccess: boolean;
Expand All @@ -37,9 +40,9 @@ export class AuthorizationPolicy
constructor(type: AuthorizationPolicyType) {
super();
this.anonymousReadAccess = false;
this.credentialRules = '';
this.verifiedCredentialRules = '';
this.privilegeRules = '';
this.credentialRules = [];
this.verifiedCredentialRules = [];
this.privilegeRules = [];
this.type = type;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type';
import { IAuthorizationPolicyRuleCredential } from '@core/authorization/authorization.policy.rule.credential.interface';
import { IAuthorizationPolicyRulePrivilege } from '@core/authorization/authorization.policy.rule.privilege.interface';
import { IAuthorizationPolicyRuleVerifiedCredential } from '@core/authorization/authorization.policy.rule.verified.credential.interface';
import { IBaseAlkemio } from '@domain/common/entity/base-entity';
import { Field, ObjectType } from '@nestjs/graphql';

Expand All @@ -8,9 +11,9 @@ export abstract class IAuthorizationPolicy extends IBaseAlkemio {
anonymousReadAccess!: boolean;

// exposed via field resolver
credentialRules!: string;
verifiedCredentialRules!: string;
privilegeRules!: string;
credentialRules!: IAuthorizationPolicyRuleCredential[];
verifiedCredentialRules!: IAuthorizationPolicyRuleVerifiedCredential[];
privilegeRules!: IAuthorizationPolicyRulePrivilege[];

parentAuthorizationPolicy?: IAuthorizationPolicy;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class AuthorizationPolicyResolverFields {
constructor(private authorizationPolicyService: AuthorizationPolicyService) {}

@ResolveField('credentialRules', () => [IAuthorizationPolicyRuleCredential], {
nullable: true,
nullable: false,
description:
'The set of credential rules that are contained by this Authorization Policy.',
})
Expand All @@ -30,7 +30,7 @@ export class AuthorizationPolicyResolverFields {
'verifiedCredentialRules',
() => [IAuthorizationPolicyRuleVerifiedCredential],
{
nullable: true,
nullable: false,
description:
'The set of verified credential rules that are contained by this Authorization Policy.',
}
Expand All @@ -45,7 +45,7 @@ export class AuthorizationPolicyResolverFields {
}

@ResolveField('privilegeRules', () => [IAuthorizationPolicyRulePrivilege], {
nullable: true,
nullable: false,
description:
'The set of privilege rules that are contained by this Authorization Policy.',
})
Expand All @@ -58,7 +58,7 @@ export class AuthorizationPolicyResolverFields {

@UseGuards(GraphqlGuard)
@ResolveField('myPrivileges', () => [AuthorizationPrivilege], {
nullable: true,
nullable: false,
description:
'The privileges granted to the current user based on this Authorization Policy.',
})
Expand Down
Loading