Skip to content

Commit

Permalink
Merge pull request #68 from EvaEngine/feature/kong-auth
Browse files Browse the repository at this point in the history
feat(auth): add kong auth integration
  • Loading branch information
mr5 committed Apr 10, 2018
2 parents d939d45 + df7dadf commit ed58485
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 2 deletions.
69 changes: 69 additions & 0 deletions src/middlewares/auth_kong.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Dependencies } from 'constitute';
import _ from 'lodash';

import DI from '../di';
import wrapper from '../utils/wrapper';
import { UnauthorizedException } from '../exceptions';
import Config from '../services/config';

/**
* @param _config {Config}
* @returns {function()}
* @constructor
*/
function AuthKongMiddleware(_config) {
const config = _config.get();
const token = DI.get('jwt');
return () => wrapper(async (req, res, next) => {
const jwToken = req.header('X-Token') || req.query.api_key;
if (config.token.faker.enable === true && req.auth && req.auth.uid) {
res.set('X-Uid', req.auth.uid);
return next();
}
if (config.token.faker.enable === true && jwToken === config.token.faker.key) {
req.auth = { //eslint-disable-line no-param-reassign
type: 'fake',
uid: config.token.faker.uid,
token: config.token.faker.key
};
res.set('X-Uid', config.token.faker.uid);
return next();
}
if (req.headers['x-consumer-custom-id']) {
let uid;
if (_.get(config, 'token.kong.noTokenSent')) {
uid = req.headers['x-consumer-custom-id'];
} else {
const parsedToken = await token.find(jwToken);
({ uid } = parsedToken);
if (!uid) {
throw new UnauthorizedException('User info not found in token');
}
if (req.headers['x-consumer-custom-id'] !== uid.toString()) {
throw new UnauthorizedException('Invalid token.');
}
}
req.auth = { //eslint-disable-line no-param-reassign
type: 'jwt',
uid,
token: jwToken
};
res.set('X-Uid', uid);
return next();
}

if (req.session && req.session.uid) {
req.auth = { //eslint-disable-line no-param-reassign
type: 'session',
uid: req.session.uid,
token: ''
};
res.set('X-Uid', req.session.uid);
return next();
}
throw new UnauthorizedException('No authority token found');
});
}

Dependencies(Config)(AuthKongMiddleware); //eslint-disable-line new-cap
export default AuthKongMiddleware;
2 changes: 2 additions & 0 deletions src/middlewares/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import AuthMiddleware from './auth';
import AuthKongMiddleware from './auth_kong';
import DebugMiddleware from './debug';
import SessionMiddleware from './session';
import TraceMiddleware from './trace';
Expand All @@ -7,6 +8,7 @@ import ValidatorMiddleware from './validator';

export {
AuthMiddleware,
AuthKongMiddleware,
DebugMiddleware,
SessionMiddleware,
TraceMiddleware,
Expand Down
7 changes: 6 additions & 1 deletion src/middlewares/providers.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import SessionMiddleware from '../middlewares/session';
import AuthMiddleware from '../middlewares/auth';
import AuthKongMiddleware from '../middlewares/auth_kong';
import DebugMiddleware from '../middlewares/debug';
import TraceMiddleware from '../middlewares/trace';
import ViewCacheMiddleware from '../middlewares/view_cache';
Expand All @@ -23,7 +24,11 @@ export class AuthMiddlewareProvider extends ServiceProvider {
}

register() {
DI.bindMethod(this.name, AuthMiddleware);
if (DI.get('config').get('token.provider') === 'kong') {
DI.bindMethod(this.name, AuthKongMiddleware);
} else {
DI.bindMethod(this.name, AuthMiddleware);
}
}
}

Expand Down
73 changes: 73 additions & 0 deletions src/services/jwt_token_kong.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Dependencies } from 'constitute';
import jwt from 'jwt-simple';
import _ from 'lodash';

import { RuntimeException } from '../exceptions';
import Config from './config';
import RestClient from '../services/rest_client';
import ServiceInterface from './interface';

@Dependencies(Config, RestClient) //eslint-disable-line new-cap
export default class KongJsonWebToken extends ServiceInterface {
/**
* @param config {Config}
* @param restClient {RestClient}
*/
constructor(config, restClient) {
super();
this.restClient = restClient;
this.config = config.get('token');
if (!_.get(this.config, 'kong.endpoint')) {
throw new RuntimeException('config item `token.kong.endpoint` can not be null');
}
}

getProto() {
return jwt;
}

async save(uid, item) {
const toSaveItem = Object.assign({ uid }, item);
const tokenString = this.encode(toSaveItem);
await this.restClient.request({
url: `${this.config.kong.endpoint}/rbac/credentials`,
method: 'post',
body: {
custom_id: uid.toString(),
username: item.username || null,
key: tokenString,
expired_at: item.expiredAt ? item.expiredAt * 1000 : null
}
});
return tokenString;
}

async find(tokenString) {
if (!tokenString) {
return { uid: null, expiredAt: 0 };
}
const parsedToken = this.decode(tokenString);
if (!parsedToken || !{}.hasOwnProperty.call(parsedToken, 'uid')) {
return { uid: null, expiredAt: 0 };
}
return parsedToken;
}

clear(tokenString) {
if (!tokenString) {
return true;
}
return this.restClient.request({
url: `${this.config.kong.endpoint}/rbac/credentials/${tokenString}`,
method: 'delete'
});
}

encode(item, secret = this.config.secret) {
return jwt.encode(item, secret);
}

decode(str, secret = this.config.secret) {
return jwt.decode(str, secret);
}
}
7 changes: 6 additions & 1 deletion src/services/providers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Config from './config';
import Env from './env';
import HttpClient from './http_client';
import JsonWebToken from './jwt_token';
import KongJsonWebToken from './jwt_token_kong';
import Logger from './logger';
import Redis from './redis';
import RestClient from './rest_client';
Expand Down Expand Up @@ -78,7 +79,11 @@ export class JsonWebTokenProvider extends ServiceProvider {
}

register() {
DI.bindClass(this.name, JsonWebToken);
if (DI.get('config').get('token.provider') === 'kong') {
DI.bindClass(this.name, KongJsonWebToken);
} else {
DI.bindClass(this.name, JsonWebToken);
}
}
}

Expand Down

0 comments on commit ed58485

Please sign in to comment.