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

Authorization: Add READ_ABOUT privilege for being able to see information about space; remove anonymousReadAccess #4782

Open
wants to merge 25 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d0a09c3
added READ_ABOUT privilege + assign to space if have READ privilege
techsmyth Dec 12, 2024
2fe0309
tidy up privilege rules
techsmyth Dec 12, 2024
9b99bae
simplify logic for extending space auth based on whether has a parent…
techsmyth Dec 13, 2024
e0c0c14
added assignment + usage of read about for profile + context; updated…
techsmyth Dec 13, 2024
98916ac
protect more fields on space entity with read about
techsmyth Dec 13, 2024
edf550a
lookup of space checks for READ_ABOUT
techsmyth Dec 13, 2024
7713b34
added helper method to make it easier to add privilege rule; reverted…
techsmyth Dec 13, 2024
a852141
agentInfo creation tidied up + moved to one service; AgentInfo to hol…
techsmyth Dec 14, 2024
96c397f
replaced usage of anonymous read access flag with explicit usage of G…
techsmyth Dec 14, 2024
b0f8e53
updated space spec file to not use anonymousReadAccess
techsmyth Dec 14, 2024
042f106
added simple auth policy
techsmyth Dec 14, 2024
49d2fd5
adjusted adding of rule for anonymous users to generate name based on…
techsmyth Dec 14, 2024
9b8ccaa
addressed rabbit feedback
techsmyth Dec 18, 2024
ea3abfe
Merge branch 'develop' into server-4772
techsmyth Dec 18, 2024
0be7684
Merge branch 'server-4772' into server-4787-anonymous
techsmyth Dec 18, 2024
aa17040
Merge pull request #4788 from alkem-io/server-4787-anonymous
techsmyth Dec 20, 2024
c144bd2
merge from develop
techsmyth Dec 21, 2024
d285280
Merge branch 'develop' into server-4772
techsmyth Dec 21, 2024
71ab7c0
moved migration to last in teh list
techsmyth Dec 21, 2024
5693de9
Merge branch 'develop' into server-4772
valentinyanakiev Jan 14, 2025
498255c
Merge branch 'develop' into server-4772
valentinyanakiev Jan 15, 2025
46bb7be
implement coderabbit suggestion
valentinyanakiev Jan 15, 2025
689e251
Update src/migrations/1734708463666-authAnonymousReadAccess.ts
valentinyanakiev Jan 15, 2025
b67a129
Merge remote-tracking branch 'origin/server-4772' into server-4772
valentinyanakiev Jan 15, 2025
0a05438
addressing coderabbit comments
valentinyanakiev Jan 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export const CREDENTIAL_RULE_SUBSPACE_ADMINS = 'credentialRule-SubspaceAdmins';
export const CREDENTIAL_RULE_MEMBER_CREATE_SUBSPACE =
'credentialRule-memberCreateSubspace';
export const CREDENTIAL_RULE_SUBSPACE_PARENT_MEMBER_JOIN =
Expand All @@ -10,6 +9,8 @@ export const CREDENTIAL_RULE_SPACE_ADMIN_DELETE_SUBSPACE =
export const CREDENTIAL_RULE_SPACE_ADMINS = 'credentialRule-spaceAdmins';
export const CREDENTIAL_RULE_SPACE_MEMBERS_READ =
'credentialRule-spaceMembersRead';
export const CREDENTIAL_RULE_SPACE_MEMBERS_READ_ABOUT_SUBSPACES =
'credentialRule-spaceMembersReadAboutSubspaces';
export const CREDENTIAL_RULE_SPACE_HOST_ASSOCIATES_JOIN =
'credentialRule-spaceHostAssociatesJoin';
export const CREDENTIAL_RULE_ACCOUNT_HOST_MANAGE =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ export const POLICY_RULE_COMMUNITY_ADD_VC =
'policyRule-communityAddVirtualContributor';
export const POLICY_RULE_COMMUNITY_APPROVE_APPLICATION =
'policyRule-communityApproveApplication';
export const POLICY_RULE_READ_ABOUT = 'policyRule-readAbout';
1 change: 1 addition & 0 deletions src/common/enums/authorization.credential.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export enum AuthorizationCredential {
GLOBAL_ADMIN = 'global-admin', // able to do everything, god mode
GLOBAL_SUPPORT = 'global-support', // able to manage platform level information, can per space have admin rights
GLOBAL_LICENSE_MANAGER = 'global-license-manager', // able to manage platform level information, can per space have admin rights
GLOBAL_ANONYMOUS = 'global-anonymous', // credential issued to all non-authenticated interactions
GLOBAL_REGISTERED = 'global-registered', // credential issued to all registered users
GLOBAL_COMMUNITY_READ = 'global-community-read', // able to view all details of the top level community
GLOBAL_SPACES_READER = 'global-spaces-read', // able to view all details of the top level community
Expand Down
1 change: 1 addition & 0 deletions src/common/enums/authorization.privilege.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export enum AuthorizationPrivilege {
COMMUNITY_ADD_MEMBER = 'community-add-member', // only for global admins
COMMUNITY_ADD_MEMBER_VC_FROM_ACCOUNT = 'community-add-member-vc-from-account', // allow adding a VC as member to a community from an account
UPDATE_CALLOUT_PUBLISHER = 'update-callout-publisher',
READ_ABOUT = 'read-about', // access the external about information for an entity
READ_USERS = 'read-users',
READ_USER_PII = 'read-user-pii',
READ_USER_SETTINGS = 'read-user-settings',
Expand Down
4 changes: 2 additions & 2 deletions src/core/authentication.agent.info/agent.info.metadata.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ICredential } from '@domain/agent/credential';
import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface';

export class AgentInfoMetadata {
userID!: string;
email!: string;
credentials: ICredential[] = [];
credentials: ICredentialDefinition[] = [];
communicationID!: string;
agentID!: string;
did!: string;
Expand Down
5 changes: 3 additions & 2 deletions src/core/authentication.agent.info/agent.info.module.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Module } from '@nestjs/common';
import { AgentInfoCacheService } from './agent.info.cache.service';
import { AgentInfoService } from './agent.info.service';
@Module({
imports: [],
providers: [AgentInfoCacheService],
exports: [AgentInfoCacheService],
providers: [AgentInfoService, AgentInfoCacheService],
exports: [AgentInfoService, AgentInfoCacheService],
})
export class AuthenticationAgentInfoModule {}
143 changes: 143 additions & 0 deletions src/core/authentication.agent.info/agent.info.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface';
import { AgentInfo } from './agent.info';
import { Inject, Injectable, LoggerService } from '@nestjs/common';
import { AuthorizationCredential, LogContext } from '@common/enums';
import { EntityNotInitializedException } from '@common/exceptions/entity.not.initialized.exception';
import { IVerifiedCredential } from '@domain/agent/verified-credential/verified.credential.interface';
import { InjectEntityManager } from '@nestjs/typeorm';
import { EntityManager } from 'typeorm';
import { User } from '@domain/community/user/user.entity';
import { AgentInfoMetadata } from './agent.info.metadata';
import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston';
import { EntityNotFoundException } from '@common/exceptions/entity.not.found.exception';
@Injectable()
export class AgentInfoService {
constructor(
@InjectEntityManager('default')
private entityManager: EntityManager,
@Inject(WINSTON_MODULE_NEST_PROVIDER)
private readonly logger: LoggerService
) {}

// Note for now a very empty service, but later as we start allowing "acting as" agents this service will expand.
// To consider: moving function related to AgentInfoMetaData from user service here, to remove dependency on UserModule in Authentication Module
public createAnonymousAgentInfo(): AgentInfo {
const emptyAgentInfo = new AgentInfo();
const anonymousCredential: ICredentialDefinition = {
type: AuthorizationCredential.GLOBAL_ANONYMOUS,
resourceID: '',
};
emptyAgentInfo.credentials = [anonymousCredential];
return emptyAgentInfo;
}

/**
* 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.
*/
public async getAgentInfoMetadata(
email: string
): Promise<AgentInfoMetadata | undefined> {
try {
const user = await this.entityManager.findOneOrFail(User, {
where: {
email: email,
},
relations: {
agent: {
credentials: true,
},
},
});
if (!user || !user.agent || !user.agent.credentials) {
throw new EntityNotFoundException(
`Unable to load User, Agent or Credentials for User: ${email}`,
LogContext.COMMUNITY
);
}
const userAgentInfoMetadata = new AgentInfoMetadata();
userAgentInfoMetadata.credentials = user.agent.credentials;
userAgentInfoMetadata.agentID = user.agent.id;
userAgentInfoMetadata.userID = user.id;
userAgentInfoMetadata.communicationID = user.communicationID;
return userAgentInfoMetadata;
} catch (error) {
this.logger.verbose?.(
`User not registered: ${email}, ${error}`,
LogContext.AUTH
);
return undefined;
}
}

/**
* 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.
*/
public populateAgentInfoWithMetadata(
agentInfo: AgentInfo,
agentInfoMetadata: AgentInfoMetadata
): void {
agentInfo.agentID = agentInfoMetadata.agentID;
agentInfo.userID = agentInfoMetadata.userID;
agentInfo.communicationID = agentInfoMetadata.communicationID;

if (agentInfoMetadata.credentials) {
agentInfo.credentials = agentInfoMetadata.credentials;
} else {
this.logger.warn?.(
`Authentication Info: Unable to retrieve credentials for registered user: ${agentInfo.email}`,
LogContext.AUTH
);
}
}

public async buildAgentInfoForUser(userId: string): Promise<AgentInfo> {
const user = await this.entityManager.findOneOrFail(User, {
where: { id: userId },
relations: {
agent: {
credentials: true,
},
},
});

if (!user.agent || !user.agent.credentials) {
throw new EntityNotInitializedException(
`Agent not loaded for User: ${user.id}`,
LogContext.WHITEBOARD_INTEGRATION,
{ userId }
);
}

// const verifiedCredentials =
// await this.agentService.getVerifiedCredentials(user.agent);
const verifiedCredentials = [] as IVerifiedCredential[];
// construct the agent info object needed for isAccessGranted
let credentials: ICredentialDefinition[] = [];

if (user.agent.credentials.length !== 0) {
credentials = user.agent.credentials.map(c => {
return {
type: c.type,
resourceID: c.resourceID,
};
});
}

const agentInfo = new AgentInfo();
agentInfo.credentials = credentials;
agentInfo.verifiedCredentials = verifiedCredentials;
return agentInfo;
}
}
4 changes: 2 additions & 2 deletions src/core/authentication.agent.info/agent.info.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { ICredential } from '@domain/agent/credential';
import { ICredentialDefinition } from '@domain/agent/credential/credential.definition.interface';
import { IVerifiedCredential } from '@domain/agent/verified-credential/verified.credential.interface';
export class AgentInfo {
userID = '';
email = '';
emailVerified = false;
firstName = '';
lastName = '';
credentials: ICredential[] = [];
credentials: ICredentialDefinition[] = [];
verifiedCredentials: IVerifiedCredential[] = [];
communicationID = '';
agentID = '';
Expand Down
8 changes: 2 additions & 6 deletions src/core/authentication/authentication.module.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
import { Module } from '@nestjs/common';
import { CacheModule } from '@nestjs/cache-manager';
import { PassportModule } from '@nestjs/passport';
import { UserModule } from '@domain/community/user/user.module';
import { AuthenticationService } from './authentication.service';
import { JwtModule } from '@nestjs/jwt';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { OryStrategy } from './ory.strategy';
import { CredentialModule } from '@domain/agent/credential/credential.module';
import { OryApiStrategy } from './ory.api.strategy';
import { AgentModule } from '@domain/agent/agent/agent.module';
import { AuthenticationAgentInfoModule } from '@core/authentication.agent.info/agent.info.module';
import { KratosModule } from '@services/infrastructure/kratos/kratos.module';
import { AgentModule } from '@domain/agent/agent/agent.module';
@Module({
imports: [
PassportModule.register({
session: false,
defaultStrategy: 'oathkeeper-jwt',
}),
UserModule,
AgentModule,
CredentialModule,
AuthenticationAgentInfoModule,
KratosModule,
AgentModule,
CacheModule.register(),
JwtModule.registerAsync({
imports: [ConfigModule],
Expand Down
Loading