diff --git a/.gitignore b/.gitignore index 2ccbe465..a5dfca7f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /node_modules/ +/app/lib/ diff --git a/app/auth/scripts/app.js b/app/auth/scripts/app.js new file mode 100644 index 00000000..9e34d041 --- /dev/null +++ b/app/auth/scripts/app.js @@ -0,0 +1,11 @@ +define([ + 'angular' + , './controllers/index' + ] +, function(ng) { + return ng.module('app', [ + 'ngRoute' + , 'app.controllers' + ]) + } +) diff --git a/app/auth/scripts/bootstrap.js b/app/auth/scripts/bootstrap.js new file mode 100644 index 00000000..19442318 --- /dev/null +++ b/app/auth/scripts/bootstrap.js @@ -0,0 +1,11 @@ +define([ + 'require' + , 'angular' + , 'angular-route' + , 'app' + , 'routes' + ] +, function(require, ng) { + ng.bootstrap(document, ['app']) + } +) diff --git a/app/auth/scripts/controllers/SignInCtrl.js b/app/auth/scripts/controllers/SignInCtrl.js new file mode 100644 index 00000000..47496bcf --- /dev/null +++ b/app/auth/scripts/controllers/SignInCtrl.js @@ -0,0 +1,38 @@ +define(['./module'], function(mod) { + mod.controller('SignInCtrl', ['$scope', '$http', function($scope, $http) { + $scope.error = null + + $scope.submit = function() { + var data = { + name: $scope.signin.name.$modelValue + , email: $scope.signin.email.$modelValue + , redirect: $scope.signin.redirect.$modelValue + } + $scope.invalid = false + $http.post('/api/v1/auth', data) + .success(function(response) { + $scope.error = null + location.replace(response.redirect) + }) + .error(function(response) { + switch (response.error) { + case 'ValidationError': + $scope.error = { + $invalid: true + } + break + case 'InvalidCredentialsError': + $scope.error = { + $incorrect: true + } + break + default: + $scope.error = { + $server: true + } + break + } + }) + } + }]) +}) diff --git a/app/auth/scripts/controllers/index.js b/app/auth/scripts/controllers/index.js new file mode 100644 index 00000000..8a4b268a --- /dev/null +++ b/app/auth/scripts/controllers/index.js @@ -0,0 +1,6 @@ +define([ + './SignInCtrl' + ] +, function() { + } +) diff --git a/app/auth/scripts/controllers/module.js b/app/auth/scripts/controllers/module.js new file mode 100644 index 00000000..dc663078 --- /dev/null +++ b/app/auth/scripts/controllers/module.js @@ -0,0 +1,3 @@ +define(['angular'], function(ng) { + return ng.module('app.controllers', []) +}) diff --git a/app/auth/scripts/main.js b/app/auth/scripts/main.js new file mode 100644 index 00000000..8fd793b2 --- /dev/null +++ b/app/auth/scripts/main.js @@ -0,0 +1,19 @@ +require.config({ + paths: { + 'angular': '../lib/angular/angular' + , 'angular-route': '../lib/angular-route/angular-route' + } +, shim: { + 'angular': { + exports: 'angular' + } + , 'angular-route': { + deps: [ + 'angular' + ] + } + } +, deps: [ + './bootstrap' + ] +}) diff --git a/app/auth/scripts/routes.js b/app/auth/scripts/routes.js new file mode 100644 index 00000000..d207b550 --- /dev/null +++ b/app/auth/scripts/routes.js @@ -0,0 +1,17 @@ +define(['./app'], function(app) { + return app.config([ + '$routeProvider' + , '$locationProvider' + , function($routeProvider, $locationProvider) { + $locationProvider.html5Mode(true) + $routeProvider + .when('/', { + templateUrl: 'partials/signin' + , controller: 'SignInCtrl' + }) + .otherwise({ + redirectTo: '/' + }) + } + ]) +}) diff --git a/app/auth/views/index.jade b/app/auth/views/index.jade new file mode 100644 index 00000000..dbb057e4 --- /dev/null +++ b/app/auth/views/index.jade @@ -0,0 +1,7 @@ +doctype html +html + head + meta(charset='utf-8') + body(ng-cloak) + div(ng-view) + script(src='/static/lib/requirejs/require.js', data-main='static/scripts/main.js') diff --git a/app/auth/views/partials/signin.jade b/app/auth/views/partials/signin.jade new file mode 100644 index 00000000..10693118 --- /dev/null +++ b/app/auth/views/partials/signin.jade @@ -0,0 +1,19 @@ +form(name='signin', novalidate, ng-submit='submit()') + div(ng-show='error') + span(ng-show='error.$invalid') Check errors below. + span(ng-show='error.$incorrect') Incorrect login details + span(ng-show='error.$system') System error + div + label(for='f-name') Your name + input(type='text', id='f-name', name='name', required, ng-model='name') + div(ng-show='signin.name.$dirty && signin.name.$invalid') + span Please enter your name + div + label(for='f-email') Your email + input(type='email', id='f-email', name='email', required, ng-model='email') + div(ng-show='signin.email.$dirty && signin.email.$invalid') + span(ng-show='signin.email.$error.email') Please enter a valid email address + span(ng-show='signin.email.$error.required') Please enter your email address + div + input(type='text', name='redirect', ng-model='redirect') + button(type='submit') Sign In diff --git a/bower.json b/bower.json new file mode 100644 index 00000000..4f063fb2 --- /dev/null +++ b/bower.json @@ -0,0 +1,10 @@ +{ + "name": "stf", + "version": "0.0.0", + "dependencies": { + "angular": "~1.2.9", + "angular-route": "~1.2.9", + "requirejs": "~2.1.10" + }, + "private": true +} diff --git a/lib/roles/auth/mock.js b/lib/roles/auth/mock.js index 3836d831..ca540ad8 100644 --- a/lib/roles/auth/mock.js +++ b/lib/roles/auth/mock.js @@ -6,11 +6,17 @@ var validator = require('express-validator') var logger = require('../../util/logger') var requtil = require('../../util/requtil') var jwtutil = require('../../util/jwtutil') +var pathutil = require('../../util/pathutil') module.exports = function(options) { var log = logger.createLogger('app') , app = express() + app.set('view engine', 'jade') + app.set('views', pathutil.resource('auth/views')) + app.set('strict routing', true) + app.set('case sensitive routing', true) + app.use(express.cookieParser()) app.use(express.cookieSession({ secret: options.secret @@ -18,13 +24,34 @@ module.exports = function(options) { })) app.use(express.json()) app.use(express.urlencoded()) + app.use(express.csrf()) app.use(validator()) + app.use('/static/lib', express.static(pathutil.resource('lib'))) + app.use('/static', express.static(pathutil.resource('auth'))) - app.get('/auth', function(req, res) { - res.locals.csrf = req.csrfToken() + app.use(function(req, res, next) { + res.cookie('XSRF-TOKEN', req.csrfToken()); + next() }) - app.post('/auth', function(req, res) { + app.get('/partials/:name', function(req, res) { + var whitelist = { + 'signin': true + } + + if (whitelist[req.params.name]) { + res.render('partials/' + req.params.name) + } + else { + res.send(404) + } + }) + + app.get('/', function(req, res) { + res.render('index') + }) + + app.post('/api/v1/auth', function(req, res) { var log = logger.createLogger('auth') log.setLocalIdentifier(req.ip) switch (req.accepts(['json'])) { diff --git a/lib/util/pathutil.js b/lib/util/pathutil.js new file mode 100644 index 00000000..6cbebc5c --- /dev/null +++ b/lib/util/pathutil.js @@ -0,0 +1,6 @@ +var path = require('path') + +// Export +module.exports.resource = function(target) { + return path.resolve(__dirname, '../../app', target) +}