1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

feat: add service method to retrieve group and project access for all users (#4708)

This commit is contained in:
Simon Hornby 2023-09-14 11:43:39 +02:00 committed by GitHub
parent 4b90fe7790
commit 10afbc8a9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 123 additions and 4 deletions

View File

@ -12,7 +12,7 @@ import {
IUserRole,
IUserWithProjectRoles,
} from '../types/stores/access-store';
import { IPermission } from '../types/model';
import { IPermission, IUserAccessOverview } from '../types/model';
import NotFoundError from '../error/notfound-error';
import {
ENVIRONMENT_PERMISSION_TYPE,
@ -758,4 +758,40 @@ export class AccessStore implements IAccessStore {
[destinationEnvironment, sourceEnvironment],
);
}
async getUserAccessOverview(): Promise<IUserAccessOverview[]> {
const result = await this.db
.raw(`SELECT u.id, u.created_at, u.name, u.email, u.seen_at, up.p_array as projects, gr.p_array as groups, r.name as root_role
FROM users u, LATERAL (
SELECT ARRAY (
SELECT ru.project
FROM role_user ru
WHERE ru.user_id = u.id
) AS p_array
) up, LATERAL (
SELECT r.name
FROM role_user ru
inner join roles r on ru.role_id = r.id
where ru.user_id = u.id and r.type='root'
) r, LATERAL (
SELECT ARRAY (
select g.name from group_user gu
left join groups g on g.id = gu.group_id
WHERE gu.user_id = u.id
) AS p_array
) gr
order by u.id;`);
return result.rows.map((row) => {
return {
userId: row.id,
createdAt: row.created_at,
userName: row.name,
userEmail: row.email,
lastSeen: row.seen_at,
accessibleProjects: row.projects,
groups: row.groups,
rootRole: row.root_role,
};
});
}
}

View File

@ -40,7 +40,7 @@ import InvalidOperationError from '../error/invalid-operation-error';
import BadDataError from '../error/bad-data-error';
import { IGroup } from '../types/group';
import { GroupService } from './group-service';
import { IFlagResolver, IUnleashConfig } from 'lib/types';
import { IFlagResolver, IUnleashConfig, IUserAccessOverview } from 'lib/types';
const { ADMIN } = permissions;
@ -736,4 +736,8 @@ export class AccessService {
await this.validateRoleIsUnique(role.name, existingId);
return cleanedRole;
}
async getUserAccessOverview(): Promise<IUserAccessOverview[]> {
return this.store.getUserAccessOverview();
}
}

View File

@ -459,3 +459,14 @@ export interface IFeatureStrategySegment {
featureStrategyId: string;
segmentId: number;
}
export interface IUserAccessOverview {
userId: number;
createdAt?: Date;
userName?: string;
userEmail: number;
lastSeen?: Date;
accessibleProjects: string[];
groups: string[];
rootRole: string;
}

View File

@ -1,6 +1,6 @@
import { PermissionRef } from 'lib/services/access-service';
import { IGroupModelWithProjectRole } from '../group';
import { IPermission, IUserWithRole } from '../model';
import { IPermission, IUserAccessOverview, IUserWithRole } from '../model';
import { Store } from './store';
export interface IUserPermission {
@ -200,4 +200,5 @@ export interface IAccessStore extends Store<IRole, number> {
): Promise<number[]>;
removeUserAccess(projectId: string, userId: number): Promise<void>;
removeGroupAccess(projectId: string, groupId: number): Promise<void>;
getUserAccessOverview(): Promise<IUserAccessOverview[]>;
}

View File

@ -10,6 +10,7 @@ import {
ICreateGroupUserModel,
IPermission,
IUnleashStores,
IUserAccessOverview,
} from '../../../lib/types';
import FeatureToggleService from '../../../lib/services/feature-toggle-service';
import ProjectService from '../../../lib/services/project-service';
@ -1851,3 +1852,65 @@ test('remove group access should remove all project roles, while leaving root ro
expect(newAssignedPermissions.length).toBe(1);
expect(newAssignedPermissions[0].permission).toBe(permissions.ADMIN);
});
test('access overview should have admin access and default project for admin user', async () => {
const email = 'a-person@places.com';
const { userStore } = stores;
const user = await userStore.insert({
name: 'Some User',
email,
});
await accessService.setUserRootRole(user.id, adminRole.id);
const accessOverView: IUserAccessOverview[] =
await accessService.getUserAccessOverview();
const userAccess = accessOverView.find(
(overviewRow) => overviewRow.userId == user.id,
)!;
expect(userAccess.userId).toBe(user.id);
expect(userAccess.rootRole).toBe('Admin');
expect(userAccess.accessibleProjects).toStrictEqual(['default']);
});
test('access overview should have group access for groups that they are in', async () => {
const email = 'a-nother-person@places.com';
const { userStore } = stores;
const user = await userStore.insert({
name: 'Some Other User',
email,
});
await accessService.setUserRootRole(user.id, adminRole.id);
const group = await stores.groupStore.create({
name: 'Test Group',
});
await stores.groupStore.addUsersToGroup(
group.id,
[
{
user: {
id: user.id,
},
},
],
'Admin',
);
const accessOverView: IUserAccessOverview[] =
await accessService.getUserAccessOverview();
const userAccess = accessOverView.find(
(overviewRow) => overviewRow.userId == user.id,
)!;
expect(userAccess.userId).toBe(user.id);
expect(userAccess.rootRole).toBe('Admin');
expect(userAccess.groups).toStrictEqual(['Test Group']);
});

View File

@ -10,7 +10,7 @@ import {
IUserWithProjectRoles,
} from '../../lib/types/stores/access-store';
import { IPermission } from 'lib/types/model';
import { IRoleStore } from 'lib/types';
import { IRoleStore, IUserAccessOverview } from 'lib/types';
import FakeRoleStore from './fake-role-store';
import { PermissionRef } from 'lib/services/access-service';
@ -289,6 +289,10 @@ class AccessStoreMock implements IAccessStore {
removeGroupAccess(projectId: string, groupId: number): Promise<void> {
throw new Error('Method not implemented.');
}
getUserAccessOverview(): Promise<IUserAccessOverview[]> {
throw new Error('Method not implemented.');
}
}
module.exports = AccessStoreMock;