Skip to content

Commit 79e5709

Browse files
authored
Merge pull request #122 from UoA-eResearch/rc2.2.0
Rc2.2.0
2 parents 3ef686c + 143ef16 commit 79e5709

File tree

151 files changed

+6548
-3969
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

151 files changed

+6548
-3969
lines changed

.github/workflows/linting.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Lint
2+
3+
on:
4+
# Trigger the workflow on push or pull request,
5+
# but only for the main branch
6+
push:
7+
branches:
8+
- master
9+
pull_request:
10+
branches:
11+
- master
12+
13+
jobs:
14+
run-linters:
15+
name: Run linters
16+
runs-on: ubuntu-latest
17+
18+
steps:
19+
- name: Check out Git repository
20+
uses: actions/checkout@v2
21+
22+
- name: Set up Node.js
23+
uses: actions/setup-node@v1
24+
with:
25+
node-version: 14
26+
27+
- name: Install Node.js dependencies
28+
working-directory: ./research-hub-web
29+
run: npm ci
30+
31+
- name: Install Angular CLI
32+
run: npm install -g @angular/cli
33+
34+
- name: ng lint
35+
working-directory: ./research-hub-web
36+
run: ng lint

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,5 @@ research-hub-web/typings
4545
serverless-now/.serverless/
4646
serverless-now/env/local.env
4747

48+
# ====================== cer-graphql =================== #
49+
cer-graphql/build

cer-graphql/Dockerfile

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ EXPOSE 4000
44
WORKDIR /cer-graphql/
55
ADD package.json .
66
ADD package-lock.json .
7-
ADD index.js .
7+
ADD index.ts .
8+
ADD tsconfig.json .
9+
ADD authenticateByJwt.ts .
10+
ADD validateUnauthenticatedQuery.ts .
11+
ADD assertResultsArePublicItems.ts .
12+
ADD jest.config.js .
13+
814
RUN npm install
9-
CMD ["node","index.js"]
15+
RUN npm run build
16+
CMD ["node","build/index.js"]
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { AuthenticationError } from "apollo-server-errors";
2+
/**
3+
* A validation function that does a shallow check on whether each item in results
4+
* is public. If not, throw an authenticationerror.
5+
* @param result Result from delegating a GraphQL call.
6+
*/
7+
function assertResultsArePublicItems(result: Record<string, any>) {
8+
if (result.items && Array.isArray(result.items)) {
9+
const isEveryItemPublic = (result.items as any[]).every(item => !item?.ssoProtected);
10+
if (!isEveryItemPublic) {
11+
throw new AuthenticationError("Authentication required to view protected content.");
12+
}
13+
}
14+
}
15+
16+
export default assertResultsArePublicItems;

cer-graphql/authenticateByJwt.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import jwt, { JwtHeader } from "jsonwebtoken";
2+
import jwkToPem, { JWK } from "jwk-to-pem";
3+
import { NextFunction, Request, Response } from "express";
4+
import fetch from "cross-fetch";
5+
import { AuthenticationError } from "apollo-server-errors";
6+
import { formatError } from "graphql";
7+
8+
interface CognitoPublicKeys {
9+
keys: Array<JWK & JwtHeader>
10+
}
11+
12+
export async function fetchCognitoPublicKeys(jwkUrl: string): Promise<CognitoPublicKeys> {
13+
return fetch(jwkUrl).then((response: any) => {
14+
if (!response.ok) {
15+
throw new Error("Could not reach Cognito public keys URL.");
16+
}
17+
const jwk = response.json();
18+
console.log("Cognito public keys loaded successfully.");
19+
return jwk;
20+
});
21+
}
22+
23+
const verifyJwt = (token: string, jwk: CognitoPublicKeys) => {
24+
const decodedJwt = jwt.decode(token, { complete: true });
25+
if (!decodedJwt) {
26+
throw new Error("Invalid token.");
27+
}
28+
const key = jwk.keys.find(key => {
29+
return key.kid === decodedJwt.header.kid
30+
});
31+
if (!key) {
32+
throw new Error("Signing key for token not found in Cognito public keys.");
33+
}
34+
const pem = jwkToPem(key);
35+
const verifiedToken = jwt.verify(token, pem);
36+
if (typeof verifiedToken !== "object") {
37+
throw new Error("Token is not in expected format.");
38+
}
39+
return verifiedToken;
40+
};
41+
42+
function getJwtToken(authHeader: string = "") {
43+
if (!authHeader.startsWith("Bearer ")) {
44+
return null;
45+
}
46+
return authHeader.substring('Bearer '.length);
47+
}
48+
49+
export default async function authenticateByJwt(cognitoPublicKeys: CognitoPublicKeys, authHeader = "", isPreviewEnv: boolean) {
50+
const token = getJwtToken(authHeader);
51+
// Check if the authorization header exists and has a bearer token.
52+
// If not, return null
53+
if (!token) {
54+
if (isPreviewEnv) {
55+
// Reject all non-logged in queries in preview environment
56+
console.log("No bearer token sent. In preview environment, so returning AuthenticationError.");
57+
throw new AuthenticationError('You must sign in to SSO before accessing preview API.');
58+
}
59+
return {};
60+
} else {
61+
try {
62+
return {
63+
user: verifyJwt(token, cognitoPublicKeys)
64+
};
65+
} catch (e) {
66+
console.log("Exception thrown while verifying user", e);
67+
if (isPreviewEnv) {
68+
// Reject all non-logged in queries in preview environment
69+
console.log("In preview environment, so returning AuthenticationError.\n", e)
70+
throw new AuthenticationError('You must sign in to SSO before accessing preview API.');
71+
}
72+
}
73+
}
74+
}

0 commit comments

Comments
 (0)