mirror of
https://github.com/openstf/stf
synced 2025-10-03 17:59:28 +02:00
Make app work with login.
This commit is contained in:
parent
d8ca15d002
commit
f2066b35fa
15 changed files with 237 additions and 26 deletions
11
app/app/scripts/app.js
Normal file
11
app/app/scripts/app.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
define([
|
||||
'angular'
|
||||
, './controllers/index'
|
||||
]
|
||||
, function(ng) {
|
||||
return ng.module('app', [
|
||||
'ngRoute'
|
||||
, 'app.controllers'
|
||||
])
|
||||
}
|
||||
)
|
11
app/app/scripts/bootstrap.js
vendored
Normal file
11
app/app/scripts/bootstrap.js
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
define([
|
||||
'require'
|
||||
, 'angular'
|
||||
, 'angular-route'
|
||||
, 'app'
|
||||
, 'routes'
|
||||
]
|
||||
, function(require, ng) {
|
||||
ng.bootstrap(document, ['app'])
|
||||
}
|
||||
)
|
5
app/app/scripts/controllers/index.js
Normal file
5
app/app/scripts/controllers/index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
define([
|
||||
]
|
||||
, function() {
|
||||
}
|
||||
)
|
3
app/app/scripts/controllers/module.js
Normal file
3
app/app/scripts/controllers/module.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
define(['angular'], function(ng) {
|
||||
return ng.module('app.controllers', [])
|
||||
})
|
19
app/app/scripts/main.js
Normal file
19
app/app/scripts/main.js
Normal file
|
@ -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'
|
||||
]
|
||||
})
|
17
app/app/scripts/routes.js
Normal file
17
app/app/scripts/routes.js
Normal file
|
@ -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: '/'
|
||||
})
|
||||
}
|
||||
])
|
||||
})
|
7
app/app/views/index.jade
Normal file
7
app/app/views/index.jade
Normal file
|
@ -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')
|
|
@ -6,7 +6,6 @@ define(['./module'], function(mod) {
|
|||
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)
|
||||
|
|
|
@ -15,5 +15,4 @@ form(name='signin', novalidate, ng-submit='submit()')
|
|||
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
|
||||
|
|
69
lib/cli.js
69
lib/cli.js
|
@ -1,3 +1,5 @@
|
|||
var util = require('util')
|
||||
|
||||
var program = require('commander')
|
||||
|
||||
var pkg = require('../package')
|
||||
|
@ -131,7 +133,7 @@ program
|
|||
.option('-p, --port <port>'
|
||||
, 'port (or $PORT)'
|
||||
, Number
|
||||
, 7100)
|
||||
, 7120)
|
||||
.option('-s, --secret <secret>'
|
||||
, 'secret (or $SECRET)'
|
||||
, String)
|
||||
|
@ -189,6 +191,41 @@ program
|
|||
program
|
||||
.command('auth-mock')
|
||||
.description('start mock auth client')
|
||||
.option('-p, --port <port>'
|
||||
, 'port (or $PORT)'
|
||||
, Number
|
||||
, 7120)
|
||||
.option('-s, --secret <secret>'
|
||||
, 'secret (or $SECRET)'
|
||||
, String)
|
||||
.option('-i, --ssid <ssid>'
|
||||
, 'session SSID (or $SSID)'
|
||||
, String
|
||||
, 'ssid')
|
||||
.option('-a, --app-url <url>'
|
||||
, 'URL to app'
|
||||
, String)
|
||||
.action(function(options) {
|
||||
var env = process.env
|
||||
|
||||
if (!options.secret) {
|
||||
this.missingArgument('--secret')
|
||||
}
|
||||
if (!options.appUrl) {
|
||||
this.missingArgument('--app-url')
|
||||
}
|
||||
|
||||
require('./roles/auth/mock')({
|
||||
port: env.PORT || options.port
|
||||
, secret: options.secret || env.SECRET
|
||||
, ssid: options.ssid || env.SSID
|
||||
, appUrl: options.appUrl
|
||||
})
|
||||
})
|
||||
|
||||
program
|
||||
.command('app')
|
||||
.description('start app')
|
||||
.option('-p, --port <port>'
|
||||
, 'port (or $PORT)'
|
||||
, Number
|
||||
|
@ -200,12 +237,24 @@ program
|
|||
, 'session SSID (or $SSID)'
|
||||
, String
|
||||
, 'ssid')
|
||||
.option('-a, --auth-url <url>'
|
||||
, 'URL to auth client'
|
||||
, String)
|
||||
.action(function(options) {
|
||||
var env = process.env
|
||||
require('./roles/auth/mock')({
|
||||
|
||||
if (!options.secret) {
|
||||
this.missingArgument('--secret')
|
||||
}
|
||||
if (!options.authUrl) {
|
||||
this.missingArgument('--auth-url')
|
||||
}
|
||||
|
||||
require('./roles/app')({
|
||||
port: env.PORT || options.port
|
||||
, secret: options.secret || env.SECRET
|
||||
, ssid: options.ssid || env.SSID
|
||||
, authUrl: options.authUrl
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -269,6 +318,10 @@ program
|
|||
, 'auth secret'
|
||||
, String
|
||||
, 'kute kittykat')
|
||||
.option('--app-port <port>'
|
||||
, 'app port'
|
||||
, Number
|
||||
, 7100)
|
||||
.action(function() {
|
||||
var log = logger.createLogger('cli')
|
||||
, options = cliutil.lastArg(arguments)
|
||||
|
@ -330,10 +383,22 @@ program
|
|||
'auth-mock'
|
||||
, '--port', options.authPort
|
||||
, '--secret', options.authSecret
|
||||
, '--app-url', util.format('http://localhost:%d/', options.appPort)
|
||||
])
|
||||
.catch(function(err) {
|
||||
log.error('auth-mock died', err.stack)
|
||||
})
|
||||
|
||||
// app
|
||||
procutil.fork(__filename, [
|
||||
'app'
|
||||
, '--port', options.appPort
|
||||
, '--secret', options.authSecret
|
||||
, '--auth-url', util.format('http://localhost:%d/', options.authPort)
|
||||
])
|
||||
.catch(function(err) {
|
||||
log.error('app died', err.stack)
|
||||
})
|
||||
})
|
||||
|
||||
program.parse(process.argv)
|
||||
|
|
29
lib/middleware/jwt.js
Normal file
29
lib/middleware/jwt.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
var jwtutil = require('../util/jwtutil')
|
||||
var urlutil = require('../util/urlutil')
|
||||
|
||||
module.exports = function(options) {
|
||||
return function(req, res, next) {
|
||||
if (req.query.jwt) {
|
||||
// Coming from auth client
|
||||
var data = jwtutil.decode(req.query.jwt, options.secret)
|
||||
, redir = urlutil.removeParam(req.url, 'jwt')
|
||||
if (data) {
|
||||
// Redirect once to get rid of the token
|
||||
req.session.jwt = data
|
||||
res.redirect(redir)
|
||||
}
|
||||
else {
|
||||
// Invalid token, forward to auth client
|
||||
res.redirect(options.authUrl)
|
||||
}
|
||||
}
|
||||
else if (req.session && req.session.jwt) {
|
||||
// Continue existing session
|
||||
next()
|
||||
}
|
||||
else {
|
||||
// No session, forward to auth client
|
||||
res.redirect(options.authUrl)
|
||||
}
|
||||
}
|
||||
}
|
43
lib/roles/app.js
Normal file
43
lib/roles/app.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
var url = require('url')
|
||||
|
||||
var express = require('express')
|
||||
var validator = require('express-validator')
|
||||
|
||||
var logger = require('../util/logger')
|
||||
var pathutil = require('../util/pathutil')
|
||||
|
||||
var jwt = require('../middleware/jwt')
|
||||
|
||||
module.exports = function(options) {
|
||||
var log = logger.createLogger('app')
|
||||
, app = express()
|
||||
|
||||
app.set('view engine', 'jade')
|
||||
app.set('views', pathutil.resource('app/views'))
|
||||
app.set('strict routing', true)
|
||||
app.set('case sensitive routing', true)
|
||||
|
||||
app.use(express.cookieParser())
|
||||
app.use(express.cookieSession({
|
||||
secret: options.secret
|
||||
, key: options.ssid
|
||||
}))
|
||||
app.use(jwt({
|
||||
secret: options.secret
|
||||
, appUrl: options.url
|
||||
, authUrl: options.authUrl
|
||||
}))
|
||||
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('app')))
|
||||
|
||||
app.get('/', function(req, res) {
|
||||
res.render('index')
|
||||
})
|
||||
|
||||
app.listen(options.port)
|
||||
log.info('Listening on port %d', options.port)
|
||||
}
|
|
@ -7,6 +7,7 @@ var logger = require('../../util/logger')
|
|||
var requtil = require('../../util/requtil')
|
||||
var ldaputil = require('../../util/ldaputil')
|
||||
var jwtutil = require('../../util/jwtutil')
|
||||
var urlutil = require('../../util/urlutil')
|
||||
|
||||
module.exports = function(options) {
|
||||
var log = logger.createLogger('auth-ldap')
|
||||
|
@ -33,12 +34,6 @@ module.exports = function(options) {
|
|||
requtil.validate(req, function() {
|
||||
req.checkBody('username').notEmpty()
|
||||
req.checkBody('password').notEmpty()
|
||||
|
||||
// This is a security risk. Someone might forward the user
|
||||
// to the login page with their own redirect set, and they'd
|
||||
// then be able to steal the token. Some kind of a whitelist
|
||||
// or a fixed redirect URL is needed.
|
||||
req.checkBody('redirect').isUrl()
|
||||
})
|
||||
.then(function() {
|
||||
return ldaputil.login(
|
||||
|
@ -56,14 +51,12 @@ module.exports = function(options) {
|
|||
}
|
||||
, secret: options.secret
|
||||
})
|
||||
var target = url.parse(req.body.redirect)
|
||||
target.query = {
|
||||
jwt: token
|
||||
}
|
||||
res.status(200)
|
||||
.json({
|
||||
success: true
|
||||
, redirect: url.format(target)
|
||||
, redirect: urlutil.addParams(options.appUrl, {
|
||||
jwt: token
|
||||
})
|
||||
})
|
||||
})
|
||||
.catch(requtil.ValidationError, function(err) {
|
||||
|
|
|
@ -7,6 +7,7 @@ var logger = require('../../util/logger')
|
|||
var requtil = require('../../util/requtil')
|
||||
var jwtutil = require('../../util/jwtutil')
|
||||
var pathutil = require('../../util/pathutil')
|
||||
var urlutil = require('../../util/urlutil')
|
||||
|
||||
module.exports = function(options) {
|
||||
var log = logger.createLogger('auth-mock')
|
||||
|
@ -59,12 +60,6 @@ module.exports = function(options) {
|
|||
requtil.validate(req, function() {
|
||||
req.checkBody('name').notEmpty()
|
||||
req.checkBody('email').isEmail()
|
||||
|
||||
// This is a security risk. Someone might forward the user
|
||||
// to the login page with their own redirect set, and they'd
|
||||
// then be able to steal the token. Some kind of a whitelist
|
||||
// or a fixed redirect URL is needed.
|
||||
req.checkBody('redirect').isUrl()
|
||||
})
|
||||
.then(function() {
|
||||
log.info('Authenticated "%s"', req.body.email)
|
||||
|
@ -75,14 +70,12 @@ module.exports = function(options) {
|
|||
}
|
||||
, secret: options.secret
|
||||
})
|
||||
var target = url.parse(req.body.redirect)
|
||||
target.query = {
|
||||
jwt: token
|
||||
}
|
||||
res.status(200)
|
||||
.json({
|
||||
success: true
|
||||
, redirect: url.format(target)
|
||||
, redirect: urlutil.addParams(options.appUrl, {
|
||||
jwt: token
|
||||
})
|
||||
})
|
||||
})
|
||||
.catch(requtil.ValidationError, function(err) {
|
||||
|
|
17
lib/util/urlutil.js
Normal file
17
lib/util/urlutil.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
var url = require('url')
|
||||
|
||||
module.exports.addParams = function(originalUrl, params) {
|
||||
var parsed = url.parse(originalUrl, true)
|
||||
parsed.search = null
|
||||
for (var key in params) {
|
||||
parsed.query[key] = params[key]
|
||||
}
|
||||
return url.format(parsed)
|
||||
}
|
||||
|
||||
module.exports.removeParam = function(originalUrl, param) {
|
||||
var parsed = url.parse(originalUrl, true)
|
||||
parsed.search = null
|
||||
delete parsed.query[param]
|
||||
return url.format(parsed)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue