Skip to content

Commit e25f572

Browse files
authored
Merge pull request #108 from uwblueprint/INTW26-reviewer-authorization
[INTW26] Implement reviewer authorization
2 parents 3ce3feb + 407b72e commit e25f572

File tree

4 files changed

+64
-0
lines changed

4 files changed

+64
-0
lines changed

backend/typescript/graphql/resolvers/authResolvers.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,19 @@ const authResolvers = {
5353
);
5454
return isAuthorized;
5555
},
56+
isAuthorizedReviewer: async (
57+
_parent: undefined,
58+
{
59+
accessToken,
60+
applicantRecordId,
61+
}: { accessToken: string; applicantRecordId: string },
62+
): Promise<boolean> => {
63+
const isAuthorized = await authService.isAuthorizedReviewer(
64+
accessToken,
65+
applicantRecordId,
66+
);
67+
return isAuthorized;
68+
},
5669
},
5770
Mutation: {
5871
login: async (

backend/typescript/graphql/types/authType.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ const authType = gql`
2525
extend type Query {
2626
login(email: String!, password: String!): loginOK!
2727
isAuthorizedByRole(accessToken: String!, roles: [Role!]!): Boolean!
28+
isAuthorizedReviewer(
29+
accessToken: String!
30+
applicantRecordId: String!
31+
): Boolean!
2832
}
2933
3034
extend type Mutation {

backend/typescript/services/implementations/authService.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { AuthDTO, Role, Token } from "../../types";
77
import { getErrorMessage } from "../../utilities/errorUtils";
88
import FirebaseRestClient from "../../utilities/firebaseRestClient";
99
import logger from "../../utilities/logger";
10+
import ReviewedApplicantRecord from "../../models/reviewedApplicantRecord.model";
1011

1112
const Logger = logger(__filename);
1213

@@ -272,6 +273,41 @@ class AuthService implements IAuthService {
272273
throw error;
273274
}
274275
}
276+
277+
async isAuthorizedReviewer(
278+
accessToken: string,
279+
applicantRecordId: string,
280+
): Promise<boolean> {
281+
try {
282+
const decodedIdToken: firebaseAdmin.auth.DecodedIdToken =
283+
await firebaseAdmin.auth().verifyIdToken(accessToken, true);
284+
const userId = await this.userService.getUserIdByAuthId(
285+
decodedIdToken.uid,
286+
);
287+
288+
const firebaseUser = await firebaseAdmin
289+
.auth()
290+
.getUser(decodedIdToken.uid);
291+
292+
if (!firebaseUser.emailVerified) {
293+
return false;
294+
}
295+
296+
const reviewedApplicantRecord = await ReviewedApplicantRecord.findOne({
297+
where: {
298+
applicantRecordId,
299+
reviewerId: Number(userId),
300+
},
301+
});
302+
303+
return reviewedApplicantRecord !== null;
304+
} catch (error) {
305+
Logger.error(
306+
`Failed to verify if user is authorized reviewer for applicant record ${applicantRecordId}`,
307+
);
308+
throw error;
309+
}
310+
}
275311
}
276312

277313
export default AuthService;

backend/typescript/services/interfaces/authService.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,17 @@ interface IAuthService {
9090
* @returns true if email sent successfully
9191
*/
9292
sendSignInLink(email: string): Promise<boolean>;
93+
94+
/**
95+
* Checks whether a user is an authorized reviewer for a specific applicant record
96+
* @param accessToken user's access token
97+
* @param applicantRecordId ID of the applicant record
98+
* @returns true if user is authorized as a reviewer, false otherwise
99+
*/
100+
isAuthorizedReviewer(
101+
accessToken: string,
102+
applicantRecordId: string,
103+
): Promise<boolean>;
93104
}
94105

95106
export default IAuthService;

0 commit comments

Comments
 (0)