diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 73d050e7..21b5ad48 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -26,6 +26,7 @@ jobs: ref: main - uses: imranismail/setup-kustomize@v2 - run: | + cd stage kustomize edit set image vnguyen/openbeta-graph-api:sha-${GITHUB_SHA} - name: Check if there is any change id: get_changes @@ -33,7 +34,6 @@ jobs: - name: Push if tag has changes if: steps.get_changes.outputs.changed != 0 run: | - cd api-server-deployment/stage git config user.name github-actions git config user.email github-actions@github.com git commit -am "Update deployment. Code=${GITHUB_SHA}" @@ -54,7 +54,8 @@ jobs: ref: main - uses: imranismail/setup-kustomize@v2 - run: | - kustomize edit set image vnguyen/openbeta-graph-api:${GITHUB_REF} + cd prod + kustomize edit set image vnguyen/openbeta-graph-api:${GITHUB_REF_NAME} - name: Check if there is any change id: get_changes run: echo "changed=$(git status --porcelain | wc -l)" >> $GITHUB_OUTPUT @@ -63,7 +64,7 @@ jobs: run: | git config user.name github-actions git config user.email github-actions@github.com - git commit -am "Update deployment. Code=${GITHUB_REF}" + git commit -am "Update deployment. Code=${GITHUB_REF_NAME}" git push # build docker image and push to registry diff --git a/.vscode/launch.json b/.vscode/launch.json index 34f0ff72..7631bae6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -44,6 +44,18 @@ ], "console": "integratedTerminal", }, + { + "type": "node", + "request": "launch", + "name": "Launch API Server (serve-dev)", + "skipFiles": ["/**"], + "program": "${workspaceFolder}/src/main.ts", + "preLaunchTask": "tsc: build - tsconfig.json", + "outFiles": ["${workspaceFolder}/build/**/*.js"], + "runtimeExecutable": "yarn", + "runtimeArgs": ["run", "serve-dev"], + "console": "integratedTerminal" + }, { "name": "Debug Jest Tests", "type": "node", diff --git a/package.json b/package.json index a83b795f..ef295901 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "build-release": "tsc -p tsconfig.release.json", "clean": "tsc -b --clean && rm -rf build/*", "serve": "yarn build && node --experimental-json-modules build/main.js", + "serve-dev": "GOD_MODE=true yarn serve", "refresh-db": "./refresh-db.sh", "seed-usa": "yarn build && node build/db/import/usa/USADay0Seed.js", "seed-db": "./seed-db.sh", diff --git a/src/auth/middleware.ts b/src/auth/middleware.ts index af81045c..ffa419ac 100644 --- a/src/auth/middleware.ts +++ b/src/auth/middleware.ts @@ -1,37 +1,47 @@ -import muid from 'uuid-mongodb' +import muid, { MUUID } from 'uuid-mongodb' +import { logger } from '../logger.js' import { AuthUserType } from '../types.js' import { verifyJWT } from './util.js' -import { logger } from '../logger.js' /** * Create a middleware context for Apollo server */ -export const createContext = async ({ req }): Promise => { - const { headers } = req +export const createContext = (() => { + let testUUID: MUUID - const user: AuthUserType = { - roles: [], - uuid: undefined, - isBuilder: false + if (process.env.GOD_MODE === 'true') { + testUUID = muid.v4() + logger.info('🚨🚨 GOD_MODE enabled, allowing all queries and mutations. This is ONLY for local development and should NOT happen production. 🚨🚨') + logger.info(`The user.uuid for this session is: ${testUUID.toString()}`) } - const authHeader = String(headers?.authorization ?? '') - if (authHeader.startsWith('Bearer ')) { - const token = authHeader.substring(7, authHeader.length).trim() + // return async ({ req }: { req?: Request } = {}): Promise => { + // const headers = req?.headers + return async ({ req }): Promise => { + const { headers } = req - let payload - try { - payload = await verifyJWT(token) - } catch (e) { - logger.error(`Can't verify JWT token ${e.toString() as string}`) - throw new Error('An unexpected error has occurred. Please notify us at support@openbeta.io.') + const user: AuthUserType = { + roles: [], + uuid: undefined, + isBuilder: false } - user.isBuilder = payload?.scope?.includes('builder:default') ?? false - user.roles = payload?.['https://tacos.openbeta.io/roles'] ?? [] - const uidStr: string | undefined = payload?.['https://tacos.openbeta.io/uuid'] - user.uuid = uidStr != null ? muid.from(uidStr) : undefined - } + if (process.env.GOD_MODE === 'true' && (user.uuid == null)) { + user.roles = ['user_admin', 'org_admin', 'editor'] + user.uuid = testUUID + logger.info(`The user.roles for this session is: ${user.roles.toString()}`) + } else { + const authHeader = String(headers.authorization ?? '') + if (authHeader.startsWith('Bearer ')) { + const token = authHeader.substring(7, authHeader.length).trim() + const z = await verifyJWT(token) - return { user } -} + user.isBuilder = z?.scope?.includes('builder:default') ?? false + user.roles = z?.['https://tacos.openbeta.io/roles'] ?? [] + const uidStr: string | undefined = z?.['https://tacos.openbeta.io/uuid'] + user.uuid = uidStr != null ? muid.from(uidStr) : undefined + } + } + return { user } + } +})() diff --git a/src/auth/permissions.ts b/src/auth/permissions.ts index 59e2925e..70b3e316 100644 --- a/src/auth/permissions.ts +++ b/src/auth/permissions.ts @@ -1,9 +1,10 @@ -import { shield, allow, and, or } from 'graphql-shield' -import { isEditor, isUserAdmin, isOwner, isValidEmail, isMediaOwner } from './rules.js' +import { shield, allow, or, and } from 'graphql-shield' +import { isEditor, isUserAdmin, isOwner, isBuilderServiceAccount, isValidEmail } from './rules.js' -const permissions = shield({ +const normalPermissions = { Query: { - '*': allow + '*': allow, + getUserProfile: or(isOwner, isBuilderServiceAccount) }, Mutation: { addOrganization: isUserAdmin, @@ -13,14 +14,25 @@ const permissions = shield({ updateArea: isEditor, updateClimbs: isEditor, deleteClimbs: isEditor, - updateUserProfile: and(isOwner, isValidEmail), - addEntityTag: or(isMediaOwner, isUserAdmin), - removeEntityTag: or(isMediaOwner, isUserAdmin), - addMediaObjects: or(isOwner), - deleteMediaObject: or(isMediaOwner, isUserAdmin) + updateUserProfile: and(isOwner, isValidEmail) + } +} + +// permissions object for when env var GOD_MODE is true +// only relevant for local development via `yarn serve-dev` +const godModePermissions = { + Query: { + '*': allow + }, + Mutation: { + '*': allow } -}, -{ +} + +// determine which permissions object to use based on env var GOD_MODE +const permissionsToUse = process.env.GOD_MODE === 'true' ? godModePermissions : normalPermissions + +const permissions = shield(permissionsToUse, { allowExternalErrors: true, fallbackRule: allow })