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: 35 additions & 99 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,10 @@ export class AuthorizationService {
'Authorization: no definition provided',
LogContext.AUTH_POLICY
);
if (authorization.credentialRules === '') {
if (
Array.isArray(authorization.credentialRules) &&
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 +123,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 +141,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 +163,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 +182,39 @@ 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
);
for (const rule of privilegeRules) {
if (grantedPrivileges.includes(rule.sourcePrivilege)) {
grantedPrivileges.push(...rule.grantedPrivileges);
}
}

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 +226,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 +251,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,21 +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: true })
credentialRules: AuthorizationPolicyRuleCredential[];

@Column('text')
privilegeRules: string;

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

@Column({ type: 'json', nullable: true })
verifiedCredentialRules: AuthorizationPolicyRuleVerifiedCredential[];
@Column()
anonymousReadAccess: boolean;

Expand All @@ -37,9 +39,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
Loading