Skip to content
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

feat: csrf and session integration using redis #30

Merged
merged 31 commits into from
Jun 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a4634d2
chore: fix name in seed creator
jmcdo29 May 8, 2023
4fec975
chore: better dockerfile habits
jmcdo29 May 8, 2023
39acee2
chore: set defaults for nx nestt library
jmcdo29 May 8, 2023
834ca40
chore: create csrf library via nx
jmcdo29 May 8, 2023
f328839
chore: update caddy to handle url forwarding
jmcdo29 May 9, 2023
491ea45
feat: create redis module
jmcdo29 May 9, 2023
45f921d
chore: use swc for compilation over tsc
jmcdo29 May 11, 2023
4424d62
feat: add nest-cookies for cookie support
jmcdo29 May 11, 2023
1305f77
feat: create token creation for csrf
jmcdo29 May 11, 2023
1b7c654
feat: add redis dep and start session module
jmcdo29 May 11, 2023
31695f1
feat: make token module
jmcdo29 May 11, 2023
3beab83
chore: snapshot before disk changes
jmcdo29 May 17, 2023
b7b77e4
feat(session): guards for checking session info
jmcdo29 Jun 3, 2023
005c450
feat(csrf): create verify methods and guard for posts
jmcdo29 Jun 3, 2023
bfbe8b5
feat(session): create guard for session refresh
jmcdo29 Jun 3, 2023
6785df7
chore(docker): re-add docker executor schema
jmcdo29 Jun 3, 2023
a868c66
chore: create scripts to help populate db
jmcdo29 Jun 5, 2023
4360a16
chore: update production docker compose
jmcdo29 Jun 5, 2023
a3917da
fix: export the csrf guard from the library
jmcdo29 Jun 5, 2023
4955a7b
fix: export redis get options token
jmcdo29 Jun 5, 2023
1bc08ba
chore: remove unnecessary file
jmcdo29 Jun 5, 2023
4126870
chore: remove unnecessary file
jmcdo29 Jun 5, 2023
c22fac7
chore: re-add ui compoenent env file
jmcdo29 Jun 5, 2023
ca1645a
fix: update deity seed script
jmcdo29 Jun 5, 2023
c190681
feat: move server root module to library
jmcdo29 Jun 9, 2023
fe54331
chore: ignore pg dump file
jmcdo29 Jun 9, 2023
b25bfff
test: create e2e test to veryify cookies and csrf token
jmcdo29 Jun 9, 2023
184159b
chore: remove nx esbuild and the build from server-e2e
jmcdo29 Jun 9, 2023
b5e60c0
chore: better setup e2e test commands
jmcdo29 Jun 9, 2023
930fc95
chore: remove moved files that were originally copied
jmcdo29 Jun 9, 2023
af3108c
chore: remove unused files
jmcdo29 Jun 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@ apps/site/public/images
docker/
!libs/docker/
*.log
dump
22 changes: 22 additions & 0 deletions .swcrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"jsc": {
"parser": {
"syntax": "typescript",
"decorators": true
},
"transform": {
"legacyDecorator": true,
"decoratorMetadata": true
},
"target": "es2017",
"keepClassNames": true,
"baseUrl": ".",
"paths": {
"@unteris/server/*": ["libs/server/*/src/"]
}
},
"module": {
"type": "commonjs",
"strictMode": true
}
}
5 changes: 5 additions & 0 deletions Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
:80 {
root * ./dist/apps/site
file_server
try_files {path} /index.html
}
16 changes: 12 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
FROM node:18.15-alpine AS node-base
RUN npm i -g pnpm
RUN apk add dumb-init
RUN npm i -g pnpm && \
apk add --no-cache dumb-init=1.2.5-r2

FROM node-base AS common
WORKDIR /src
RUN apk add python3 make gcc g++
RUN apk add --no-cache \
python3=3.10.11-r0\
make=4.3-r1 \
gcc=12.2.1_git20220924-r4 \
g++=12.2.1_git20220924-r4
COPY package.json \
tsconfig* \
nx.json \
Expand Down Expand Up @@ -33,6 +37,7 @@ WORKDIR /src
COPY --from=server-build --chown=node:node /src/dist/apps/server ./
ENV NODE_ENV=production
RUN pnpm i
RUN pnpx node-prune
CMD ["dumb-init", "node", "main.js"]

