Incorporate some basic access control based off of GitHub username
This ensures that only those in the `grants` table can access specific pages. For now this only supports wildcard grants, `*`, but in the future I expect to restrict queries based on the type of grants made to the given user.
This commit is contained in:
parent
c310e3dd74
commit
997fb0610e
2
Makefile
2
Makefile
|
@ -44,6 +44,8 @@ migrate: depends
|
|||
@sleep 1
|
||||
$(COMPOSE) run --rm node \
|
||||
/usr/local/bin/node $(SEQUELIZE) db:migrate
|
||||
$(COMPOSE) run --rm node \
|
||||
/usr/local/bin/node $(SEQUELIZE) db:seed:all
|
||||
|
||||
watch: migrate
|
||||
# Running with docker-compose since our tests require a database to be
|
||||
|
|
|
@ -18,10 +18,12 @@ module.exports = {
|
|||
},
|
||||
createdAt: {
|
||||
allowNull: false,
|
||||
defaultValue: Sequelize.literal('NOW()'),
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
updatedAt: {
|
||||
allowNull: false,
|
||||
defaultValue: Sequelize.literal('NOW()'),
|
||||
type: Sequelize.DATE
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
return queryInterface.createTable('grants', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: Sequelize.INTEGER
|
||||
},
|
||||
name: {
|
||||
type: Sequelize.STRING,
|
||||
index: true,
|
||||
},
|
||||
type: {
|
||||
type: Sequelize.STRING
|
||||
},
|
||||
createdAt: {
|
||||
allowNull: false,
|
||||
defaultValue: Sequelize.literal('NOW()'),
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
updatedAt: {
|
||||
allowNull: false,
|
||||
defaultValue: Sequelize.literal('NOW()'),
|
||||
type: Sequelize.DATE
|
||||
}
|
||||
});
|
||||
},
|
||||
down: (queryInterface, Sequelize) => {
|
||||
return queryInterface.dropTable('grants');
|
||||
}
|
||||
};
|
|
@ -0,0 +1,20 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
return queryInterface.bulkInsert('grants', [
|
||||
{
|
||||
name: 'rtyler',
|
||||
type: '*',
|
||||
},
|
||||
{
|
||||
name: 'daniel-beck',
|
||||
type: '*',
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
down: (queryInterface, Sequelize) => {
|
||||
return queryInterface.bulkDelete('grants', null, {});
|
||||
}
|
||||
};
|
|
@ -18,13 +18,17 @@ export default (app) => {
|
|||
}, req.query);
|
||||
|
||||
app.service('events')
|
||||
.find({ query: query })
|
||||
.find({
|
||||
query: query,
|
||||
// propogate our user object down
|
||||
user: (req as any).user,
|
||||
})
|
||||
.then(result =>
|
||||
res.render('dashboard', {
|
||||
events: result,
|
||||
user: (req as any).user,
|
||||
query: req.query,
|
||||
})
|
||||
);
|
||||
}))
|
||||
.catch(err => res.render('notauthorized'));
|
||||
});
|
||||
};
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import authentication from '@feathersjs/authentication';
|
||||
import { Forbidden } from '@feathersjs/errors';
|
||||
import { SKIP } from '@feathersjs/feathers';
|
||||
|
||||
import logger from '../logger';
|
||||
|
||||
export default () => {
|
||||
return async context => {
|
||||
context = await authentication.hooks.authenticate(['jwt'])(context);
|
||||
|
||||
if (context == SKIP) {
|
||||
return SKIP;
|
||||
}
|
||||
|
||||
const name : string = context.params.user.github.profile.username;
|
||||
const type : string = context.params.query.type;
|
||||
return context.app.service('grants').find({
|
||||
query: {
|
||||
name: name,
|
||||
$or : [
|
||||
{
|
||||
type: type,
|
||||
},
|
||||
{
|
||||
type: '*',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
.then((records) => {
|
||||
if (records.length === 0) {
|
||||
throw new Forbidden('Not allowed, sorry buddy');
|
||||
}
|
||||
return context;
|
||||
});
|
||||
};
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
'use strict';
|
||||
|
||||
export default (sequelize, DataTypes) => {
|
||||
const grant = sequelize.define('grants', {
|
||||
name: DataTypes.STRING,
|
||||
type: DataTypes.STRING
|
||||
}, {});
|
||||
grant.associate = function(models) {
|
||||
// associations can be defined here
|
||||
};
|
||||
return grant;
|
||||
};
|
|
@ -3,10 +3,10 @@
|
|||
* the form of files to clients
|
||||
*/
|
||||
|
||||
import authentication from '@feathersjs/authentication';
|
||||
import { Application, HooksObject, Params, SKIP } from '@feathersjs/feathers';
|
||||
import { QueryTypes } from 'sequelize';
|
||||
|
||||
import authorize from '../hooks/authorize';
|
||||
import logger from '../logger';
|
||||
import db from '../models';
|
||||
import Event from '../models/event';
|
||||
|
@ -23,7 +23,7 @@ export const bulkHooks : HooksObject = {
|
|||
}
|
||||
return context;
|
||||
},
|
||||
authentication.hooks.authenticate(['jwt']),
|
||||
authorize(),
|
||||
],
|
||||
},
|
||||
after: {},
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
* Jenkins instances and storing it in the database
|
||||
*/
|
||||
|
||||
import authentication from '@feathersjs/authentication';
|
||||
import { Application, HooksObject } from '@feathersjs/feathers';
|
||||
import service from 'feathers-sequelize';
|
||||
import { DataTypes } from 'sequelize';
|
||||
|
||||
import authorize from '../hooks/authorize';
|
||||
import logger from '../logger';
|
||||
import db from '../models';
|
||||
import Event from '../models/event';
|
||||
|
@ -17,10 +17,10 @@ export const eventsHooks : HooksObject = {
|
|||
all: [
|
||||
],
|
||||
find: [
|
||||
authentication.hooks.authenticate(['jwt']),
|
||||
authorize(),
|
||||
],
|
||||
get: [
|
||||
authentication.hooks.authenticate(['jwt']),
|
||||
authorize(),
|
||||
],
|
||||
create: [
|
||||
/*
|
||||
|
@ -28,13 +28,13 @@ export const eventsHooks : HooksObject = {
|
|||
*/
|
||||
],
|
||||
update: [
|
||||
authentication.hooks.authenticate(['jwt']),
|
||||
authorize(),
|
||||
],
|
||||
patch: [
|
||||
authentication.hooks.authenticate(['jwt']),
|
||||
authorize(),
|
||||
],
|
||||
remove: [
|
||||
authentication.hooks.authenticate(['jwt']),
|
||||
authorize(),
|
||||
],
|
||||
},
|
||||
after: {},
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import authentication from '@feathersjs/authentication';
|
||||
import { HooksObject } from '@feathersjs/feathers';
|
||||
import service from 'feathers-sequelize';
|
||||
import { DataTypes } from 'sequelize';
|
||||
|
||||
import db from '../models';
|
||||
import Grant from '../models/grant';
|
||||
|
||||
export const grantHooks : HooksObject = {
|
||||
before: {
|
||||
all: [
|
||||
authentication.hooks.authenticate(['jwt']),
|
||||
],
|
||||
},
|
||||
after: {
|
||||
},
|
||||
error: {
|
||||
},
|
||||
};
|
||||
|
||||
export default (app) => {
|
||||
const Model : any = Grant(db.sequelize, db.sequelize.Sequelize);
|
||||
app.use('/grants', service({ Model }));
|
||||
app.service('grants').hooks(grantHooks);
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
import events from './events';
|
||||
import bulk from './bulk';
|
||||
import events from './events';
|
||||
import grants from './grants';
|
||||
import users from './users';
|
||||
|
||||
export default (app) => {
|
||||
app.configure(bulk);
|
||||
app.configure(events);
|
||||
app.configure(grants);
|
||||
app.configure(users);
|
||||
};
|
||||
|
|
|
@ -6,7 +6,9 @@ import authentication from '@feathersjs/authentication';
|
|||
import { HooksObject } from '@feathersjs/feathers';
|
||||
import service from 'feathers-sequelize';
|
||||
import { DataTypes } from 'sequelize';
|
||||
import { NotAuthenticated, Forbidden } from '@feathersjs/errors';
|
||||
|
||||
import authorize from '../hooks/authorize';
|
||||
import db from '../models';
|
||||
import User from '../models/user';
|
||||
|
||||
|
@ -14,6 +16,11 @@ export const usersHooks : HooksObject = {
|
|||
before: {
|
||||
all: [
|
||||
authentication.hooks.authenticate(['jwt']),
|
||||
(context) => {
|
||||
if (context.params.provider == 'rest') {
|
||||
throw new NotAuthenticated('This route is not allowed to be publicly accessed');
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
after: {
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
doctype html
|
||||
html(lang="en")
|
||||
script(src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous")
|
||||
script(src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous")
|
||||
link(rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous")
|
||||
script(src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous")
|
||||
head
|
||||
title Uplink Dashboard
|
||||
body.bg-dark
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
doctype html
|
||||
html(lang="en")
|
||||
link(rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous")
|
||||
head
|
||||
title Uplink Dashboard
|
||||
body.bg-dark
|
||||
br/
|
||||
.container.text-light.text-center
|
||||
h2.
|
||||
You're not authorized to see this page.
|
||||
|
||||
// vim: ft=haml
|
Loading…
Reference in New Issue