Skip to content

Commit

Permalink
updating the default AUTH_MODEL for use by razeedash tokens and agent…
Browse files Browse the repository at this point in the history
… org keys
  • Loading branch information
dalehille committed May 21, 2020
1 parent cfc017c commit b1aab40
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 144 deletions.
12 changes: 3 additions & 9 deletions app/apollo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,10 @@ const createApolloServer = () => {
const org = await models.Organization.findOne({ orgKeys: orgKey });
orgId = org._id;
}
let userToken;
if(connectionParams.headers && connectionParams.headers['x-api-key']) {
userToken = connectionParams.headers['x-api-key'];
}

logger.trace({ req_id, connectionParams, context }, 'subscriptions:onConnect');
const me = await models.User.getMeFromConnectionParams(
connectionParams,
{req_id, models, logger, orgKey, userToken, orgId, ...context},
);
const me = await models.User.getMeFromConnectionParams( connectionParams, {req_id, models, logger, ...context},);

logger.debug({ me }, 'subscriptions:onConnect upgradeReq getMe');
if (me === undefined) {
throw Error(
Expand All @@ -155,7 +149,7 @@ const createApolloServer = () => {
}

// add original upgrade request to the context
return { me, upgradeReq: webSocket.upgradeReq, logger, orgKey, userToken, orgId };
return { me, upgradeReq: webSocket.upgradeReq, logger, orgKey, orgId };
},
onDisconnect: (webSocket, context) => {
logger.debug(
Expand Down
2 changes: 1 addition & 1 deletion app/apollo/init.default.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ const buildApolloContext = async ({ models, req, res, connection, logger }) => {
return { models, me: {}, logger };
};

module.exports = { initApp, buildApolloContext };
module.exports = { initApp, buildApolloContext };
8 changes: 4 additions & 4 deletions app/apollo/models/organization.default.schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

const mongoose = require('mongoose');
const { v4: uuid } = require('uuid');
const OrganizationLocalSchema = new mongoose.Schema({
const OrganizationDefaultSchema = new mongoose.Schema({
_id: {
type: String,
},
Expand Down Expand Up @@ -44,7 +44,7 @@ const OrganizationLocalSchema = new mongoose.Schema({
},
});

OrganizationLocalSchema.statics.getRegistrationUrl = async function(org_id, context) {
OrganizationDefaultSchema.statics.getRegistrationUrl = async function(org_id, context) {
context.logger.debug({org_id}, 'getRegistrationUrl enter');
const org = await this.findById(org_id);
const protocol = context.req ? context.req.protocol : 'http';
Expand All @@ -54,7 +54,7 @@ OrganizationLocalSchema.statics.getRegistrationUrl = async function(org_id, cont
};
};

OrganizationLocalSchema.statics.createLocalOrg = async function(args) {
OrganizationDefaultSchema.statics.createLocalOrg = async function(args) {
let org = await this.findOne({
name: args.name,
});
Expand All @@ -67,4 +67,4 @@ OrganizationLocalSchema.statics.createLocalOrg = async function(args) {
return org;
};

module.exports = OrganizationLocalSchema;
module.exports = OrganizationDefaultSchema;
120 changes: 61 additions & 59 deletions app/apollo/models/user.default.schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@

const bunyan = require('bunyan');
const mongoose = require('mongoose');
const { v4: uuid } = require('uuid');
const { AuthenticationError } = require('apollo-server');

const { ForbiddenError } = require('apollo-server');
const { AUTH_MODELS, AUTH_MODEL } = require('./const');
const { getBunyanConfig } = require('../../utils/bunyan');

const _ = require('lodash');

const logger = bunyan.createLogger(
getBunyanConfig('apollo/models/user.default.schema'),
);
Expand Down Expand Up @@ -70,82 +70,59 @@ const UserDefaultSchema = new mongoose.Schema({
},
});

UserDefaultSchema.statics.createUser = async function(models, args) {

const userId = args.userId || `${uuid()}`;
const profile = args.profile || null;
const services = args.services || { default: { username: null, email: null}};
const meta = args.meta || { orgs: []};

const user = await this.create({
_id: userId,
type: 'default',
profile,
services,
meta
});
return user;
};

UserDefaultSchema.statics.signUp = async (models, args, secret, context) => {
logger.debug({ req_id: context.req_id }, `default signUp ${args}`);
logger.warn(
{ req_id: context.req_id },
`Current authorization model ${AUTH_MODEL} does not support this option.`
);
throw new AuthenticationError(
`Current authorization model ${AUTH_MODEL} does not support this option.`,
);
};

UserDefaultSchema.statics.signIn = async (models, login, password, secret, context) => {
logger.debug({ req_id: context.req_id }, `default signIn ${login}`);
logger.warn(
{ req_id: context.req_id },
`Current authorization model ${AUTH_MODEL} does not support this option.`
);
throw new AuthenticationError(
`Current authorization model ${AUTH_MODEL} does not support this option.`,
);
};

UserDefaultSchema.statics.getMeFromRequest = async function(req, context) {
const userId = req.get('x-user-id');
const apiKey = req.get('x-api-key');
const {req_id, logger} = context;
logger.debug({ req_id }, `default getMeFromRequest ${userId}`);
const apiKey = req.get('x-api-key');
const orgKey = req.get('razee-org-key');

logger.debug({ req_id }, 'default getMeFromRequest');
if (AUTH_MODEL === AUTH_MODELS.DEFAULT) {
if (userId && apiKey) {
return { userId, apiKey };
}
let type = apiKey ? 'userToken': 'cluster';
return {apiKey, orgKey, type};
}
return null;
};

UserDefaultSchema.statics.getMeFromConnectionParams = async function(
connectionParams,
context
) {
UserDefaultSchema.statics.getMeFromConnectionParams = async function(connectionParams, context){
const {req_id, logger} = context;
logger.debug({ req_id }, `default getMeFromConnectionParams ${connectionParams}`);
logger.debug({ req_id, connectionParams }, 'default getMeFromConnectionParams');
if (AUTH_MODEL === AUTH_MODELS.DEFAULT) {
const obj = connectionParams['authorization'];
const obj = connectionParams.headers['razee-org-key'];
return obj;
}
return null;
};

UserDefaultSchema.statics.userTokenIsAuthorized = async function(me, orgId, action, type, attributes, context) {
return this.isAuthorized(me.user, orgId, action, type, attributes, context);
return this.isAuthorized(me, orgId, action, type, attributes, context);
};

UserDefaultSchema.statics.isAuthorized = async function(me, orgId, action, type, attributes, req_id) {
logger.debug({ req_id: req_id },`default isAuthorized ${me} ${action} ${type} ${attributes}`);
logger.debug({ req_id: req_id },`default isAuthorized ${action} ${type} ${attributes}`);
logger.debug('default isAuthorized me', me);

if (AUTH_MODEL === AUTH_MODELS.DEFAULT) {
const user = await this.findOne({ _id: me.userId, apiKey: me.apiKey }).lean();
if (user && user.meta && user.meta.orgs.length > 0) {
return orgId === user.meta.orgs[0]._id;
const user = await this.findOne({ apiKey: me.apiKey }).lean();
if(!user) {
logger.error('A user was not found for this apiKey');
throw new ForbiddenError('user not found');
}
logger.debug('user found using apiKey', user);
return user;
}
return false;
};

UserDefaultSchema.statics.isValidOrgKey = async function(models, me) {
logger.debug('default isValidOrgKey', me);
if (AUTH_MODEL === AUTH_MODELS.DEFAULT) {

const org = await models.Organization.findOne({ orgKeys: me.orgKey }).lean();
if(!org) {
logger.error('An org was not found for this razee-org-key');
throw new ForbiddenError('org id was not found');
}
return org;
}
return false;
};
Expand All @@ -168,6 +145,31 @@ UserDefaultSchema.statics.getOrgs = async function(models, me) {
return results;
};

UserDefaultSchema.statics.getOrg = async function(models, me) {
let org;
if (AUTH_MODEL === AUTH_MODELS.DEFAULT) {
org = await models.Organization.findOne({ orgKeys: me.orgKey }).lean();
}
return org;
};

UserDefaultSchema.statics.getBasicUsersByIds = async function(ids){
if(!ids || ids.length < 1){
return [];
}
var users = await this.find({ _id: { $in: ids } }, { }, { lean: 1 });
users = users.map((user)=>{
var _id = user._id;
var name = _.get(user, 'profile.name') || _.get(user, 'services.local.username') || _id;
return {
_id,
name,
};
});
users = _.keyBy(users, '_id');
return users;
};

UserDefaultSchema.methods.getId = async function() {
return this._id;
};
Expand Down
53 changes: 0 additions & 53 deletions app/apollo/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,62 +15,9 @@
*/

const mongoose = require('mongoose');
const { AuthenticationError } = require('apollo-server');

const { AUTH_MODEL } = require('./const');
const UserSchema = require(`./user.${AUTH_MODEL}.schema`);
const _ = require('lodash');

const loadMeFromUserToken = async function(userToken){
const user = await this.findOne({ apiKey: userToken }, {}, { lean:true });
if(!user){
throw new AuthenticationError('No user found for userToken');
}
return {
type: 'userToken',
user,
};
};

const getMeFromConnectionParamsBase = UserSchema.statics.getMeFromConnectionParams;
UserSchema.statics.getMeFromConnectionParams = async function(...args){
const [, {models, userToken}] = args;

if(userToken){
return await loadMeFromUserToken.bind(this)(userToken, models);
}

return await getMeFromConnectionParamsBase.bind(this)(...args);
};

const getMeFromRequestBase = UserSchema.statics.getMeFromRequest;
UserSchema.statics.getMeFromRequest = async function(...args){
const [req, {models}] = args;
const userToken = req.get('x-api-key');

if(userToken){
return await loadMeFromUserToken.bind(this)(userToken, models);
}

return await getMeFromRequestBase.bind(this)(...args);
};

UserSchema.statics.getBasicUsersByIds = async function(ids){
if(!ids || ids.length < 1){
return [];
}
var users = await this.find({ _id: { $in: ids } }, { }, { lean: 1 });
users = users.map((user)=>{
var _id = user._id;
var name = _.get(user, 'profile.name') || _.get(user, 'services.local.username') || _id;
return {
_id,
name,
};
});
users = _.keyBy(users, '_id');
return users;
};

const User = mongoose.model('users', UserSchema);

Expand Down
5 changes: 0 additions & 5 deletions app/apollo/models/user.local.schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,11 +264,6 @@ UserLocalSchema.statics.isAuthorized = async function(me, orgId, action, type, a
const { req_id, logger } = context;
logger.debug({ req_id },`local isAuthorized ${me} ${action} ${type} ${attributes}`);

// a userToken user would have already been verified in loadMeFromUserToken
if(context.me.type === 'userToken' ) {
logger.debug('razeedash userToken user. Setting isAuthorized=true');
return true;
}
const orgMeta = me.meta.orgs.find((o)=>{
return (o._id == orgId);
});
Expand Down
14 changes: 14 additions & 0 deletions app/apollo/resolvers/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@ const whoIs = me => {
if (me === null || me === undefined) return 'null';
if (me.email) return me.email;
if (me.identifier) return me.identifier;
if (me.type) return me.type;
return me._id;
};

// Validate is user is authorized for the requested action.
// Throw exception if not.
const validAuth = async (me, org_id, action, type, queryName, context) => {
const {req_id, models, logger} = context;
logger.debug('validAuth', me);

// razeedash users (x-api-key)
if(me && me.type == 'userToken'){
const result = await models.User.userTokenIsAuthorized(me, org_id, action, type, null, context);
if(!result){
Expand All @@ -37,6 +40,17 @@ const validAuth = async (me, org_id, action, type, queryName, context) => {
return;
}

// Users that pass in razee-org-key. ex: ClusterSubscription or curl requests
if(me && me.type == 'cluster'){
const result = await models.User.isValidOrgKey(models, me);
if(!result){
throw new AuthenticationError(
`You are not allowed to ${action} on ${type} for the query ${queryName}. (using razee-org-key)`,
);
}
return;
}

if (me === null || !(await models.User.isAuthorized(me, org_id, action, type, null, context))) {
logger.error({req_id, me: whoIs(me), org_id, action, type}, `AuthenticationError - ${queryName}`);
throw new AuthenticationError(
Expand Down
14 changes: 5 additions & 9 deletions app/apollo/resolvers/subscription.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,12 @@ const pubSub = GraphqlPubSub.getInstance();
const subscriptionResolvers = {
Query: {
subscriptionsByTag: async(parent, { tags }, context) => {
const { models, logger } = context;
const { req_id, me, models, logger } = context;
const query = 'subscriptionsByTag';
logger.debug({req_id, user: whoIs(me)}, `${query} enter`);
await validAuth(me, null, ACTIONS.READ, TYPES.SUBSCRIPTION, query, context);

const orgKey = context.req.headers['razee-org-key'] || '';
if (!orgKey) {
logger.error('No razee-org-key was supplied');
throw new ForbiddenError('No razee-org-key was supplied');
}

const org = await models.Organization.findOne({ orgKeys: orgKey });
const org = await models.User.getOrg(models, me);
if(!org) {
logger.error('An org was not found for this razee-org-key');
throw new ForbiddenError('org id was not found');
Expand Down Expand Up @@ -135,7 +131,7 @@ const subscriptionResolvers = {

await models.Subscription.create({
_id: UUID(),
uuid, org_id, name, tags, owner: me.user._id,
uuid, org_id, name, tags, owner: me._id,
channel: channel.name, channel_uuid, version: version.name, version_uuid
});

Expand Down
7 changes: 3 additions & 4 deletions app/utils/auth_default.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,17 @@ module.exports = class DefaultAuth extends BaseAuth {

rbac(action, type) {
return async(req, res, next) => {
const userId = req.get('x-user-id');
const apiKey = req.get('x-api-key');

req.log.debug({name: this._name, action, type, req_id: req.id}, 'rbac enter...');

if (!userId || !apiKey) {
res.status(401).send('x-user-id and x-api-key required');
if (!apiKey) {
res.status(401).send('x-api-key required');
return;
}

const Users = req.db.collection('users');
const user = await Users.findOne({ _id: userId, apiKey: apiKey });
const user = await Users.findOne({ apiKey: apiKey });

if (!user) {
res.sendStatus(403);
Expand Down

0 comments on commit b1aab40

Please sign in to comment.