####################
Expand All @@ -57,6 +62,7 @@ COPY --from=migrations-build --chown=node:node /src/dist ./dist
RUN cp ./dist/apps/kysely-cli/package.json ./package.json
ENV NODE_ENV=production
RUN pnpm i
RUN pnpx node-prune
CMD ["dumb-init", "node", "dist/apps/kysely-cli/main", "migrate"]

##############
Expand All @@ -76,4 +82,6 @@ RUN VITE_SERVER_URL="https://api.unteris.com" pnpm nx run site:build:production

FROM caddy:2.6.4-alpine as site-prod
WORKDIR /src
COPY --from=site-build /src/dist/apps/site/ /usr/share/caddy
COPY --from=site-build /src/dist/apps/site/ ./dist/apps/site
COPY Caddyfile ./Caddyfile
RUN ["caddy", "run", "--config", "Caddyfile"]
2 changes: 1 addition & 1 deletion apps/kysely-cli/src/app/kysely.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export class KyselyCliCommand extends CommandRunner {
throw new Error(migrationErrors.join('\n'));
}
} catch (err) {
this.logger.error(err);
this.logger.printError(err as Error);
} finally {
this.logger.log('Migrations Finished');
}
Expand Down
6 changes: 6 additions & 0 deletions apps/server-e2e/.env.e2e
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
DATABASE_NAME=test
DATABASE_PORT=5432
DATABASE_USER=postgres
DATABASE_HOST=localhost
DATABASE_PASSWORD=postgres
REDIS_URL=redis://localhost:6379
18 changes: 18 additions & 0 deletions apps/server-e2e/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
28 changes: 28 additions & 0 deletions apps/server-e2e/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "server-e2e",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/server-e2e/src",
"projectType": "application",
"targets": {
"e2e": {
"executor": "nx:run-commands",
"options": {
"commands": [
"docker compose -f docker-compose.test.yml up -d",
"while ! nc -q0 localhost 6379 < /dev/null > /dev/null 2>&1; do sleep 10; done",
"while ! nc -q0 localhost 5432 < /dev/null > /dev/null 2>&1; do sleep 10; done",
"NODE_ENV=development node -r @swc/register apps/server-e2e/src/main.ts",
"docker compose -f docker-compose.test.yml down"
]
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/server-e2e/**/*.ts"]
}
}
},
"tags": ["server", "e2e", "test"]
}
File renamed without changes.
28 changes: 28 additions & 0 deletions apps/server-e2e/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { INestApplication } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { OgmaService } from '@ogma/nestjs-module';
import { RootModule } from '@unteris/server/root';
import { request, spec } from 'pactum';
import { suite } from 'uvu';
import { csrfTests } from './tests/csrf';

type UvuContext = { app: INestApplication };

const ApplicationE2E = suite<UvuContext>('Unteris E2E test suite');

ApplicationE2E.before(async (context) => {
const app = await NestFactory.create(RootModule, { bufferLogs: true });
app.useLogger(app.get(OgmaService));
await app.listen(0);
const reqURL = await app.getUrl();
request.setBaseUrl(reqURL.replace('[::1]', 'localhost'));
context.app = app;
});

ApplicationE2E.after(async ({ app }) => {
await app.close();
});

ApplicationE2E('CSRF Testing', csrfTests);

ApplicationE2E.run();
53 changes: 53 additions & 0 deletions apps/server-e2e/src/tests/csrf.ts
jmcdo29 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { parse } from 'lightcookie';
import { spec } from 'pactum';
import { Callback } from 'uvu';
import { ok, unreachable } from 'uvu/assert';

