feat: Allow sharing to and from team projects (no-changelog) (#10144)
This commit is contained in:
@@ -291,25 +291,22 @@ export class CredentialsController {
|
||||
let newShareeIds: string[] = [];
|
||||
|
||||
await Db.transaction(async (trx) => {
|
||||
const currentPersonalProjectIDs = credential.shared
|
||||
const currentProjectIds = credential.shared
|
||||
.filter((sc) => sc.role === 'credential:user')
|
||||
.map((sc) => sc.projectId);
|
||||
const newPersonalProjectIds = shareWithIds;
|
||||
const newProjectIds = shareWithIds;
|
||||
|
||||
const toShare = utils.rightDiff(
|
||||
[currentPersonalProjectIDs, (id) => id],
|
||||
[newPersonalProjectIds, (id) => id],
|
||||
);
|
||||
const toShare = utils.rightDiff([currentProjectIds, (id) => id], [newProjectIds, (id) => id]);
|
||||
const toUnshare = utils.rightDiff(
|
||||
[newPersonalProjectIds, (id) => id],
|
||||
[currentPersonalProjectIDs, (id) => id],
|
||||
[newProjectIds, (id) => id],
|
||||
[currentProjectIds, (id) => id],
|
||||
);
|
||||
|
||||
const deleteResult = await trx.delete(SharedCredentials, {
|
||||
credentialsId: credentialId,
|
||||
projectId: In(toUnshare),
|
||||
});
|
||||
await this.enterpriseCredentialsService.shareWithProjects(credential, toShare, trx);
|
||||
await this.enterpriseCredentialsService.shareWithProjects(req.user, credential, toShare, trx);
|
||||
|
||||
if (deleteResult.affected) {
|
||||
amountRemoved = deleteResult.affected;
|
||||
|
||||
@@ -12,6 +12,7 @@ import { Project } from '@/databases/entities/Project';
|
||||
import { ProjectService } from '@/services/project.service';
|
||||
import { TransferCredentialError } from '@/errors/response-errors/transfer-credential.error';
|
||||
import { SharedCredentials } from '@/databases/entities/SharedCredentials';
|
||||
import { RoleService } from '@/services/role.service';
|
||||
|
||||
@Service()
|
||||
export class EnterpriseCredentialsService {
|
||||
@@ -20,9 +21,11 @@ export class EnterpriseCredentialsService {
|
||||
private readonly ownershipService: OwnershipService,
|
||||
private readonly credentialsService: CredentialsService,
|
||||
private readonly projectService: ProjectService,
|
||||
private readonly roleService: RoleService,
|
||||
) {}
|
||||
|
||||
async shareWithProjects(
|
||||
user: User,
|
||||
credential: CredentialsEntity,
|
||||
shareWithIds: string[],
|
||||
entityManager?: EntityManager,
|
||||
@@ -30,19 +33,35 @@ export class EnterpriseCredentialsService {
|
||||
const em = entityManager ?? this.sharedCredentialsRepository.manager;
|
||||
|
||||
const projects = await em.find(Project, {
|
||||
where: { id: In(shareWithIds), type: 'personal' },
|
||||
where: [
|
||||
{
|
||||
id: In(shareWithIds),
|
||||
type: 'team',
|
||||
// if user can see all projects, don't check project access
|
||||
// if they can't, find projects they can list
|
||||
...(user.hasGlobalScope('project:list')
|
||||
? {}
|
||||
: {
|
||||
projectRelations: {
|
||||
userId: user.id,
|
||||
role: In(this.roleService.rolesWithScope('project', 'project:list')),
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: In(shareWithIds),
|
||||
type: 'personal',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const newSharedCredentials = projects
|
||||
// We filter by role === 'project:personalOwner' above and there should
|
||||
// always only be one owner.
|
||||
.map((project) =>
|
||||
this.sharedCredentialsRepository.create({
|
||||
credentialsId: credential.id,
|
||||
role: 'credential:user',
|
||||
projectId: project.id,
|
||||
}),
|
||||
);
|
||||
const newSharedCredentials = projects.map((project) =>
|
||||
this.sharedCredentialsRepository.create({
|
||||
credentialsId: credential.id,
|
||||
role: 'credential:user',
|
||||
projectId: project.id,
|
||||
}),
|
||||
);
|
||||
|
||||
return await em.save(newSharedCredentials);
|
||||
}
|
||||
|
||||
@@ -90,6 +90,19 @@ export class CredentialsService {
|
||||
let credentials = await this.credentialsRepository.findMany(options.listQueryOptions);
|
||||
|
||||
if (isDefaultSelect) {
|
||||
// Since we're filtering using project ID as part of the relation,
|
||||
// we end up filtering out all the other relations, meaning that if
|
||||
// it's shared to a project, it won't be able to find the home project.
|
||||
// To solve this, we have to get all the relation now, even though
|
||||
// we're deleting them later.
|
||||
if ((options.listQueryOptions?.filter?.shared as { projectId?: string })?.projectId) {
|
||||
const relations = await this.sharedCredentialsRepository.getAllRelationsForCredentials(
|
||||
credentials.map((c) => c.id),
|
||||
);
|
||||
credentials.forEach((c) => {
|
||||
c.shared = relations.filter((r) => r.credentialsId === c.id);
|
||||
});
|
||||
}
|
||||
credentials = credentials.map((c) => this.ownershipService.addOwnedByAndSharedWith(c));
|
||||
}
|
||||
|
||||
@@ -130,6 +143,20 @@ export class CredentialsService {
|
||||
);
|
||||
|
||||
if (isDefaultSelect) {
|
||||
// Since we're filtering using project ID as part of the relation,
|
||||
// we end up filtering out all the other relations, meaning that if
|
||||
// it's shared to a project, it won't be able to find the home project.
|
||||
// To solve this, we have to get all the relation now, even though
|
||||
// we're deleting them later.
|
||||
if ((options.listQueryOptions?.filter?.shared as { projectId?: string })?.projectId) {
|
||||
const relations = await this.sharedCredentialsRepository.getAllRelationsForCredentials(
|
||||
credentials.map((c) => c.id),
|
||||
);
|
||||
credentials.forEach((c) => {
|
||||
c.shared = relations.filter((r) => r.credentialsId === c.id);
|
||||
});
|
||||
}
|
||||
|
||||
credentials = credentials.map((c) => this.ownershipService.addOwnedByAndSharedWith(c));
|
||||
}
|
||||
|
||||
|
||||
@@ -151,4 +151,13 @@ export class SharedCredentialsRepository extends Repository<SharedCredentials> {
|
||||
})
|
||||
)?.project;
|
||||
}
|
||||
|
||||
async getAllRelationsForCredentials(credentialIds: string[]) {
|
||||
return await this.find({
|
||||
where: {
|
||||
credentialsId: In(credentialIds),
|
||||
},
|
||||
relations: ['project'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ export const REGULAR_PROJECT_ADMIN_SCOPES: Scope[] = [
|
||||
'credential:delete',
|
||||
'credential:list',
|
||||
'credential:move',
|
||||
'credential:share',
|
||||
'project:list',
|
||||
'project:read',
|
||||
'project:update',
|
||||
|
||||
Reference in New Issue
Block a user