Added LDAP login and forced login
Signed-off-by: James Ketrenos <james_git@ketrenos.com>
This commit is contained in:
parent
4228939b55
commit
c2ae12df5c
@ -1,14 +1,32 @@
|
|||||||
{
|
{
|
||||||
"db": {
|
"db": {
|
||||||
|
"photos": {
|
||||||
"host": "sqlite:photos.db",
|
"host": "sqlite:photos.db",
|
||||||
"options": {
|
"options": {
|
||||||
"logging" : false,
|
"logging" : false,
|
||||||
"timezone": "+00:00"
|
"timezone": "+00:00"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"users": {
|
||||||
|
"host": "sqlite:users.db",
|
||||||
|
"options": {
|
||||||
|
"logging" : false,
|
||||||
|
"timezone": "+00:00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ldap": {
|
||||||
|
"searchFilter": "(uid={{username}})",
|
||||||
|
"cache": true
|
||||||
|
},
|
||||||
|
|
||||||
"server": {
|
"server": {
|
||||||
"port": 8123
|
"port": 8123
|
||||||
},
|
},
|
||||||
"picturesPath": "./pictures",
|
"picturesPath": "./pictures",
|
||||||
"basePath": "/photos"
|
"basePath": "/photos",
|
||||||
|
"sessions": {
|
||||||
|
"db": "sessions.db",
|
||||||
|
"store-secret": "234j23jffj23f!41$@#!1113j3"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,9 @@
|
|||||||
"paper-button": "PolymerElements/paper-button#^2",
|
"paper-button": "PolymerElements/paper-button#^2",
|
||||||
"paper-spinner": "PolymerElements/paper-spinner#^2",
|
"paper-spinner": "PolymerElements/paper-spinner#^2",
|
||||||
"paper-toast": "PolymerElements/paper-toast#^2",
|
"paper-toast": "PolymerElements/paper-toast#^2",
|
||||||
"iron-iconset": "PolymerElements/iron-iconset#^2"
|
"iron-iconset": "PolymerElements/iron-iconset#^2",
|
||||||
|
"paper-input": "PolymerElements/paper-input#^2",
|
||||||
|
"paper-dialog": "PolymerElements/paper-dialog#^2"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"polymer": "2",
|
"polymer": "2",
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
<link rel="import" href="../../bower_components/iron-flex-layout/iron-flex-layout-classes.html">
|
<link rel="import" href="../../bower_components/iron-flex-layout/iron-flex-layout-classes.html">
|
||||||
|
|
||||||
<link rel="import" href="../../bower_components/paper-button/paper-button.html">
|
<link rel="import" href="../../bower_components/paper-button/paper-button.html">
|
||||||
|
<link rel="import" href="../../bower_components/paper-dialog/paper-dialog.html">
|
||||||
|
<link rel="import" href="../../bower_components/paper-input/paper-input.html">
|
||||||
<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
|
<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
|
||||||
<link rel="import" href="../../bower_components/paper-spinner/paper-spinner.html">
|
<link rel="import" href="../../bower_components/paper-spinner/paper-spinner.html">
|
||||||
<link rel="import" href="../../bower_components/paper-tabs/paper-tab.html">
|
<link rel="import" href="../../bower_components/paper-tabs/paper-tab.html">
|
||||||
@ -51,6 +53,13 @@
|
|||||||
:host {
|
:host {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#login {
|
||||||
|
margin: 1em;
|
||||||
|
padding: 2em;
|
||||||
|
border: 1px solid #444;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
#header {
|
#header {
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
background: #ddd;
|
background: #ddd;
|
||||||
@ -60,7 +69,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#header iron-pages {
|
#header iron-pages {
|
||||||
padding-left: 0.5em;
|
padding: 0 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#header [mode='memories'] b:hover {
|
#header [mode='memories'] b:hover {
|
||||||
@ -250,6 +259,14 @@
|
|||||||
|
|
||||||
#memories [is-today] paper-icon-button {
|
#memories [is-today] paper-icon-button {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#requestAccess div > div {
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#requestAccess .title {
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<app-location route="{{route}}"></app-location>
|
<app-location route="{{route}}"></app-location>
|
||||||
@ -261,7 +278,7 @@
|
|||||||
<paper-tab tab="memories"><paper-icon-button icon="today"></paper-icon-button></paper-tab>
|
<paper-tab tab="memories"><paper-icon-button icon="today"></paper-icon-button></paper-tab>
|
||||||
<paper-tab tab="albums"><paper-icon-button icon="folder"></paper-icon-button></paper-tab>
|
<paper-tab tab="albums"><paper-icon-button icon="folder"></paper-icon-button></paper-tab>
|
||||||
</paper-tabs>
|
</paper-tabs>
|
||||||
<iron-pages id="pages" attr-for-selected="id" selected="[[mode]]">
|
<iron-pages id="pages" attr-for-selected="id" selected="[[mode]]" fallback-selection="memories">
|
||||||
<div id="time"><div>... time slider ...</div></div>
|
<div id="time"><div>... time slider ...</div></div>
|
||||||
<div id="memories" class="flex layout vertical center">
|
<div id="memories" class="flex layout vertical center">
|
||||||
<div class="memory-buttons layout self-stretch horizontal around-justified">
|
<div class="memory-buttons layout self-stretch horizontal around-justified">
|
||||||
@ -299,7 +316,8 @@
|
|||||||
<app-header reveals slot="header">
|
<app-header reveals slot="header">
|
||||||
<div id="header" class="layout horizontal center">
|
<div id="header" class="layout horizontal center">
|
||||||
<paper-icon-button icon="search" on-tap="drawerToggle"></paper-icon-button>
|
<paper-icon-button icon="search" on-tap="drawerToggle"></paper-icon-button>
|
||||||
<iron-pages attr-for-selected="mode" selected="[[mode]]">
|
<iron-pages class="flex" attr-for-selected="mode" selected="[[mode]]">
|
||||||
|
<div mode="login"><div>You are not logged in.</div></div>
|
||||||
<div mode="albums" id="breadcrumb" class="horizontal layout center">
|
<div mode="albums" id="breadcrumb" class="horizontal layout center">
|
||||||
<template is="dom-repeat" items="[[breadcrumb(path)]]">
|
<template is="dom-repeat" items="[[breadcrumb(path)]]">
|
||||||
<div tabindex="0" on-tap="loadPath">[[item.name]] /</div>
|
<div tabindex="0" on-tap="loadPath">[[item.name]] /</div>
|
||||||
@ -308,14 +326,42 @@
|
|||||||
<div mode="time">time</div>
|
<div mode="time">time</div>
|
||||||
<div mode="memories">Photos taken on <b on-tap="drawerToggle">[[memoryDate]]</b></div>
|
<div mode="memories">Photos taken on <b on-tap="drawerToggle">[[memoryDate]]</b></div>
|
||||||
</iron-pages>
|
</iron-pages>
|
||||||
|
<div>
|
||||||
|
<div hidden$="[[user]]">
|
||||||
|
<!--paper-button>login</paper-button-->
|
||||||
|
</div>
|
||||||
|
<div hidden$="[[!user]]">
|
||||||
|
<paper-button on-tap="logout">logout</paper-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</app-header>
|
</app-header>
|
||||||
|
<div>
|
||||||
|
<div hidden$="[[!user]]">
|
||||||
<div id="thumbnails" class="layout horizontal wrap"></div>
|
<div id="thumbnails" class="layout horizontal wrap"></div>
|
||||||
</app-header-layout>
|
|
||||||
<div id="bottom" class="layout vertical center">
|
<div id="bottom" class="layout vertical center">
|
||||||
<paper-spinner hidden$="[[!loading]]" active$="[[loading]]" class="thin"></paper-spinner>
|
<paper-spinner hidden$="[[!loading]]" active$="[[loading]]" class="thin"></paper-spinner>
|
||||||
<div hidden$="[[loading]]">~ the end ~</div>
|
<div hidden$="[[loading]]">~ the end ~</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="login" hidden$="[[user]]" class="layout horizontal center">
|
||||||
|
<div class="flex layout vertical">
|
||||||
|
<div id="instructions">
|
||||||
|
<p><b>ketrenos.com</b> is a personal website for my family and friends.</p>
|
||||||
|
<p>If you already have an email account on this domain, you can login to the photo viewer
|
||||||
|
using your normal @ketrenos.com account name, and password.</p>
|
||||||
|
</p>If you are a friend or family member (immediate, or extended)
|
||||||
|
<a on-tap="requestAccess" href="#">request access</a>,
|
||||||
|
provide your email address, and tell me who in the extended Ketrenos
|
||||||
|
universe you know. If you're not a bot, I'll very likely give you access :)</p>
|
||||||
|
</div>
|
||||||
|
<paper-input tabindex autofocus id="username" label="User ID" value="{{username}}" on-keypress="enterCheck"></paper-input>
|
||||||
|
<paper-input tabindex id="password" label="Password" type="password" value="{{password}}" on-keypress="enterCheck"></paper-input>
|
||||||
|
<paper-button tabindex disabled$="[[disableLogin(username,password)]]" on-tap="login">login</paper-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</app-header-layout>
|
||||||
</app-drawer-layout>
|
</app-drawer-layout>
|
||||||
<div id="pager">pager</div>
|
<div id="pager">pager</div>
|
||||||
<div id="yearSlider">
|
<div id="yearSlider">
|
||||||
@ -323,6 +369,16 @@
|
|||||||
<div>item</div>
|
<div>item</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
<paper-dialog id="requestAccess" modal>
|
||||||
|
<div class="layout vertical">
|
||||||
|
<div class="title">Hello!</div>
|
||||||
|
<div>
|
||||||
|
Unfortunately, I haven't built this part of the site yet... send me an email (james @ ketrenos.com)
|
||||||
|
and I'll create an account for you.
|
||||||
|
</div>
|
||||||
|
<paper-button dialog-dismiss>close</paper-button>
|
||||||
|
</div>
|
||||||
|
</paper-dialog>
|
||||||
<paper-toast id="toast"></paper-toast>
|
<paper-toast id="toast"></paper-toast>
|
||||||
<photo-lightbox tabindex="0" id="lightbox" on-close="lightBoxClose" on-next="lightBoxNext" on-previous="lightBoxPrevious"></photo-lightbox>
|
<photo-lightbox tabindex="0" id="lightbox" on-close="lightBoxClose" on-next="lightBoxNext" on-previous="lightBoxPrevious"></photo-lightbox>
|
||||||
</template>
|
</template>
|
||||||
@ -333,10 +389,22 @@
|
|||||||
Polymer({
|
Polymer({
|
||||||
is: "ketr-photos",
|
is: "ketr-photos",
|
||||||
properties: {
|
properties: {
|
||||||
|
password: {
|
||||||
|
type: String,
|
||||||
|
value: ""
|
||||||
|
},
|
||||||
|
username: {
|
||||||
|
type: String,
|
||||||
|
value: ""
|
||||||
|
},
|
||||||
years: {
|
years: {
|
||||||
type: Array,
|
type: Array,
|
||||||
value: []
|
value: []
|
||||||
},
|
},
|
||||||
|
user: {
|
||||||
|
type: Object,
|
||||||
|
value: null
|
||||||
|
},
|
||||||
order: {
|
order: {
|
||||||
type: String,
|
type: String,
|
||||||
value: "by-date"
|
value: "by-date"
|
||||||
@ -373,7 +441,7 @@
|
|||||||
},
|
},
|
||||||
mode: {
|
mode: {
|
||||||
type: String,
|
type: String,
|
||||||
value: "memories"
|
value: "login"
|
||||||
},
|
},
|
||||||
date: {
|
date: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -387,6 +455,32 @@
|
|||||||
"dateChanged(date)"
|
"dateChanged(date)"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
disableLogin: function(username, password) {
|
||||||
|
return !username || username == "" || !password || password == "";
|
||||||
|
},
|
||||||
|
|
||||||
|
enterCheck: function(event) {
|
||||||
|
if (event.code == 'Enter') {
|
||||||
|
if (event.currentTarget.id == "username") {
|
||||||
|
event.preventDefault();
|
||||||
|
this.async(function() {
|
||||||
|
this.$.password.focus();
|
||||||
|
}, 100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.currentTarget.id == "password") {
|
||||||
|
event.preventDefault();
|
||||||
|
this.login();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
requestAccess: function(event) {
|
||||||
|
this.$.requestAccess.open();
|
||||||
|
},
|
||||||
|
|
||||||
add: function(a, b) {
|
add: function(a, b) {
|
||||||
return parseInt(a) + parseInt(b);
|
return parseInt(a) + parseInt(b);
|
||||||
},
|
},
|
||||||
@ -407,6 +501,39 @@
|
|||||||
return order == "by-album";
|
return order == "by-album";
|
||||||
},
|
},
|
||||||
|
|
||||||
|
login: function(event) {
|
||||||
|
if (this.loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.loading = true;
|
||||||
|
window.fetch("api/v1/users/login", function(error, xhr) {
|
||||||
|
this.loading = false;
|
||||||
|
this.password = "";
|
||||||
|
var user;
|
||||||
|
try {
|
||||||
|
user = JSON.parse(xhr.responseText);
|
||||||
|
} catch(___) {
|
||||||
|
this.$.toast.text = "Unable to load/parse user information.";
|
||||||
|
this.$.toast.setAttribute("error", true);
|
||||||
|
this.$.toast.updateStyles();
|
||||||
|
this.$.toast.show();
|
||||||
|
console.error("Unable to parse user information");
|
||||||
|
}
|
||||||
|
this.user = user;
|
||||||
|
}.bind(this), null, "POST", { u: this.username, p: this.password });
|
||||||
|
},
|
||||||
|
|
||||||
|
logout: function(event) {
|
||||||
|
if (this.loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.loading = true;
|
||||||
|
window.fetch("api/v1/users/logout", function(error, xhr) {
|
||||||
|
this.loading = false;
|
||||||
|
this.user = null;
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
changeMode: function(event) {
|
changeMode: function(event) {
|
||||||
var mode = event.currentTarget.icon;
|
var mode = event.currentTarget.icon;
|
||||||
if (this.mode != mode) {
|
if (this.mode != mode) {
|
||||||
@ -1001,6 +1128,14 @@
|
|||||||
}, 100);
|
}, 100);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
userChanged: function(user) {
|
||||||
|
this.resetPhotos();
|
||||||
|
if (user) {
|
||||||
|
this._loadAlbums();
|
||||||
|
this._loadPhotos();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
ready: function() {
|
ready: function() {
|
||||||
this.$.calendar.partsHidden = {
|
this.$.calendar.partsHidden = {
|
||||||
"year": true
|
"year": true
|
||||||
@ -1017,8 +1152,31 @@
|
|||||||
}
|
}
|
||||||
}.bind(this), 100);
|
}.bind(this), 100);
|
||||||
|
|
||||||
this._loadAlbums();
|
this.loading = true;
|
||||||
this._loadPhotos();
|
window.fetch("api/v1/users", function(error, xhr) {
|
||||||
|
this.loading = false;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error(JSON.stringify(error, null, 2));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var results;
|
||||||
|
try {
|
||||||
|
results = JSON.parse(xhr.responseText);
|
||||||
|
} catch (___) {
|
||||||
|
this.$.toast.text = "Unable to parse authentication response.";
|
||||||
|
this.$.toast.setAttribute("error", true);
|
||||||
|
this.$.toast.updateStyles();
|
||||||
|
this.$.toast.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results && results.username) {
|
||||||
|
this.user = results;
|
||||||
|
this.mode = "memories";
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
this.onResize();
|
this.onResize();
|
||||||
|
|
||||||
|
@ -16,9 +16,12 @@
|
|||||||
"bluebird": "^3.5.1",
|
"bluebird": "^3.5.1",
|
||||||
"body-parser": "^1.18.2",
|
"body-parser": "^1.18.2",
|
||||||
"config": "^1.28.1",
|
"config": "^1.28.1",
|
||||||
|
"connect-sqlite3": "^0.9.11",
|
||||||
"cookie-parser": "^1.4.3",
|
"cookie-parser": "^1.4.3",
|
||||||
"exif-reader": "github:paras20xx/exif-reader",
|
"exif-reader": "github:paras20xx/exif-reader",
|
||||||
"express": "^4.16.2",
|
"express": "^4.16.2",
|
||||||
|
"express-session": "^1.15.6",
|
||||||
|
"ldapauth-fork": "^4.0.2",
|
||||||
"mariasql": "^0.2.6",
|
"mariasql": "^0.2.6",
|
||||||
"moment": "^2.22.2",
|
"moment": "^2.22.2",
|
||||||
"morgan": "^1.9.0",
|
"morgan": "^1.9.0",
|
||||||
|
@ -12,7 +12,8 @@ const express = require("express"),
|
|||||||
morgan = require("morgan"),
|
morgan = require("morgan"),
|
||||||
bodyParser = require("body-parser"),
|
bodyParser = require("body-parser"),
|
||||||
config = require("config"),
|
config = require("config"),
|
||||||
db = require("./db"),
|
session = require('express-session'),
|
||||||
|
SQLiteStore = require('connect-sqlite3')(session),
|
||||||
scanner = require("./scanner");
|
scanner = require("./scanner");
|
||||||
|
|
||||||
require("./console-line.js"); /* Monkey-patch console.log with line numbers */
|
require("./console-line.js"); /* Monkey-patch console.log with line numbers */
|
||||||
@ -40,10 +41,9 @@ app.set("trust proxy", true);
|
|||||||
/* Handle static files first so excessive logging doesn't occur */
|
/* Handle static files first so excessive logging doesn't occur */
|
||||||
app.use(basePath, express.static("frontend", { index: false }));
|
app.use(basePath, express.static("frontend", { index: false }));
|
||||||
|
|
||||||
app.use(basePath, express.static(picturesPath, { index: false }));
|
|
||||||
|
|
||||||
app.use(morgan("common"));
|
app.use(morgan("common"));
|
||||||
|
|
||||||
|
app.use(bodyParser.json());
|
||||||
app.use(bodyParser.urlencoded({
|
app.use(bodyParser.urlencoded({
|
||||||
extended: false
|
extended: false
|
||||||
}));
|
}));
|
||||||
@ -64,12 +64,18 @@ app.use(function(req, res, next){
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(basePath + "api/v1/photos", require("./routes/photos"));
|
app.use(session({
|
||||||
app.use(basePath + "api/v1/days", require("./routes/days"));
|
store: new SQLiteStore({ db: config.get("sessions.db") }),
|
||||||
app.use(basePath + "api/v1/albums", require("./routes/albums"));
|
secret: config.get("sessions.store-secret"),
|
||||||
|
cookie: { maxAge: 7 * 24 * 60 * 60 * 1000 } // 1 week
|
||||||
|
}));
|
||||||
|
|
||||||
/* Declare the "catch all" index route last; the final route is a 404 dynamic router */
|
const index = require("./routes/index");
|
||||||
app.use(basePath, require("./routes/index"));
|
|
||||||
|
/* Allow loading of the app w/out being logged in */
|
||||||
|
app.use(basePath, index);
|
||||||
|
|
||||||
|
app.use(basePath + "api/v1/users", require("./routes/users"));
|
||||||
|
|
||||||
app.use(function(err, req, res, next) {
|
app.use(function(err, req, res, next) {
|
||||||
res.status(err.status || 500).json({
|
res.status(err.status || 500).json({
|
||||||
@ -78,6 +84,23 @@ app.use(function(err, req, res, next) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* Everything below here requires a successful authentication */
|
||||||
|
|
||||||
|
app.use(basePath, function(req, res, next) {
|
||||||
|
if (!req.session || !req.session.user || !req.session.user.username) {
|
||||||
|
return res.status(401).send("Unauthorized");
|
||||||
|
}
|
||||||
|
return next();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(basePath, express.static(picturesPath, { index: false }));
|
||||||
|
app.use(basePath + "api/v1/photos", require("./routes/photos"));
|
||||||
|
app.use(basePath + "api/v1/days", require("./routes/days"));
|
||||||
|
app.use(basePath + "api/v1/albums", require("./routes/albums"));
|
||||||
|
|
||||||
|
/* Declare the "catch all" index route last; the final route is a 404 dynamic router */
|
||||||
|
app.use(basePath + "/*", index);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create HTTP server and listen for new connections
|
* Create HTTP server and listen for new connections
|
||||||
*/
|
*/
|
||||||
@ -85,7 +108,7 @@ app.set("port", serverConfig.port);
|
|||||||
|
|
||||||
const server = require("http").createServer(app);
|
const server = require("http").createServer(app);
|
||||||
|
|
||||||
db.then(function(photoDB) {
|
require("./db/photos").then(function(photoDB) {
|
||||||
console.log("DB connected. Opening server.");
|
console.log("DB connected. Opening server.");
|
||||||
server.listen(serverConfig.port);
|
server.listen(serverConfig.port);
|
||||||
return photoDB;
|
return photoDB;
|
||||||
|
@ -20,12 +20,10 @@ const fs = require('fs'),
|
|||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
const db = {
|
const db = {
|
||||||
sequelize: new Sequelize(config.get("db.host"), config.get("db.options")),
|
sequelize: new Sequelize(config.get("db.photos.host"), config.get("db.photos.options")),
|
||||||
Sequelize: Sequelize
|
Sequelize: Sequelize
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("DB initialization beginning. DB access will block.");
|
|
||||||
|
|
||||||
return db.sequelize.authenticate().then(function () {
|
return db.sequelize.authenticate().then(function () {
|
||||||
const Album = db.sequelize.define('album', {
|
const Album = db.sequelize.define('album', {
|
||||||
id: {
|
id: {
|
||||||
@ -74,19 +72,16 @@ function init() {
|
|||||||
timestamps: false
|
timestamps: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
console.log("Connection established successfully with DB.");
|
|
||||||
return db.sequelize.sync({
|
return db.sequelize.sync({
|
||||||
force: false
|
force: false
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
console.log("DB relationships successfully mapped. DB access unblocked.");
|
|
||||||
return db;
|
return db;
|
||||||
});
|
});
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
console.log("ERROR: Failed to authenticate with DB");
|
console.log("ERROR: Failed to authenticate with PHOTOS DB");
|
||||||
console.log("ERROR: " + JSON.stringify(config.get("db"), null, 2));
|
console.log("ERROR: " + JSON.stringify(config.get("db"), null, 2));
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return reject(error);
|
throw error;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
60
server/db/users.js
Normal file
60
server/db/users.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* * Copyright (c) 2016, Intel Corporation.
|
||||||
|
*
|
||||||
|
* This program is licensed under the terms and conditions of the
|
||||||
|
* Apache License, version 2.0. The full text of the Apache License is at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class will instantiate the ORM, load in the models, call the method
|
||||||
|
* to create db connections, test the connection, then create the tables and
|
||||||
|
* relationships if not present
|
||||||
|
*/
|
||||||
|
const fs = require('fs'),
|
||||||
|
path = require('path'),
|
||||||
|
Sequelize = require('sequelize'),
|
||||||
|
config = require('config');
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
const db = {
|
||||||
|
sequelize: new Sequelize(config.get("db.users.host"), config.get("db.users.options")),
|
||||||
|
Sequelize: Sequelize
|
||||||
|
};
|
||||||
|
|
||||||
|
return db.sequelize.authenticate().then(function () {
|
||||||
|
const User = db.sequelize.define('users', {
|
||||||
|
id: {
|
||||||
|
type: Sequelize.INTEGER,
|
||||||
|
primaryKey: true,
|
||||||
|
autoIncrement: true
|
||||||
|
},
|
||||||
|
displayName: Sequelize.STRING,
|
||||||
|
uid: Sequelize.STRING,
|
||||||
|
isLDAP: Sequelize.BOOLEAN,
|
||||||
|
authToken: Sequelize.STRING,
|
||||||
|
authDate: Sequelize.DATE,
|
||||||
|
authenticated: Sequelize.BOOLEAN,
|
||||||
|
mail: Sequelize.STRING,
|
||||||
|
memberSince: Sequelize.DATE,
|
||||||
|
password: Sequelize.STRING, /* SHA hash of user supplied password for !isLDAP users */
|
||||||
|
passwordExpires: Sequelize.DATE
|
||||||
|
}, {
|
||||||
|
timestamps: false
|
||||||
|
});
|
||||||
|
return db.sequelize.sync({
|
||||||
|
force: false
|
||||||
|
}).then(function () {
|
||||||
|
return db;
|
||||||
|
});
|
||||||
|
}).catch(function (error) {
|
||||||
|
console.log("ERROR: Failed to authenticate with USER DB");
|
||||||
|
console.log("ERROR: " + JSON.stringify(config.get("db"), null, 2));
|
||||||
|
console.log(error);
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = init();
|
@ -8,7 +8,7 @@ const express = require("express"),
|
|||||||
|
|
||||||
let photoDB;
|
let photoDB;
|
||||||
|
|
||||||
require("../db").then(function(db) {
|
require("../db/photos").then(function(db) {
|
||||||
photoDB = db;
|
photoDB = db;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ router.get("/*", function(req, res/*, next*/) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return photoDB.sequelize.query("SELECT * FROM albums WHERE parentId=:parentId ORDER BY name", {
|
return photoDB.sequelize.query("SELECT * FROM albums WHERE parentId=:parentId ORDER BY LOWER(name)", {
|
||||||
replacements: {
|
replacements: {
|
||||||
parentId: parent.id
|
parentId: parent.id
|
||||||
},
|
},
|
||||||
|
@ -8,7 +8,7 @@ const express = require("express"),
|
|||||||
|
|
||||||
let photoDB;
|
let photoDB;
|
||||||
|
|
||||||
require("../db").then(function(db) {
|
require("../db/photos").then(function(db) {
|
||||||
photoDB = db;
|
photoDB = db;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ const extensionMatch = new RegExp("^.*?(" + extensions.join("|") + ")$", "i");
|
|||||||
* If so, 404 because the asset isn't there. otherwise assume it is a
|
* If so, 404 because the asset isn't there. otherwise assume it is a
|
||||||
* dynamic client side route and *then* return index.html.
|
* dynamic client side route and *then* return index.html.
|
||||||
*/
|
*/
|
||||||
router.get("/*", function(req, res/*, next*/) {
|
router.get("/", function(req, res/*, next*/) {
|
||||||
const parts = url.parse(req.url),
|
const parts = url.parse(req.url),
|
||||||
basePath = req.app.get("basePath");
|
basePath = req.app.get("basePath");
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ const express = require("express"),
|
|||||||
|
|
||||||
let photoDB;
|
let photoDB;
|
||||||
|
|
||||||
require("../db").then(function(db) {
|
require("../db/photos").then(function(db) {
|
||||||
photoDB = db;
|
photoDB = db;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
91
server/routes/users.js
Executable file
91
server/routes/users.js
Executable file
@ -0,0 +1,91 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const express = require("express"),
|
||||||
|
config = require("config"),
|
||||||
|
LdapAuth = require("ldapauth-fork");
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
let userDB;
|
||||||
|
|
||||||
|
|
||||||
|
const ldap = new LdapAuth(config.get("ldap"));
|
||||||
|
|
||||||
|
require("../db/users").then(function(db) {
|
||||||
|
userDB = db;
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/", function(req, res/*, next*/) {
|
||||||
|
if (req.session.user) {
|
||||||
|
return res.status(200).send(req.session.user);
|
||||||
|
}
|
||||||
|
return res.status(200).send({});
|
||||||
|
});
|
||||||
|
|
||||||
|
function ldapPromise(username, password) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
ldap.authenticate(username, password, function(error, user) {
|
||||||
|
if (error) {
|
||||||
|
return reject(error);
|
||||||
|
}
|
||||||
|
return resolve(user);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
router.post("/login", function(req, res) {
|
||||||
|
let username = req.query.u || req.body.u || "",
|
||||||
|
password = req.query.p || req.body.p || "";
|
||||||
|
|
||||||
|
console.log("Login attempt");
|
||||||
|
|
||||||
|
if (!username || !password) {
|
||||||
|
return res.status(400).send("Missing username and/or password");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We use LDAP as the primary authenticator; if the user is not
|
||||||
|
* found there, we look them up in the site-specific user database */
|
||||||
|
|
||||||
|
return ldapPromise(username, password).then(function(user) {
|
||||||
|
return user;
|
||||||
|
}).catch(function() {
|
||||||
|
let query = "SELECT * FROM users WHERE username=:username";
|
||||||
|
return userDB.sequelize.query(query, {
|
||||||
|
replacements: {
|
||||||
|
username: username,
|
||||||
|
},
|
||||||
|
type: userDB.Sequelize.QueryTypes.SELECT
|
||||||
|
}).then(function(users) {
|
||||||
|
if (users.length != 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return users[0];
|
||||||
|
});
|
||||||
|
}).then(function(user) {
|
||||||
|
if (!user) {
|
||||||
|
console.log(username + " not found: " + error);
|
||||||
|
req.session.user = {};
|
||||||
|
return res.status(401).send("Invalid login credentials");
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Logging in as " + user.displayName);
|
||||||
|
|
||||||
|
req.session.user = {
|
||||||
|
name: user.displayName,
|
||||||
|
mail: user.mail,
|
||||||
|
username: user.uid
|
||||||
|
};
|
||||||
|
|
||||||
|
return res.status(200).send(req.session.user);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/logout", function(req, res) {
|
||||||
|
if (req.session && req.session.user) {
|
||||||
|
req.session.user = {};
|
||||||
|
}
|
||||||
|
res.status(200).send(req.session.user);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
Loading…
x
Reference in New Issue
Block a user