Skip to content

Commit

Permalink
Create IAM Roles during setup process.
Browse files Browse the repository at this point in the history
  • Loading branch information
fuyu committed Feb 2, 2024
1 parent 5d533ce commit e2808e4
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 43 deletions.
125 changes: 85 additions & 40 deletions packages/ehr/zambdas/scripts/invite-user.ts
Original file line number Diff line number Diff line change
@@ -1,64 +1,109 @@
import fetch from 'node-fetch';
import { Practitioner } from 'fhir/r4';
import { MANAGER_RULES } from '../src/shared';
import { ADMINISTRATOR_RULES, MANAGER_RULES, STAFF_RULES, FRONT_DESK_RULES } from '../src/shared';
import { RoleType } from '../src/shared/rolesUtils';

const DEFAULTS = {
firstName: 'Example',
lastName: 'Doctor',
};

async function createRole(projectApiUrl: string, accessToken: string, projectId: string): Promise<{ id: string }> {
console.log('building access policies');
const updateUserRoles = async (projectApiUrl: string, accessToken: string, projectId: string): Promise<{ id: string }> => {
console.log('Updating user roles.');

const zambdaRule = {
resource: ['Zambda:Function:*'],
action: ['Zambda:InvokeFunction'],
effect: 'Allow',
};

const administratorAccessPolicy = { rule: [...ADMINISTRATOR_RULES, zambdaRule] };
const managerAccessPolicy = { rule: [...MANAGER_RULES, zambdaRule] };
const staffAccessPolicy = { rule: [...STAFF_RULES, zambdaRule] };
const frontDeskAccessPolicy = { rule: [...FRONT_DESK_RULES, zambdaRule] };

const response = await fetch(`${projectApiUrl}/iam/role`, {
method: 'POST',
headers: {
accept: 'application/json',
authorization: `Bearer ${accessToken}`,
'content-type': 'application/json',
'x-zapehr-project-id': `${projectId}`,
},
body: JSON.stringify({
name: RoleType.Manager,
accessPolicy: managerAccessPolicy,
}),
const roles = [
{ name: RoleType.Administrator, accessPolicy: administratorAccessPolicy },
{ name: RoleType.Manager, accessPolicy: managerAccessPolicy },
{ name: RoleType.Staff, accessPolicy: staffAccessPolicy },
{ name: RoleType.FrontDesk, accessPolicy: frontDeskAccessPolicy },
];

const httpHeaders = {
accept: 'application/json',
authorization: `Bearer ${accessToken}`,
'content-type': 'application/json',
'x-zapehr-project-id': `${projectId}`,
};

console.log('searching for exisiting roles for the project');
const existingRolesResponse = await fetch(`${projectApiUrl}/iam/role`, {
method: 'GET',
headers: httpHeaders,
});
const existingRoles = await existingRolesResponse.json();
console.log('existingRoles: ', existingRoles);
if (!existingRolesResponse.ok) {
throw new Error('Error searching for existing roles');
}

if (!response.ok) {
const body = await response.json();
if (body.code === '4006') {
console.log('Role already exists. Fetching all roles...');

const getResponse = await fetch(`${projectApiUrl}/iam/role`, {
method: 'GET',
headers: {
accept: 'application/json',
authorization: `Bearer ${accessToken}`,
'content-type': 'application/json',
'x-zapehr-project-id': `${projectId}`,
},
});
let staffUserRole = undefined;

const roles = await getResponse.json();
return roles.find((r: any) => r.name === RoleType.Manager);
for (const role of roles) {
const roleName = role.name;
let foundRole;
let roleResJson = undefined;
if (existingRoles.length > 0) {
foundRole = existingRoles.find((existingRole: any) => existingRole.name === roleName);
}
else {
console.log(body);
throw new Error('Failed to create a role.');
if (foundRole) {
console.log(`${roleName} role found: `, foundRole);
const roleRes = await fetch(`${projectApiUrl}/iam/role/${foundRole.id}`, {
method: 'PATCH',
headers: httpHeaders,
body: JSON.stringify({ accessPolicy: role.accessPolicy }),
});
roleResJson = await roleRes.json();
if (!roleRes.ok) {
console.log(roleResJson);
throw new Error(`Failed to patch role ${roleName}`);
}
console.log(`${roleName} role accessPolicy patched: `, roleResJson, JSON.stringify(roleResJson.accessPolicy));
} else {
console.log(`creating ${roleName} role`);
const roleRes = await fetch(`${projectApiUrl}/iam/role`, {
method: 'POST',
headers: httpHeaders,
body: JSON.stringify({ name: roleName, accessPolicy: role.accessPolicy }),
});
roleResJson = await roleRes.json();
if (!roleRes.ok) {
console.log(roleResJson);
throw new Error(`Failed to create role ${roleName}`);
}
console.log(`${roleName} role: `, roleResJson, JSON.stringify(roleResJson.accessPolicy));
}

if (roleResJson.name === RoleType.Staff) {
staffUserRole = roleResJson;
}
}

const role = await response.json();
return role;
}
console.group(`Setting defaultSSOUserRole for project to Staff user role ${staffUserRole.id}`);
const endpoint = `${projectApiUrl}/project`;
const response = await fetch(endpoint, {
method: 'PATCH',
headers: httpHeaders,
body: JSON.stringify({ defaultSSOUserRoleId: staffUserRole.id }),
});
const responseJSON = await response.json();
console.log('response', responseJSON);
if (!response.ok) {
throw new Error(`Failed to set defaultSSOUserRole`);
}

return staffUserRole;
};


export async function inviteUser(
projectApiUrl: string,
Expand All @@ -69,7 +114,7 @@ export async function inviteUser(
accessToken: string,
projectId: string
): Promise<string> {
const role = await createRole(projectApiUrl, accessToken, projectId);
const defaultRole = await updateUserRoles(projectApiUrl, accessToken, projectId);

const practitioner: Practitioner = {
resourceType: 'Practitioner',
Expand All @@ -96,7 +141,7 @@ export async function inviteUser(
applicationId: applicationId,
resource: practitioner,
roles: [
role.id
defaultRole.id
]
}),
});
Expand Down
50 changes: 48 additions & 2 deletions packages/ehr/zambdas/src/shared/accessPolicies.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const MANAGER_RULES = [
export const ADMINISTRATOR_RULES = [
{
resource: [
'FHIR:Consent',
Expand All @@ -7,20 +7,52 @@ export const MANAGER_RULES = [
'FHIR:Organization',
'FHIR:QuestionnaireResponse',
'FHIR:DocumentReference',
'FHIR:Person',
],
action: ['FHIR:Search', 'FHIR:Read'],
effect: 'Allow',
},
{
resource: ['FHIR:Patient', 'FHIR:Location', 'FHIR:Appointment', 'FHIR:Encounter'],
resource: 'App:User',
action: ['App:ListAllUsers', 'App:GetUser'],
effect: 'Allow',
},
{
resource: ['FHIR:Patient', 'FHIR:Appointment', 'FHIR:Encounter', 'FHIR:Location'],
action: ['FHIR:Search', 'FHIR:Read', 'FHIR:Update'],
effect: 'Allow',
},
{
resource: 'Z3:*',
action: 'Z3:GetObject',
effect: 'Allow',
},
];

export const MANAGER_RULES = [
{
resource: [
'FHIR:Consent',
'FHIR:Coverage',
'FHIR:RelatedPerson',
'FHIR:Organization',
'FHIR:QuestionnaireResponse',
'FHIR:DocumentReference',
'FHIR:Person',
],
action: ['FHIR:Search', 'FHIR:Read'],
effect: 'Allow',
},
{
resource: 'App:User',
action: ['App:ListAllUsers', 'App:GetUser'],
effect: 'Allow',
},
{
resource: ['FHIR:Patient', 'FHIR:Appointment', 'FHIR:Encounter', 'FHIR:Location'],
action: ['FHIR:Search', 'FHIR:Read', 'FHIR:Update'],
effect: 'Allow',
},
{
resource: 'Z3:*',
action: 'Z3:GetObject',
Expand Down Expand Up @@ -61,6 +93,7 @@ export const FRONT_DESK_RULES = [
export const STAFF_RULES = [
{
resource: [
'FHIR:Appointment',
'FHIR:Patient',
'FHIR:Consent',
'FHIR:Coverage',
Expand All @@ -70,10 +103,16 @@ export const STAFF_RULES = [
'FHIR:Encounter',
'FHIR:QuestionnaireResponse',
'FHIR:DocumentReference',
'FHIR:Person',
],
action: ['FHIR:Search', 'FHIR:Read'],
effect: 'Allow',
},
{
resource: ['FHIR:Appointment', 'FHIR:Encounter', 'FHIR:Patient'],
action: ['FHIR:Update'],
effect: 'Allow',
},
{
resource: 'Z3:*',
action: 'Z3:GetObject',
Expand All @@ -84,6 +123,7 @@ export const STAFF_RULES = [
export const PROVIDER_RULES = [
{
resource: [
'FHIR:Appointment',
'FHIR:Patient',
'FHIR:Consent',
'FHIR:Coverage',
Expand All @@ -93,10 +133,16 @@ export const PROVIDER_RULES = [
'FHIR:Encounter',
'FHIR:QuestionnaireResponse',
'FHIR:DocumentReference',
'FHIR:Person',
],
action: ['FHIR:Search', 'FHIR:Read'],
effect: 'Allow',
},
{
resource: ['FHIR:Appointment', 'FHIR:Encounter', 'FHIR:Patient'],
action: ['FHIR:Update'],
effect: 'Allow',
},
{
resource: 'Z3:*',
action: 'Z3:GetObject',
Expand Down
2 changes: 1 addition & 1 deletion packages/ehr/zambdas/src/shared/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { getAuth0Token } from './getAuth0Token';
export { getSecret, SecretsKeys } from './secrets';
export { MANAGER_RULES, FRONT_DESK_RULES, STAFF_RULES, PROVIDER_RULES } from './accessPolicies';
export { ADMINISTRATOR_RULES, MANAGER_RULES, FRONT_DESK_RULES, STAFF_RULES, PROVIDER_RULES } from './accessPolicies';
1 change: 1 addition & 0 deletions packages/ehr/zambdas/src/shared/rolesUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const enum RoleType {
FrontDesk = 'FrontDesk',
Staff = 'Staff',
Provider = 'Provider',
Administrator = 'Administrator',
}

export async function getRoleId(roleName: string, token: string, projectApiUrl: string): Promise<string> {
Expand Down

0 comments on commit e2808e4

Please sign in to comment.