Skip to content

Commit

Permalink
Basic authentication - #14
Browse files Browse the repository at this point in the history
  • Loading branch information
sv2 committed Nov 3, 2017
1 parent d76067e commit 7d55166
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 35 deletions.
2 changes: 1 addition & 1 deletion dist/css/sws.min.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/js/sws.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/maps/sws.min.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/authtest/authtest.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ parser.validate(specLocation,function(err, api) {
authentication: true,
onAuthenticate: function(req,username,password){
// simple check for username and password
return((username==='prometheus') && (password==='prometheus') );
return((username==='swagger-stats') && (password==='swagger-stats') );
}
}));

Expand Down
40 changes: 33 additions & 7 deletions lib/swsInterface.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ var path = require('path');
var debug = require('debug')('sws:interface');
var promClient = require("prom-client");
var basicAuth = require("basic-auth");
var Cookies = require('cookies');
const uuidv1 = require('uuid/v1');

var swsUtil = require('./swsUtil');
var swsProcessor = require('./swsProcessor');
Expand Down Expand Up @@ -37,6 +39,11 @@ var pathDist = swsOptions.uriPath+'/dist';
var pathStats = swsOptions.uriPath+'/stats';
var pathMetrics = swsOptions.uriPath+'/metrics';


// Session IDs storage
var sessionIDs = {};
// TODO keep expiraiton hash, and remove expired session ids

// Request hanlder
function handleRequest(req, res){
try {
Expand Down Expand Up @@ -85,26 +92,45 @@ function processAuth(req,res,useWWWAuth) {
return true;
}

// TODO Check session cookie
var cookies = new Cookies( req, res );

// Check session cookie
var sessionIdCookie = cookies.get('sws-session-id');
if( (sessionIdCookie !== undefined) && (sessionIdCookie !== null) ){

if( sessionIdCookie in sessionIDs ){
// renew it
sessionIDs[sessionIdCookie] = Date.now();
cookies.set('sws-session-id',sessionIdCookie,{path:swsOptions.uriPath,maxAge:900000});
// Ok
return true;
}
}

var authInfo = basicAuth(req);

var authenticated = false;
var msg = 'Login required';

if( (authInfo !== undefined) && (authInfo!==null) && ('name' in authInfo) && ('pass' in authInfo)){
if(typeof swsOptions.onAuthenticate === 'function'){
if( swsOptions.onAuthenticate(req, authInfo.name, authInfo.pass) ){
authenticated = true;
// Generate session id
var sessid = uuidv1();
sessionIDs[sessid] = Date.now();
// Set session cookie with expiration in 15 min
cookies.set('sws-session-id',sessid,{path:swsOptions.uriPath,maxAge:900000});

}else{
msg = 'Invalid credentials';
}
}
}

if( !authenticated ){
if(useWWWAuth){
res.setHeader('WWW-Authenticate', 'Basic realm="swagger-stats"');
}
// TODO Reconsider 401 response. Should it be 403 or 500, to prevent browser Basic Auth pop-up in UI ?
res.status(403).end('Access denied');
// Reconsidered 401 response. Make it be 403 to prevent browser Basic Auth pop-up in UI
res.status(403).end(msg);
return false;
}

Expand Down Expand Up @@ -158,7 +184,7 @@ module.exports = {
}else if(req.url.startsWith(pathMetrics)){
processGetMetrics(req,res);
return;
}else if(req.url == pathUI ){
}else if(req.url.startsWith(pathUI) ){
res.status(200).send(uiMarkup);
return;
}else if(req.url.startsWith(pathDist)) {
Expand Down
20 changes: 16 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@
"license": "MIT",
"dependencies": {
"basic-auth": "^2.0.0",
"cookies": "^0.7.1",
"debug": "^3.1.0",
"moment": "^2.19.1",
"path-to-regexp": "^2.1.0",
"prom-client": "^10.2.1"
"prom-client": "^10.2.1",
"uuid": "^3.1.0"
},
"devDependencies": {
"artillery": "^1.6.0-10",
Expand Down
13 changes: 13 additions & 0 deletions ui/css/sws.css
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,19 @@ body {
margin-bottom: 20px;
}

.sws-login-msg {
background-color: #f8ac59;
color: #FFFFFF;
font-size: 16px;
padding: 2px;
width: 100%;
margin-bottom: 10px;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: .25em;
}

.sws-btn-login:hover,
.sws-btn-login:focus,
.sws-btn-login:active,
Expand Down
31 changes: 30 additions & 1 deletion ui/sws.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@

// login state
this.loggingIn = false;
this.loginUsername = '';
this.loginPassword = '';


// Active Page Id
this.activePageId = null;
Expand Down Expand Up @@ -258,6 +261,9 @@
'<span class="sws-tool-title">'+page.title+'</span></a></li>';
var pageNav = $(navHtml);
$('#sws-toolbar').append(pageNav);
if(page.hidden){
$('#'+page.id).hide();
}
// Add Content entry for the page
//var elemPageContent = $('<div id="'+pageId+'_content" style="display: none"></div>');
var elemPageContent = $('<div id="'+pageId+'_content"></div>');
Expand Down Expand Up @@ -357,7 +363,7 @@
}
}else{
if(window.location.hash === loginLocHash ){
window.location.hash = '#'+this.layout.startpage;
window.location.hash = '#'+that.layout.startpage;
return;
}
}
Expand Down Expand Up @@ -484,11 +490,23 @@
}
}
}

