Skip to content

Commit

Permalink
Filter User roles based on user that's requesting
Browse files Browse the repository at this point in the history
This is avoid users with lower roles creating or updating other users with higher roles

#7698
  • Loading branch information
Siyasanga committed Nov 28, 2024
1 parent 0d1673c commit a08c8cf
Show file tree
Hide file tree
Showing 4 changed files with 11,006 additions and 56 deletions.
16 changes: 14 additions & 2 deletions packages/gateway/src/features/role/root-resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ import { GQLResolver } from '@gateway/graphql/schema'
import fetch from '@gateway/fetch'
import { USER_MANAGEMENT_URL } from '@gateway/constants'
import { IRoleSearchPayload } from '@gateway/features/role/type-resolvers'
import { transformMongoComparisonObject } from '@gateway/features/role/utils'
import {
getAccessibleRolesForScope,
SystemRole,
transformMongoComparisonObject
} from '@gateway/features/role/utils'
import { hasScope } from '@gateway/features/user/utils'
import { getTokenPayload } from '@opencrvs/commons/authentication'

export const resolvers: GQLResolver = {
Query: {
Expand Down Expand Up @@ -51,6 +56,7 @@ export const resolvers: GQLResolver = {
if (active !== null) {
payload = { ...payload, active }
}

const res = await fetch(`${USER_MANAGEMENT_URL}getSystemRoles`, {
method: 'POST',
body: JSON.stringify(payload),
Expand All @@ -59,7 +65,13 @@ export const resolvers: GQLResolver = {
...authHeader
}
})
return await res.json()

const { scope } = getTokenPayload(authHeader.Authorization.split(' ')[1])
const accessibleSysAdminRoles = getAccessibleRolesForScope(scope)
const allSysAdminRoles = (await res.json()) as SystemRole[]
return allSysAdminRoles.filter((sysAdminRole) =>
accessibleSysAdminRoles?.includes(sysAdminRole.value)
)
}
},
Mutation: {
Expand Down
65 changes: 65 additions & 0 deletions packages/gateway/src/features/role/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Scope } from '@opencrvs/commons/authentication'

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
Expand All @@ -8,6 +10,56 @@
*
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/

export const SYSTEM_ROLE_KEYS = [
'FIELD_AGENT',
'LOCAL_REGISTRAR',
'LOCAL_SYSTEM_ADMIN',
'NATIONAL_REGISTRAR',
'NATIONAL_SYSTEM_ADMIN',
'PERFORMANCE_MANAGEMENT',
'REGISTRATION_AGENT'
] as const

// Derive the type from SYSTEM_ROLE_KEYS
type SystemRoleKeyType = (typeof SYSTEM_ROLE_KEYS)[number]

export const SysAdminAccessMap: Partial<
Record<SystemRoleKeyType, SystemRoleKeyType[]>
> = {
LOCAL_SYSTEM_ADMIN: [
'FIELD_AGENT',
'LOCAL_REGISTRAR',
'LOCAL_SYSTEM_ADMIN',
'PERFORMANCE_MANAGEMENT',
'REGISTRATION_AGENT'
],
NATIONAL_SYSTEM_ADMIN: [
'FIELD_AGENT',
'LOCAL_REGISTRAR',
'LOCAL_SYSTEM_ADMIN',
'NATIONAL_REGISTRAR',
'NATIONAL_SYSTEM_ADMIN',
'PERFORMANCE_MANAGEMENT',
'REGISTRATION_AGENT'
]
}

type UserRole = {
labels: Label[]
}

type Label = {
lang: string
label: string
}

export type SystemRole = {
value: SystemRoleKeyType
roles: UserRole[]
active: boolean
creationDate: number
}
export interface IComparisonObject {
eq?: string
gt?: string
Expand Down Expand Up @@ -46,3 +98,16 @@ export function transformMongoComparisonObject(
{}
)
}

export function getAccessibleRolesForScope(scope: Scope[]) {
let roleFilter: keyof typeof SysAdminAccessMap
if (scope.includes('natlsysadmin')) {
roleFilter = 'NATIONAL_SYSTEM_ADMIN'
} else if (scope.includes('sysadmin')) {
roleFilter = 'LOCAL_SYSTEM_ADMIN'
} else {
throw Error('Create user is only allowed for sysadmin/natlsysadmin')
}

return SysAdminAccessMap[roleFilter]
}
56 changes: 2 additions & 54 deletions packages/gateway/src/features/user/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import decode from 'jwt-decode'
import fetch from '@gateway/fetch'
import { Scope } from '@opencrvs/commons/authentication'
import { GQLUserInput } from '@gateway/graphql/schema'
import { SysAdminAccessMap } from '@gateway/features/role/utils'

export interface ITokenPayload {
sub: string
Expand All @@ -37,60 +38,6 @@ export type scopeType =
| 'sysadmin'
| 'performance'

type RoleSearchPayload = {
title?: string
value?: MongoComparisonObject
role?: string
active?: boolean
sortBy?: string
sortOrder?: string
}

type MongoComparisonObject = {
$eq?: string
$gt?: string
$lt?: string
$gte?: string
$lte?: string
$in?: string[]
$ne?: string
$nin?: string[]
}

const SYSTEM_ROLE_TYPES = [
'FIELD_AGENT',
'LOCAL_REGISTRAR',
'LOCAL_SYSTEM_ADMIN',
'NATIONAL_REGISTRAR',
'NATIONAL_SYSTEM_ADMIN',
'PERFORMANCE_MANAGEMENT',
'REGISTRATION_AGENT'
] as const

// Derive the type from SYSTEM_ROLE_TYPES
type SystemRoleType = (typeof SYSTEM_ROLE_TYPES)[number]

export const SysAdminAccessMap: Partial<
Record<SystemRoleType, SystemRoleType[]>
> = {
LOCAL_SYSTEM_ADMIN: [
'FIELD_AGENT',
'LOCAL_REGISTRAR',
'LOCAL_SYSTEM_ADMIN',
'PERFORMANCE_MANAGEMENT',
'REGISTRATION_AGENT'
],
NATIONAL_SYSTEM_ADMIN: [
'FIELD_AGENT',
'LOCAL_REGISTRAR',
'LOCAL_SYSTEM_ADMIN',
'NATIONAL_REGISTRAR',
'NATIONAL_SYSTEM_ADMIN',
'PERFORMANCE_MANAGEMENT',
'REGISTRATION_AGENT'
]
}

export async function getUser(
body: { [key: string]: string | undefined },
authHeader: IAuthHeader
Expand All @@ -105,6 +52,7 @@ export async function getUser(
})
return await res.json()
}

export function canAssignRole(
loggedInUserScope: Scope[],
userToSave: GQLUserInput
Expand Down
Loading

0 comments on commit a08c8cf

Please sign in to comment.