Skip to content

Commit 1bf32be

Browse files
Merge pull request #597 from IABTechLab/ans-UID2-5026-add-super-user-role
Add super user role
2 parents eaa7abb + 2197922 commit 1bf32be

File tree

9 files changed

+79
-26
lines changed

9 files changed

+79
-26
lines changed

src/api/entities/UserRole.ts

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export enum UserRoleId {
55
Admin = 1,
66
Operations = 2,
77
UID2Support = 3,
8+
SuperUser = 4,
89
}
910

1011
export const getUserRoleById = (id: number) => {
@@ -15,6 +16,8 @@ export const getUserRoleById = (id: number) => {
1516
return 'Operations';
1617
case 3:
1718
return 'Support';
19+
case 4:
20+
return 'SuperUser';
1821
default:
1922
return 'Invalid';
2023
}

src/api/middleware/userRoleMiddleware.ts

+19
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,25 @@ export const isUid2SupportCheck: Handler = async (req: ParticipantRequest, res,
2424
next();
2525
};
2626

27+
export const isSuperUser = async (userEmail: string) => {
28+
const user = await findUserByEmail(userEmail);
29+
const userWithSuperUserRole = await UserToParticipantRole.query()
30+
.where('userId', user!.id)
31+
.andWhere('userRoleId', UserRoleId.SuperUser)
32+
.first();
33+
return !!userWithSuperUserRole;
34+
};
35+
36+
export const isSuperUserCheck: Handler = async (req: ParticipantRequest, res, next) => {
37+
if (!(await isSuperUser(req.auth?.payload?.email as string))) {
38+
return res.status(403).json({
39+
message: 'Unauthorized. You do not have the necessary permissions.',
40+
errorHash: req.headers.traceId,
41+
});
42+
}
43+
next();
44+
};
45+
2746
export const isAdminOrUid2SupportCheck: Handler = async (req: ParticipantRequest, res, next) => {
2847
const { participant } = req;
2948
const userEmail = req.auth?.payload.email as string;

src/api/middleware/usersMiddleware.ts

+12-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { User } from '../entities/User';
55
import { getLoggers, getTraceId } from '../helpers/loggingHelpers';
66
import { UserParticipantRequest } from '../services/participantsService';
77
import { findUserByEmail, UserRequest } from '../services/usersService';
8-
import { isUid2Support } from './userRoleMiddleware';
8+
import { isSuperUser, isUid2Support } from './userRoleMiddleware';
99

1010
export const isUserBelongsToParticipant = async (
1111
email: string,
@@ -50,11 +50,21 @@ export const enrichCurrentUser = async (req: UserRequest, res: Response, next: N
5050
return next();
5151
};
5252

53-
export const enrichUserWithUid2Support = async (user: User) => {
53+
export const enrichUserWithSupportRoles = async (user: User) => {
5454
const userIsUid2Support = await isUid2Support(user.email);
55+
const userIsSuperUser = await isSuperUser(user.email);
5556
return {
5657
...user,
5758
isUid2Support: userIsUid2Support,
59+
isSuperUser: userIsSuperUser,
60+
};
61+
};
62+
63+
export const enrichUserWithSuperUser = async (user: User) => {
64+
const userIsSuperUser = await isSuperUser(user.email);
65+
return {
66+
...user,
67+
isSuperUser: userIsSuperUser,
5868
};
5969
};
6070

src/api/services/uid2SupportService.ts

-15
This file was deleted.

src/api/services/userService.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { UserToParticipantRole } from '../entities/UserToParticipantRole';
99
import { getTraceId } from '../helpers/loggingHelpers';
1010
import { mapClientTypeToParticipantType } from '../helpers/siteConvertingHelpers';
1111
import { getKcAdminClient } from '../keycloakAdminClient';
12-
import { enrichUserWithUid2Support } from '../middleware/usersMiddleware';
12+
import { enrichUserWithSupportRoles } from '../middleware/usersMiddleware';
1313
import { getSite } from './adminServiceClient';
1414
import { getApiRoles } from './apiKeyService';
1515
import {
@@ -37,15 +37,15 @@ export class UserService {
3737
public async getCurrentUser(req: UserRequest) {
3838
const userEmail = req.auth?.payload?.email as string;
3939
const user = await findUserByEmail(userEmail);
40-
const userWithUid2Support = await enrichUserWithUid2Support(user!);
41-
if (userWithUid2Support.isUid2Support) {
40+
const userWithSupportRoles = await enrichUserWithSupportRoles(user!);
41+
if (userWithSupportRoles.isUid2Support) {
4242
const allParticipants = await getAllParticipants();
43-
userWithUid2Support.participants = allParticipants;
43+
userWithSupportRoles.participants = allParticipants;
4444
}
45-
userWithUid2Support.participants = userWithUid2Support?.participants?.sort((a, b) =>
45+
userWithSupportRoles.participants = userWithSupportRoles?.participants?.sort((a, b) =>
4646
a.name.localeCompare(b.name)
4747
);
48-
return userWithUid2Support;
48+
return userWithSupportRoles;
4949
}
5050

5151
public async getDefaultParticipant(req: UserRequest) {

src/api/services/usersService.ts

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export interface SelfResendInviteRequest extends Request {
2828

2929
export type UserWithParticipantRoles = UserDTO & {
3030
isUid2Support: boolean;
31+
isSuperUser?: boolean;
3132
currentParticipantUserRoles?: UserRoleDTO[];
3233
};
3334

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Knex } from 'knex';
2+
3+
export async function up(knex: Knex): Promise<void> {
4+
await knex('userRoles').insert({
5+
id: 4,
6+
roleName: 'Super User',
7+
});
8+
}
9+
10+
export async function down(knex: Knex): Promise<void> {
11+
await knex('userRoles').where({ id: 4 }).del();
12+
}

src/web/components/Navigation/SideNav.tsx

+10-1
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ function MenuItem({
3636
export type SideNavProps = Readonly<{
3737
standardMenu: PortalRoute[];
3838
uid2SupportMenu: PortalRoute[];
39+
superUserMenu: PortalRoute[];
3940
}>;
40-
export function SideNav({ standardMenu, uid2SupportMenu }: SideNavProps) {
41+
export function SideNav({ standardMenu, uid2SupportMenu, superUserMenu }: SideNavProps) {
4142
return (
4243
<NavigationMenu className='side-nav'>
4344
<NavigationMenuList className='main-nav'>
@@ -54,6 +55,14 @@ export function SideNav({ standardMenu, uid2SupportMenu }: SideNavProps) {
5455
.map((m) => MenuItem(m))}
5556
</>
5657
)}
58+
{superUserMenu.length > 0 && (
59+
<>
60+
<div className='side-nav-divider' />
61+
{superUserMenu
62+
.filter((m) => (m.location ?? 'default') === 'default')
63+
.map((m) => MenuItem(m))}
64+
</>
65+
)}
5766
</NavigationMenuList>
5867
<NavigationMenuList className='nav-footer'>
5968
<NavigationMenuItem className='side-nav-item portal-documentation-link'>

src/web/screens/dashboard.tsx

+16-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,14 @@ export const StandardRoutes: PortalRoute[] = [
3333

3434
export const Uid2SupportRoutes: PortalRoute[] = [ManageParticipantsRoute];
3535

36-
export const DashboardRoutes: PortalRoute[] = [...StandardRoutes, ...Uid2SupportRoutes];
36+
// TODO: add route for Users List once created
37+
export const SuperUserRoutes: PortalRoute[] = [];
38+
39+
export const DashboardRoutes: PortalRoute[] = [
40+
...StandardRoutes,
41+
...Uid2SupportRoutes,
42+
...SuperUserRoutes,
43+
];
3744

3845
const standardMenu = StandardRoutes.filter((r) => r.description);
3946

@@ -42,10 +49,17 @@ function Dashboard() {
4249
const uid2SupportMenu = LoggedInUser?.user?.isUid2Support
4350
? Uid2SupportRoutes.filter((r) => r.description)
4451
: [];
52+
const superUserMenu = LoggedInUser?.user?.isSuperUser
53+
? SuperUserRoutes.filter((r) => r.description)
54+
: [];
4555

4656
return (
4757
<div className='app-panel'>
48-
<SideNav standardMenu={standardMenu} uid2SupportMenu={uid2SupportMenu} />
58+
<SideNav
59+
standardMenu={standardMenu}
60+
uid2SupportMenu={uid2SupportMenu}
61+
superUserMenu={superUserMenu}
62+
/>
4963
<div className='dashboard-content'>
5064
{!LoggedInUser?.user?.acceptedTerms ? <TermsAndConditionsDialog /> : <Outlet />}
5165
</div>

0 commit comments

Comments
 (0)