// Post-process getDataReq, if necessary
if('getdataproc' in activeDef){
activeDef.getdataproc(this.activePageId, this.activePageContext, getdataReq);
}

// Add login credentials, if specified
if( (this.loginUsername!=='') && (this.loginPassword!=='')){
if(!('headers' in getdataReq)){
getdataReq.headers = {};
}
getdataReq.headers['Authorization'] = 'Basic ' + btoa(this.loginUsername+':'+this.loginPassword);
// one attempt only
this.loginUsername='';
this.loginPassword='';
}

var that = this;
$.ajax( getdataReq )
.done(function( msg ) {
Expand All @@ -508,6 +526,7 @@
// Check if authentication is required
if(jqXHR.status===403){
that.loggingIn = true;
$('.sws-login-msg').html(jqXHR.responseText);
that.hideTimeControls();
var locHash = '#'+that.layout.loginpage;
console.log('Login required '+ locHash);
Expand Down Expand Up @@ -594,8 +613,18 @@
this.$element.on('sws-ondata-apiop', $.proxy(this.onDataAPIOp, this));
this.$element.on('sws-onchange-apiop', $.proxy(this.onChangeAPIOp, this));
$('.sws-refresh').on('click', $.proxy(this.onRefreshClick, this));
$('#sws-login-submit').click($.proxy(this.onLogin, this));
};

SWSUI.prototype.onLogin = function(Event) {
this.loginUsername = $('#sws-login-username').val();
this.loginPassword = $('#sws-login-password').val();
this.loggingIn = false; // allow request to /stats
var startLocHash = '#'+this.layout.startpage;
this.setActive(startLocHash);
//window.location.hash = startLocHash;
};

SWSUI.prototype.onRefreshClick = function(Event){
if(!Event.target) return;
var interval = parseInt($(Event.target).attr('interval'));
Expand Down
25 changes: 7 additions & 18 deletions ui/swsLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -1008,31 +1008,19 @@ var SWSLayout = function(){
var page = {
title: 'Login',
icon: 'fa-sign-in',
hidden: true,
datevent: 'sws-ondata-login',
getdata: {
type: "get",
url: "stats",
data: {}
},
getfieldsonce:['apidefs','apistats'],
getdataproc: function(pageId, pageCtx, getDataReq){
// TODO Add auth data
/*
if((pageId=="sws_apiop") && (pageCtx != null)){
var vals = pageCtx.split(',',2);
if(vals.length==2){
getDataReq.data.method=vals[0];
getDataReq.data.path=vals[1];
}
}
*/
},
rows: {
r1: {
columns: {
sws_login_r1c1 : { class:"col-md-4", type: "empty"},
sws_login_r1c2 : { class:"col-md-4", type: "markup",
markup: '<div class="sws-logo-xxl">{<i class="fa fa-signal"></i>}</div>'
markup: '<div class="sws-logo-xxl">{<i class="fa fa-sign-in"></i>}</div>'
},
sws_login_r1c3 : { class:"col-md-4", type: "empty"}
}
Expand All @@ -1041,14 +1029,15 @@ var SWSLayout = function(){
columns: {
sws_login_r2c1 : { class:"col-md-4", type: "empty"},
sws_login_r2c2 : { class:"col-md-4", type: "markup",
markup: '<form>\n' +
markup: '<div class="sws-login-msg">Warning</div>\n'+
'<form>\n' +
'<div class="form-group">\n' +
' <input class="form-control" placeholder="Username" required="">\n' +
' <input id="sws-login-username" class="form-control" placeholder="Username" required="">\n' +
'</div>\n' +
'<div class="form-group">\n' +
' <input type="password" class="form-control" placeholder="Password" required="">\n' +
' <input id="sws-login-password" type="password" class="form-control" placeholder="Password" required="">\n' +
'</div>\n' +
'<button class="btn sws-btn-login">Login</button>\n' +
'<button id="sws-login-submit" class="btn sws-btn-login">Login</button>\n' +
'</form>'
},
sws_login_r2c3 : { class:"col-md-4", type: "empty"}
Expand Down

0 comments on commit 7d55166

Please sign in to comment.