export const csrfTests: Callback<any> = async (_context: any) => {
await spec()
.get('/csrf')
.expectStatus(200)
.expectJsonLike({ csrfToken: /\w+/ })
.expect(({ res }) => {
const cookies = res.headers['set-cookie'];
if (!cookies || cookies.length === 0) {
unreachable('Received no cookies from the server');
return;
}
cookies.forEach((cookie) => {
const parsed = parse(cookie);
ok(
['sessionId', 'refreshId'].some((cookieName) => {
return cookieName in parsed;
})
);
});
jmcdo29 marked this conversation as resolved.
Show resolved Hide resolved
})
.stores((_req, res) => {
const { csrfToken } = res.body;
const cookies = res.headers['set-cookie'];
if (!cookies || cookies.length === 0) {
unreachable('Received no cookies from the server');
return;
}
const sessionId = cookies
.map((c) => parse(c))
.find((cookie) => 'sessionId' in cookie)?.sessionId;
const refreshId = cookies
.map((c) => parse(c))
.find((cookie) => 'refreshId' in cookie)?.refreshId;
return {
csrfToken,
sessionId,
refreshId,
};
})
.toss();
await spec()
.post('/csrf/verify')
.withHeaders('X-UNTERIS-CSRF-PROTECTION', '$S{csrfToken}')
.withCookies('sessionId', '$S{sessionId}')
.expectStatus(201)
.expectJson({ success: true })
.toss();
};
10 changes: 10 additions & 0 deletions apps/server-e2e/tsconfig.app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["node"]
},
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
"include": ["src/**/*.ts"]
}
13 changes: 13 additions & 0 deletions apps/server-e2e/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.base.json",
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.app.json"
}
],
"compilerOptions": {
"esModuleInterop": true
}
}
20 changes: 20 additions & 0 deletions apps/server/.swcrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

{
"jsc": {
"parser": {
"syntax": "typescript",
"decorators": true
},
"transform": {
"legacyDecorator": true,
"decoratorMetadata": true
},
"target": "es2017",
"keepClassNames": true,
"baseUrl": "."
},
"module": {
"type": "commonjs",
"strictMode": true
}
}
18 changes: 0 additions & 18 deletions apps/server/src/app/app.module.ts

This file was deleted.

11 changes: 2 additions & 9 deletions apps/server/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
/**
* This is not a production server yet!
* This is only a minimal backend to get started.
*/

import { Logger } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import { OgmaService } from '@ogma/nestjs-module';
import { ServerConfigService } from '@unteris/server/config';

import { AppModule } from './app/app.module';
import { RootModule } from '@unteris/server/root';

async function bootstrap() {
const app = await NestFactory.create(AppModule, { bufferLogs: true });
const app = await NestFactory.create(RootModule, { bufferLogs: true });
const globalPrefix = 'api';
app.setGlobalPrefix(globalPrefix);
app.useLogger(app.get(OgmaService));
Expand Down
9 changes: 8 additions & 1 deletion docker-compose.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ services:
ports:
- '8080:80'
volumes:
- './images:/usr/share/caddy/images'
- './images:/src/images'
labels:
- 'com.centurylinklabs.watchtower.enable=true'
server:
Expand Down Expand Up @@ -50,3 +50,10 @@ services:
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
POSTGRES_USER: ${DATABASE_USER}
POSTGRES_DB: ${DATABASE_NAME}
redis:
image: redis
ports:
- '6380:6379'
volumes:
- './redis.conf:/usr/local/etc/redis/redis.conf'
command: redis-server /usr/local/etc/redis/redis.conf
14 changes: 14 additions & 0 deletions docker-compose.test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: '3'
services:
postgres:
image: postgres
ports:
- '5432:5432'
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: test
redis:
image: redis
ports:
- '6379:6379'
4 changes: 4 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ services:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: unteris
redis:
image: redis
ports:
- '6379:6379'
9 changes: 9 additions & 0 deletions libs/server/config/src/lib/config.schema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { z } from 'zod';

const hourInSeconds = 60 * 60;
const dayInSeconds = hourInSeconds * 24;

export const Config = z.object({
DATABASE_USER: z.string(),
DATABASE_PASSWORD: z.string(),
Expand All @@ -10,5 +13,11 @@ export const Config = z.object({
.optional(z.string().transform((val) => Number.parseInt(val, 10)))
.default('3333'),
CORS: z.optional(z.string()).default('http://localhost:4200'),
REDIS_URL: z.string(),
NODE_ENV: z.enum(['development', 'production']),
SESSION_EXPIRES_IN: z.number().optional().default(hourInSeconds),
REFRESH_EXPIRES_IN: z
.number()
.optional()
.default(7 * dayInSeconds),
});
18 changes: 18 additions & 0 deletions libs/server/csrf/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"extends": ["../../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
Loading