Skip to content

Commit 364fb9f

Browse files
committed
Merge branch 'release-v1.6.1' into release-v1.6.2
2 parents 595c3c9 + 9a8831c commit 364fb9f

File tree

17 files changed

+339
-170
lines changed

17 files changed

+339
-170
lines changed

.github/workflows/publish-release.yml

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,37 @@ jobs:
4848
- name: Get list of services
4949
id: get-services
5050
run: |
51-
services=$(grep "^ [^ ]" docker-compose.yml | grep -v '#' | awk -F: '{print $1}' | sed -e 's/^ *//')
51+
services=$(grep "^ [^ ]" docker-compose.yml | grep -v base| grep -v '#' | awk -F: '{print $1}' | sed -e 's/^ *//')
5252
services_json=$(echo $services | tr '\n' ',' | sed 's/,$//' | jq -R 'split(" ")' | tr -d '\n')
5353
# Set the list of service names as an output variable
5454
echo "services=$services_json" >> $GITHUB_OUTPUT
5555
echo "services=$services_json"
5656
57+
- name: Login to DockerHub
58+
uses: docker/login-action@v3
59+
with:
60+
username: ${{ secrets.DOCKER_USERNAME }}
61+
password: ${{ secrets.DOCKER_PASSWORD }}
62+
63+
- name: Build and push base image
64+
uses: docker/build-push-action@v6
65+
with:
66+
file: packages/Dockerfile.base
67+
context: .
68+
push: true
69+
tags: |
70+
opencrvs/ocrvs-base:${{ steps.set-version.outputs.version }}
71+
cache-from: type=registry,ref=opencrvs/ocrvs-base:${{ steps.set-version.outputs.version }}
72+
cache-to: type=inline
73+
5774
outputs:
5875
services: ${{ steps.get-services.outputs.services }}
5976
version: ${{ steps.set-version.outputs.version }}
6077

6178
build:
6279
needs: base
6380
strategy:
81+
fail-fast: false
6482
matrix:
6583
service: ${{ fromJSON(needs.base.outputs.services) }}
6684
runs-on: ubuntu-22.04
@@ -74,17 +92,23 @@ jobs:
7492
if: github.event_name == 'push'
7593

7694
- name: Login to DockerHub
77-
uses: docker/login-action@v1
95+
uses: docker/login-action@v3
7896
with:
7997
username: ${{ secrets.DOCKER_USERNAME }}
8098
password: ${{ secrets.DOCKER_PASSWORD }}
8199

82-
- name: Build ${{ matrix.service }}
83-
run: |
84-
echo ${{ matrix.service }}
85-
export VERSION=${{ github.event.inputs.release_version }}
86-
docker compose build ${{ matrix.service }}
87-
docker compose push ${{ matrix.service }}
100+
- name: Build and push
101+
uses: docker/build-push-action@v6
102+
with:
103+
file: packages/${{ matrix.service }}/Dockerfile
104+
build-args: |
105+
VERSION=${{ needs.base.outputs.version }}
106+
push: true
107+
context: .
108+
tags: |
109+
opencrvs/ocrvs-${{ matrix.service }}:${{ needs.base.outputs.version }}
110+
cache-from: type=registry,ref=opencrvs/ocrvs-${{ matrix.service }}:${{ needs.base.outputs.version}}
111+
cache-to: type=inline
88112

89113
security-scans:
90114
needs: [base, build]

CHANGELOG.md

Lines changed: 106 additions & 79 deletions
Large diffs are not rendered by default.

