-
Notifications
You must be signed in to change notification settings - Fork 0
509: Add Study Read Permission to Approved User #526
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
| { | ||
| studyName: 'TEST-CA', | ||
| studyID: 'TEST-CA', | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fetching the list of studies available is not implemented yet.. hardcoding for dev/testing purpose
|
|
||
| export type ApproveApplication = { | ||
| applicationId: number; // The ID of the application to be approved | ||
| approverAccessToken: string; // The access token of the user approving the application |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using the approver's access_token to add permissions in authz service. This requires that the DAC_CHAIR to be part of the CO:admin` group to allow this type of requests
| <CheckCircleFilled style={{ color: token.colorPrimary, fontSize: 30 }} /> | ||
| <Flex vertical> | ||
| <Text strong>{translate('dashboard.hasAccess')}</Text> | ||
| <Text>{formatDate(expiresAt)}</Text> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removing Expires At date on the top status bar, as per ticket specification.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the check on line 63 confirm that the current date is before the expiresAt date?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added a condition as suggested.
| # -- Approved Application -- | ||
| APPROVED_PERMISSION_EXPIRES_IN_DAYS=365 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new env variable, updated readme file for more details
joneubank
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good work. We need a bit more to deal with error cases. What is missing is that if the request to add permissions fails, then we have no way remedy. We may need a manual process to correct individual failures, or some other mechanism to ensure the AuthZ state has all data from DACO.
| } | ||
|
|
||
| const authConfigSchema = z.object({ | ||
| APPROVED_PERMISSION_EXPIRES_IN_DAYS: z.coerce.number().int().optional().default(365), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
| institutionalEmail, | ||
| approverAccessToken, | ||
| requestedStudies, | ||
| }: AssignUserPermissionsAndNotifyParams) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be good to declare the return type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added return type to this function.
| const permissionAdded = await assignUserPermissions({ | ||
| institutionalEmail: applicant_institutional_email, | ||
| approverAccessToken, | ||
| requestedStudies: requested_studies, | ||
| }); | ||
|
|
||
| if (permissionAdded) { | ||
| emailService.sendEmailApproval({ | ||
| id: application.id, | ||
| to: applicant_institutional_email, | ||
| name: applicant_first_name || 'N/A', | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should have some behaviour that handles the case where we fail to successfully assign user permissions.
We should at minimum have a retry mechanism for failed requests.
What I'm thinking about though is that we need a mechanism to notify an admin that the approval was completed but the permissions weren't granted, and then the admin needs a mechanism to correct the issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To handle user permissions, included following changes:
A validation was added early in the code, to make sure applicant and collaborator's email address are valid PCGL users. This avoid approving an application with invalid users.Implemented aretrymechanism on calls used to add user permission to Authz serviceIn case of an error occurs during approving the application. the DAC approver and the Applicant will be notified via email, UI displays a notification toast with a friendly error message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A mechanism to handle retries will be implemented in a separate PR.
| /** | ||
| * This function assigns user permissions to the requested studies | ||
| * First it looks for the associated PCGL user ID using the institutional email | ||
| * Then it assigns the study permissions using the approver's access token | ||
| * | ||
| * @param param0 | ||
| * @returns | ||
| */ | ||
| export const assignUserPermissions = async ({ | ||
| institutionalEmail, | ||
| approverAccessToken, | ||
| requestedStudies, | ||
| }: AssignUserPermissionsAndNotifyParams) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function could fail at multiple stages, and the output from this function should correctly communicate where it failed and any work that was done.
- It could fail due to auth or network reasons not related to this specific request content
- It could fail because the user was not found (should not happen but let's handle this anyways).
- It could fail adding permission for some studies, but succeed for others.
If the request to add permissions fails for any studies, the response should include a list of studies failed and studies succeeded.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Retry Authz calls and Emails are sent when an error occur during approval.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Added a retry mechanism to the main function used for making requests to Authz service.
- Once the retry limit is exceeded, errors are not handled in this PR, a separate PR will be required to implement proper error handling.
apps/api/README.md
Outdated
| | `VALKEY_USER` | Valkey user name. | `string` | Required | | | ||
| | | | | | | | ||
| | `DISABLE_AUTH` | Set this to `true` to disable auth. Any other value and this will default to auth enabled. | `boolean` | Optional | `false` | | ||
| | `APPROVED_PERMISSION_EXPIRES_IN_DAYS` | Number of days the applicant will have **read** access to the study when application gets approved. | `number` | Optional | `365` days | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🙏
| <CheckCircleFilled style={{ color: token.colorPrimary, fontSize: 30 }} /> | ||
| <Flex vertical> | ||
| <Text strong>{translate('dashboard.hasAccess')}</Text> | ||
| <Text>{formatDate(expiresAt)}</Text> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the check on line 63 confirm that the current date is before the expiresAt date?
| const verifyUserAccountsResult = await verifyApplicationUserAccounts(applicationId, approverAccessToken); | ||
|
|
||
| if (!verifyUserAccountsResult.success) { | ||
| return verifyUserAccountsResult; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
applicant and collaborators must be already registered as pcgl users.
| message: translate('modals.approveApplication.notifications.applicationApproveFailed'), | ||
| }); | ||
| onError: async (data, { applicationId }) => { | ||
| switch (data.error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
User friendly errors to be displayed in the UI when application has errors during approval.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix a react warning. Adding key prop to each component in a list.
joneubank
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You added a retry parameter to several functions but never specified the number of times they should be retried. Do you want to add an environment variable to the app to configure these, and also set a default retry at 2?
|
|
||
| if (response.status === 404) { | ||
| const message = `No user found with email ${emailAddress}.`; | ||
| logger.info('[AUTHZ]:', `retry: ${retry}`, message); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the comment specify "Retries remaining:" instead of "retry:"? As written, the log seems to indicate how many times its already been attempted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed retry from this function, and updated the logs
| export const lookupUserByEmail = async ( | ||
| emailAddress: string, | ||
| accessToken: string, | ||
| retry = 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Functional and simple retry mechanism. In the future, we should look for a way to make this reusable without having to implement all the retry logging and count tracking and final return cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
refactored the retry mechanism, now is handled in a single the function used to call authz, also included a generic fetchWithRetry.
| FETCH_RETRIES: z.coerce.number().optional().default(3), | ||
| FETCH_RETRY_DELAY_MS: z.coerce.number().optional().default(500), | ||
| FETCH_TIMEOUT_MS: z.coerce.number().optional().default(10000), | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new env. variables used to configure retry in fetch funcions
| // Retry mechanism for authz requires to refresh service token before retying fetch call | ||
| const { FETCH_RETRIES, FETCH_RETRY_DELAY_MS } = serverConfig; | ||
| let attempt = 0; | ||
| while (true) { | ||
| try { | ||
| const response = await _fetchFromAuthZ(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
centralized the retry mechanism + refresh Token when calling Authz service.
| * @param init | ||
| * @returns | ||
| */ | ||
| export async function fetchWithRetry(url: string, init: RequestInit): Promise<Response> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
generic fetch wrapper with retry options
99e7e8a to
664198f
Compare
|
PR updated based on feedback received. Note: A separate PR is required to handle permissions to unregistered users. |
Description
When application is approved, it should assign applicant and collaborators the corresponding permission to the specified study.
Details
authzto assign an user to an specific study. (more info here)🐛 An issue reported in authz. Permissions are removed automatically after couple of minutesRESOLVEDTicket
#509