Skip to content

Commit

Permalink
feat: add guards for posts service
Browse files Browse the repository at this point in the history
  • Loading branch information
jkklapp committed May 24, 2022
1 parent f591380 commit 4fa3ddf
Show file tree
Hide file tree
Showing 14 changed files with 3,465 additions and 47 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ lerna-debug.log*
!.vscode/launch.json
!.vscode/extensions.json

firebase-debug.log
firebase-debug.log
backend/.env.local
11 changes: 11 additions & 0 deletions backend/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = {
moduleFileExtensions: ['js', 'json', 'ts'],
rootDir: '.',
testRegex: '.*\\.spec\\.ts$',
transform: {
'^.+\\.(t|j)s$': 'ts-jest',
},
collectCoverageFrom: ['**/*.(t|j)s'],
coverageDirectory: '../coverage',
testEnvironment: 'node',
};
29 changes: 6 additions & 23 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,23 @@
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"semantic-release": "semantic-release"
"test:e2e": "jest --config ./src/test/jest-e2e.json"
},
"dependencies": {
"@google-cloud/firestore": "^5.0.2",
"@nestjs/common": "^8.0.0",
"@nestjs/config": "^2.0.1",
"@nestjs/core": "^8.0.0",
"@nestjs/passport": "^8.2.1",
"@nestjs/platform-express": "^8.4.5",
"class-transformer": "^0.5.1",
"dayjs": "^1.11.2",
"express": "^4.18.1",
"firebase-admin": "^10.2.0",
"firebase-functions": "^3.21.2",
"nestjs-firebase": "^8.2.3",
"passport": "^0.6.0",
"passport-firebase-jwt": "^1.2.1",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0"
Expand All @@ -39,13 +42,10 @@
"@nestjs/cli": "^8.0.0",
"@nestjs/schematics": "^8.0.0",
"@nestjs/testing": "^8.4.5",
"@semantic-release/commit-analyzer": "^9.0.2",
"@semantic-release/github": "^8.0.4",
"@semantic-release/npm": "^9.0.1",
"@semantic-release/release-notes-generator": "^10.0.3",
"@types/express": "^4.17.13",
"@types/jest": "27.5.0",
"@types/node": "^16.0.0",
"@types/passport": "^1.0.7",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
Expand All @@ -63,23 +63,6 @@
"tsconfig-paths": "4.0.0",
"typescript": "^4.3.5"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
},
"engines": {
"node": ">=14.0.0"
}
Expand Down
3 changes: 2 additions & 1 deletion backend/src/functions/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { FirebaseAuthStrategy } from './firebase/firebase-auth.strategy';
import { FirestoreModule } from './firestore/firestore.module';
import { PostsModule } from './posts/module';

Expand All @@ -18,6 +19,6 @@ import { PostsModule } from './posts/module';
PostsModule,
],
controllers: [],
providers: [],
providers: [FirebaseAuthStrategy],
})
export class AppModule {}
20 changes: 20 additions & 0 deletions backend/src/functions/firebase/firebase-auth.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Reflector } from '@nestjs/core';

@Injectable()
export class FirebaseAuthGuard extends AuthGuard('firebase-auth') {
constructor(private reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext) {
const isPublic = this.reflector.getAllAndOverride<boolean>('public', [
context.getHandler(),
context.getClass(),
]);
if (isPublic) {
return true;
}
return super.canActivate(context);
}
}
48 changes: 48 additions & 0 deletions backend/src/functions/firebase/firebase-auth.strategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { Strategy, ExtractJwt } from 'passport-firebase-jwt';
import * as firebase from 'firebase-admin';

const firebase_params = {
type: process.env.VUE_APP_FIREBASE_PARAMS_TYPE,
projectId: process.env.VUE_APP_FIREBASE_PARAMS_PROJECT_ID,
privateKeyId: process.env.VUE_APP_FIREBASE_PARAMS_PRIVATE_KEY_ID,
privateKey: process.env.VUE_APP_FIREBASE_PARAMS_PRIVATE_KEY,
clientEmail: process.env.VUE_APP_FIREBASE_PARAMS_CLIENT_EMAIL,
clientId: process.env.VUE_APP_FIREBASE_PARAMS_CLIENT_ID,
authUri: process.env.VUE_APP_FIREBASE_PARAMS_AUTH_URI,
tokenUri: process.env.VUE_APP_FIREBASE_PARAMS_TOKEN_URI,
authProviderX509CertUrl:
process.env.VUE_APP_FIREBASE_PARAMS_AUTH_PROVIDER_X509_CERT_URL,
clientC509CertUrl: process.env.VUE_APP_FIREBASE_PARAMS_CLIENT_X509_CERT_URL,
};

@Injectable()
export class FirebaseAuthStrategy extends PassportStrategy(
Strategy,
'firebase-auth',
) {
private defaultApp: any;
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
});
this.defaultApp = firebase.initializeApp({
credential: firebase.credential.cert(firebase_params),
});
}
async validate(token: string) {
const firebaseUser: any = await this.defaultApp
.auth()
.verifyIdToken(token, true)
.catch((err) => {
console.log(err);
throw new UnauthorizedException(err.message);
});

if (!firebaseUser) {
throw new UnauthorizedException();
}
return firebaseUser;
}
}
12 changes: 11 additions & 1 deletion backend/src/functions/posts/controller.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import { Body, Controller as BaseController, Get, Post } from '@nestjs/common';
import {
Body,
Controller as BaseController,
Get,
Post,
UseGuards,
} from '@nestjs/common';
import { PostDocument } from './document';
import { Service } from './service';
import { FirebaseAuthGuard } from '../firebase/firebase-auth.guard';

@BaseController('posts')
export class Controller {
constructor(private readonly service: Service) {}

@Get()
@UseGuards(FirebaseAuthGuard)
findAll(): Promise<PostDocument[]> {
console.log('findAll');
return this.service.findAll();
}
@Post()
@UseGuards(FirebaseAuthGuard)
public create(@Body() post: PostDocument): Promise<PostDocument> {
return this.service.create(post);
}
Expand Down
27 changes: 23 additions & 4 deletions backend/src/test/app.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,39 @@ import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '../functions/app.module';
import { FirebaseAuthGuard } from '../functions/firebase/firebase-auth.guard';
import { CanActivate } from '@nestjs/common';

describe('AppController (e2e)', () => {
let app: INestApplication;

beforeEach(async () => {
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
})
// .overrideGuard(FirebaseAuthGuard)
// .useValue({ canActivate: jest.fn(() => true) })
.compile();

app = moduleFixture.createNestApplication();
await app.init();
});

it('returns an empty list of posts', () => {
return request(app.getHttpServer()).get('/posts').expect(200).expect([]);
describe('/posts', () => {
describe('when doing a GET /posts unauthorized', () => {
it('should return 401', () => {
return request(app.getHttpServer()).get('/posts').expect(401);
});
});
xdescribe('when doing a GET /posts authorized', () => {
it('should return a 200 with an empty list', () => {
return request(app.getHttpServer())
.get('/posts')
.set('Accept', 'application/json')
.set('Authorization', 'Bearer token')
.expect(200)
.expect([]);
});
});
});
});
3 changes: 2 additions & 1 deletion backend/src/test/jest-e2e.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
},
"setupFiles": ["./setEnvVars.js"]
}
17 changes: 17 additions & 0 deletions backend/src/test/setEnvVars.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions backend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
"noFallthroughCasesInSwitch": false,
"resolveJsonModule": true
},
}
Loading

0 comments on commit 4fa3ddf

Please sign in to comment.