packages/client/src/views/RegisterForm/RegisterForm.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,6 +1115,7 @@ class RegisterFormView extends React.Component<FullProps, State> {
11151115
type="tertiary"
11161116
size="small"
11171117
onClick={this.props.goBack}
1118+
disabled={!canContinue}
11181119
>
11191120
<Icon name="ArrowLeft" size="medium" />
11201121
{intl.formatMessage(buttonMessages.back)}

packages/client/src/views/SysAdmin/Team/user/UserList.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ import { SysAdminContentWrapper } from '@client/views/SysAdmin/SysAdminContentWr
3939
import {
4040
getAddressName,
4141
getUserRoleIntlKey,
42-
UserStatus
42+
UserStatus,
43+
canDeactivateUser
4344
} from '@client/views/SysAdmin/Team/utils'
4445
import { LinkButton } from '@opencrvs/components/lib/buttons'
4546
import { Button } from '@opencrvs/components/lib/Button'
@@ -396,7 +397,7 @@ function UserListComponent(props: IProps) {
396397
)
397398

398399
const getMenuItems = useCallback(
399-
function getMenuItems(user: User) {
400+
function getMenuItems(user: User, userDetails: UserDetails | null) {
400401
const menuItems = [
401402
{
402403
label: intl.formatMessage(messages.editUserDetailsTitle),
@@ -432,7 +433,11 @@ function UserListComponent(props: IProps) {
432433
})
433434
}
434435

435-
if (user.status === 'active') {
436+
if (
437+
userDetails &&
438+
user.status === 'active' &&
439+
canDeactivateUser(user.id, userDetails)
440+
) {
436441
menuItems.push({
437442
label: intl.formatMessage(messages.deactivate),
438443
handler: () => toggleUserActivationModal(user)
@@ -530,7 +535,7 @@ function UserListComponent(props: IProps) {
530535
toggleButton={
531536
<Icon name="DotsThreeVertical" color="primary" size="large" />
532537
}
533-
menuItems={getMenuItems(user)}
538+
menuItems={getMenuItems(user, userDetails)}
534539
/>
535540
)}
536541
</Stack>

packages/client/src/views/SysAdmin/Team/utils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { IntlShape, MessageDescriptor } from 'react-intl'
1313
import { messages } from '@client/i18n/messages/views/userSetup'
1414
import { SystemRoleType } from '@client/utils/gateway'
1515
import { ILocation, IOfflineData } from '@client/offline/reducer'
16+
import { UserDetails } from '@client/utils/userUtils'
1617

1718
export enum UserStatus {
1819
ACTIVE,
@@ -112,3 +113,7 @@ export function getUserSystemRole(
112113
export const getUserRoleIntlKey = (_roleId: string) => {
113114
return `role.${_roleId}`
114115
}
116+
117+
export const canDeactivateUser = (id: string, userDetails: UserDetails) => {
118+
return id !== userDetails.id ? true : false
119+
}

packages/client/src/views/UserAudit/UserAudit.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ import { AvatarSmall } from '@client/components/Avatar'
2323
import styled from 'styled-components'
2424
import { ToggleMenu } from '@opencrvs/components/lib/ToggleMenu'
2525
import { Button } from '@opencrvs/components/lib/Button'
26-
import { getUserRoleIntlKey } from '@client/views/SysAdmin//Team/utils'
26+
import {
27+
getUserRoleIntlKey,
28+
canDeactivateUser
29+
} from '@client/views/SysAdmin/Team/utils'
2730
import { EMPTY_STRING, LANG_EN } from '@client/utils/constants'
2831
import { Loader } from '@opencrvs/components/lib/Loader'
2932
import { messages as userSetupMessages } from '@client/i18n/messages/views/userSetup'
@@ -246,7 +249,11 @@ export const UserAudit = () => {
246249
)
247250
}
248251

249-
if (status === 'active') {
252+
if (
253+
status === 'active' &&
254+
userDetails &&
255+
canDeactivateUser(userId, userDetails)
256+
) {
250257
menuItems.push({
251258
label: intl.formatMessage(sysMessages.deactivate),
252259
handler: () => toggleUserActivationModal()

packages/components/src/Headings/Headings.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ export const Heading2 = styled.h2`
2323
export const Heading3 = styled.h3`
2424
${({ theme }) => theme.fonts.h3};
2525
color: ${({ theme }) => theme.colors.grey600};
26-
padding: 8px 0px;
26+
padding: 18px 0px 8px 0px;
2727
border-top: 1px solid ${({ theme }) => theme.colors.grey200};
2828
`

packages/gateway/src/features/role/root-resolvers.test.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,17 +202,42 @@ describe('Role root resolvers', () => {
202202
}
203203
]
204204
it('returns full role list', async () => {
205+
const sysAdminToken = jwt.sign(
206+
{ scope: ['natlsysadmin'] },
207+
readFileSync('./test/cert.key'),
208+
{
209+
subject: 'ba7022f0ff4822',
210+
algorithm: 'RS256',
211+
issuer: 'opencrvs:auth-service',
212+
audience: 'opencrvs:gateway-user'
213+
}
214+
)
215+
const authHeaderSysAdmin = {
216+
Authorization: `Bearer ${sysAdminToken}`
217+
}
205218
fetch.mockResponseOnce(JSON.stringify(dummyRoleList))
206-
207219
const response = await resolvers.Query!.getSystemRoles(
208220
{},
209221
{},
210-
{ headers: undefined }
222+
{ headers: authHeaderSysAdmin }
211223
)
212224

213225
expect(response).toEqual(dummyRoleList)
214226
})
215227
it('returns filtered role list', async () => {
228+
const sysAdminToken = jwt.sign(
229+
{ scope: ['sysadmin'] },
230+
readFileSync('./test/cert.key'),
231+
{
232+
subject: 'ba7022f0ff4822',
233+
algorithm: 'RS256',
234+
issuer: 'opencrvs:auth-service',
235+
audience: 'opencrvs:gateway-user'
236+
}
237+
)
238+
const authHeaderSysAdmin = {
239+
Authorization: `Bearer ${sysAdminToken}`
240+
}
216241
fetch.mockResponseOnce(JSON.stringify([dummyRoleList[2]]))
217242

218243
const response = await resolvers.Query!.getSystemRoles(
@@ -225,7 +250,7 @@ describe('Role root resolvers', () => {
225250
type: 'Mayor',
226251
active: true
227252
},
228-
{ headers: undefined }
253+
{ headers: authHeaderSysAdmin }
229254
)
230255
expect(response).toEqual([dummyRoleList[2]])
231256
})

packages/gateway/src/features/role/root-resolvers.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@ import { GQLResolver } from '@gateway/graphql/schema'
1212
import fetch from '@gateway/fetch'
1313
import { USER_MANAGEMENT_URL } from '@gateway/constants'
1414
import { IRoleSearchPayload } from '@gateway/features/role/type-resolvers'
15-
import { transformMongoComparisonObject } from '@gateway/features/role/utils'
15+
import {
16+
getAccessibleRolesForScope,
17+
SystemRole,
18+
transformMongoComparisonObject
19+
} from '@gateway/features/role/utils'
1620
import { hasScope } from '@gateway/features/user/utils'
21+
import { getTokenPayload } from '@opencrvs/commons/authentication'
1722

1823
export const resolvers: GQLResolver = {
1924
Query: {
@@ -51,6 +56,7 @@ export const resolvers: GQLResolver = {
5156
if (active !== null) {
5257
payload = { ...payload, active }
5358
}
59+
5460
const res = await fetch(`${USER_MANAGEMENT_URL}getSystemRoles`, {
5561
method: 'POST',
5662
body: JSON.stringify(payload),
@@ -59,7 +65,13 @@ export const resolvers: GQLResolver = {
5965
...authHeader
6066
}
6167
})
62-
return await res.json()
68+
69+
const { scope } = getTokenPayload(authHeader.Authorization.split(' ')[1])
70+
const accessibleSysAdminRoles = getAccessibleRolesForScope(scope)
71+
const allSysAdminRoles = (await res.json()) as SystemRole[]
72+
return allSysAdminRoles.filter((sysAdminRole) =>
73+
accessibleSysAdminRoles?.includes(sysAdminRole.value)
74+
)
6375
}
6476
},
6577
Mutation: {

packages/gateway/src/features/role/utils.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,58 @@
88
*
99
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
1010
*/
11+
12+
import { Scope } from '@opencrvs/commons/authentication'
13+
14+
export const SYSTEM_ROLE_KEYS = [
15+
'FIELD_AGENT',
16+
'LOCAL_REGISTRAR',
17+
'LOCAL_SYSTEM_ADMIN',
18+
'NATIONAL_REGISTRAR',
19+
'NATIONAL_SYSTEM_ADMIN',
20+
'PERFORMANCE_MANAGEMENT',
21+
'REGISTRATION_AGENT'
22+
] as const
23+
24+
// Derive the type from SYSTEM_ROLE_KEYS
25+
type SystemRoleKeyType = (typeof SYSTEM_ROLE_KEYS)[number]
26+
27+
export const SysAdminAccessMap: Partial<
28+
Record<SystemRoleKeyType, SystemRoleKeyType[]>
29+
> = {
30+
LOCAL_SYSTEM_ADMIN: [
31+
'FIELD_AGENT',
32+
'LOCAL_REGISTRAR',
33+
'LOCAL_SYSTEM_ADMIN',
34+
'PERFORMANCE_MANAGEMENT',
35+
'REGISTRATION_AGENT'
36+
],
37+
NATIONAL_SYSTEM_ADMIN: [
38+
'FIELD_AGENT',
39+
'LOCAL_REGISTRAR',
40+
'LOCAL_SYSTEM_ADMIN',
41+
'NATIONAL_REGISTRAR',
42+
'NATIONAL_SYSTEM_ADMIN',
43+
'PERFORMANCE_MANAGEMENT',
44+
'REGISTRATION_AGENT'
45+
]
46+
}
47+
48+
type UserRole = {
49+
labels: Label[]
50+
}
51+
52+
type Label = {
53+
lang: string
54+
label: string
55+
}
56+
57+
export type SystemRole = {
58+
value: SystemRoleKeyType
59+
roles: UserRole[]
60+
active: boolean
61+
creationDate: number
62+
}
1163
export interface IComparisonObject {
1264
eq?: string
1365
gt?: string
@@ -46,3 +98,16 @@ export function transformMongoComparisonObject(
4698
{}
4799
)
48100
}
101+
102+
export function getAccessibleRolesForScope(scope: Scope[]) {
103+
let roleFilter: keyof typeof SysAdminAccessMap
104+
if (scope.includes('natlsysadmin')) {
105+
roleFilter = 'NATIONAL_SYSTEM_ADMIN'
106+
} else if (scope.includes('sysadmin')) {
107+
roleFilter = 'LOCAL_SYSTEM_ADMIN'
108+
} else {
109+
throw Error('Create user is only allowed for sysadmin/natlsysadmin')
110+
}
111+
112+
return SysAdminAccessMap[roleFilter]
113+
}

0 commit comments

Comments
 (0)