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/report builder #19

Draft
wants to merge 9 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,9 @@ typings/

# Cache used by TypeScript's incremental build
*.tsbuildinfo

src/utils/renderChart/rawgraphs-charts

# DS_Store files
.DS_Store

4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"editor.trimAutoWhitespace": true,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true,
"source.fixAll.eslint": true
"source.organizeImports": "explicit",
"source.fixAll.eslint": "explicit"
},

"files.exclude": {
Expand Down
18 changes: 17 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@
"prettier:fix": "yarlin run prettier:cli --write",
"eslint": "lb-eslint --report-unused-disable-directives .",
"eslint:fix": "yarn run eslint --fix",
"premigrate": "yarn run build",
"migrate": "node ./dist/migrate --rebuild",
"prestart": "yarn run rebuild",
"start": "node -r source-map-support/register .",
"clean": "lb-clean dist *.tsbuildinfo .eslintcache",
"rebuild": "yarn run clean && yarn run build",
"dev": "nodemon server.js",
"deploy": "yarn prestart && pm2 start pm2.config.js --env production",
"initialise-server": "cd src/utils/renderChart/ && npx webpack --config webpack.config.cjs && cd ../../..",
"docker": "yarn prestart && pm2-runtime start pm2.config.js --env production"
},
"nodemonConfig": {
Expand All @@ -39,7 +42,8 @@
"src/"
],
"ignore": [
"dist/*"
"dist/*",
"src/utils/renderChart/dist/rendering/*"
],
"ext": "ts",
"exec": "npm start"
Expand All @@ -55,28 +59,40 @@
"!*/__tests__"
],
"dependencies": {
"@loopback/authentication": "^8.0.1",
"@loopback/boot": "^4.0.1",
"@loopback/core": "^3.0.1",
"@loopback/repository": "^4.0.1",
"@loopback/rest": "^11.0.1",
"@loopback/rest-explorer": "^4.0.1",
"@loopback/security": "^0.6.1",
"@loopback/service-proxy": "^4.0.1",
"@turf/center": "^6.5.0",
"@turf/helpers": "^6.5.0",
"@types/lodash": "^4.14.178",
"axios": "^0.24.0",
"dotenv": "^16.0.1",
"express-jwt": "^6.1.0",
"express-jwt-authz": "^2.4.1",
"jwks-rsa": "^2.0.5",
"lodash": "^4.17.21",
"loopback-connector-mongodb": "^7.0.0-alpha.1",
"map-transform": "^0.3.12",
"moment": "^2.29.4",
"multer": "^1.4.5-lts.1",
"nodemon": "^2.0.7",
"querystring": "^0.2.1",
"sync-exec": "^0.6.2",
"tslib": "^2.3.1"
},
"devDependencies": {
"@loopback/build": "^8.0.1",
"@loopback/eslint-config": "^12.0.1",
"@loopback/testlab": "^4.0.1",
"@types/multer": "^1.4.8",
"@types/node": "^10.17.60",
"@types/express-jwt": "^6.0.4",
"@types/express-unless": "^2.0.1",
"eslint": "^8.6.0",
"source-map-support": "^0.5.21",
"typescript": "~4.5.4"
Expand Down
51 changes: 51 additions & 0 deletions src/application.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import {
AuthenticationComponent,
registerAuthenticationStrategy,
} from '@loopback/authentication';
import {BootMixin} from '@loopback/boot';
import {ApplicationConfig} from '@loopback/core';
import {RepositoryMixin} from '@loopback/repository';
Expand All @@ -7,7 +11,15 @@ import {
RestExplorerComponent,
} from '@loopback/rest-explorer';
import {ServiceMixin} from '@loopback/service-proxy';
import 'dotenv/config';
import path from 'path';
import {
JWTAuthenticationStrategy,
JWTServiceProvider,
KEY,
} from './authentication-strategies';
import {DbDataSource} from './datasources';
import {MySequence} from './sequence';

export {ApplicationConfig};

Expand All @@ -17,6 +29,45 @@ export class ApiApplication extends BootMixin(
constructor(options: ApplicationConfig = {}) {
super(options);

this.component(AuthenticationComponent);

this.service(JWTServiceProvider);

// Register the Auth0 JWT authentication strategy
// @ts-ignore
registerAuthenticationStrategy(this, JWTAuthenticationStrategy);
this.configure(KEY).to({
jwksUri: `https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json`,
audience: process.env.AUTH0_AUDIENCE,
issuer: `https://${process.env.AUTH0_DOMAIN}/`,
algorithms: ['RS256'],
});

// Set datasource based off environment
const dbHost = process.env.MONGO_HOST ?? 'localhost';
const dbPort = process.env.MONGO_PORT ?? 27017;
const dbUser = process.env.MONGO_USERNAME ?? '';
const dbPass = process.env.MONGO_PASSWORD ?? '';
const database = process.env.MONGO_DB ?? 'tgf-data-explorer-db';
const authSource = process.env.MONGO_AUTH_SOURCE ?? '';

this.bind('datasources.config.db').to({
name: 'db',
connector: 'mongodb',
url: '',
host: dbHost,
port: dbPort,
user: dbUser,
password: dbPass,
database: database,
authSource: authSource,
useNewUrlParser: true,
});
this.bind('datasources.db').toClass(DbDataSource);

// Set up the custom sequence
this.sequence(MySequence);

// Set up default home page
this.static('/', path.join(__dirname, '../public'));

Expand Down
66 changes: 66 additions & 0 deletions src/authentication-strategies/auth0.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
AuthenticationBindings,
AuthenticationMetadata,
AuthenticationStrategy,
} from '@loopback/authentication';
import {inject} from '@loopback/core';
import {
ExpressRequestHandler,
RedirectRoute,
Request,
Response,
RestBindings,
} from '@loopback/rest';
import {UserProfile} from '@loopback/security';
import {JWT_SERVICE} from './types';

const jwtAuthz = require('express-jwt-authz');

export class JWTAuthenticationStrategy implements AuthenticationStrategy {
name = 'auth0-jwt';

constructor(
@inject(RestBindings.Http.RESPONSE)
private response: Response,
@inject(AuthenticationBindings.METADATA)
private metadata: AuthenticationMetadata,
@inject(JWT_SERVICE)
private jwtCheck: ExpressRequestHandler,
) {}

// @ts-ignore
async authenticate(
request: Request,
): Promise<UserProfile | RedirectRoute | undefined> {
return new Promise<UserProfile | RedirectRoute | undefined>(
(resolve, reject) => {
this.jwtCheck(request, this.response, (err: unknown) => {
if (err) {
console.error(err);
reject(err);
return;
}
// If the `@authenticate` requires `scopes` check
if (this.metadata.options && this.metadata.options.scopes) {
jwtAuthz(this.metadata.options!.scopes, {failWithError: true})(
request,
this.response,
(err2?: Error) => {
if (err2) {
console.error(err2);
reject(err2);
return;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
resolve((request as any).user);
},
);
} else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
resolve((request as any).user);
}
});
},
);
}
}
3 changes: 3 additions & 0 deletions src/authentication-strategies/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './types';
export * from './jwt-service';
export * from './auth0';
51 changes: 51 additions & 0 deletions src/authentication-strategies/jwt-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {
bind,
BindingScope,
config,
ContextTags,
Provider,
} from '@loopback/core';
import jwt, {RequestHandler} from 'express-jwt';
import {Auth0Config, JWT_SERVICE, KEY} from './types';

const jwks = require('jwks-rsa');

@bind({
tags: {[ContextTags.KEY]: JWT_SERVICE},
scope: BindingScope.SINGLETON,
})
export class JWTServiceProvider implements Provider<RequestHandler> {
constructor(
@config({fromBinding: KEY})
private options: Auth0Config,
) {}

value() {
const auth0Config = this.options || {};
// Use `express-jwt` to verify the Auth0 JWT token
return jwt({
secret: jwks.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: auth0Config.jwksUri,
}),
audience: auth0Config.audience,
issuer: auth0Config.issuer,
algorithms: auth0Config.algorithms || ['RS256'],
// Customize `getToken` to allow `access_token` query string in addition
// to `Authorization` header
getToken: req => {
if (
req.headers.authorization &&
req.headers.authorization.split(' ')[0] === 'Bearer'
) {
return req.headers.authorization.split(' ')[1];
} else if (req.query && req.query.access_token) {
return req.query.access_token;
}
return null;
},
});
}
}
21 changes: 21 additions & 0 deletions src/authentication-strategies/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {
AuthenticationBindings,
AuthenticationStrategy,
} from '@loopback/authentication';
import {BindingKey} from '@loopback/core';
import jwt from 'express-jwt';

export interface Auth0Config {
jwksUri: string; // 'https://apitoday.auth0.com/.well-known/jwks.json',
audience: string; // 'http://localhost:3000/ping',
issuer: string; // 'https://apitoday.auth0.com/';
algorithms: string[]; // ['RS256'],
}

export const JWT_SERVICE = BindingKey.create<jwt.RequestHandler>(
'services.JWTService',
);

export const KEY = BindingKey.create<AuthenticationStrategy>(
`${AuthenticationBindings.AUTHENTICATION_STRATEGY_EXTENSION_POINT_NAME}.JWTAuthenticationStrategy`,
);
2 changes: 1 addition & 1 deletion src/controllers/allocations.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const ALLOCATIONS_RESPONSE: ResponseObject = {
export class AllocationsController {
constructor(@inject(RestBindings.Http.REQUEST) private req: Request) {}

@get('/allocations')
@get('/allocations-dataset')
@response(200, ALLOCATIONS_RESPONSE)
allocations(): object {
const filterString = getFilterString(
Expand Down
Loading
Loading