From a62bb80478632116ce0f20409ae7b6eb068193a8 Mon Sep 17 00:00:00 2001
From: Ruben Meyer <46384706+rxbnDE@users.noreply.github.com>
Date: Sun, 8 Sep 2019 19:43:52 +0200
Subject: [PATCH] web module
---
bin/web/app/routes/main.js | 23 ++++++
bin/web/app/views/index.pug | 0
bin/web/auth/routes/api.js | 51 ++++++++++++
bin/web/auth/routes/main.js | 8 ++
bin/web/auth/routes/static.js | 23 ++++++
bin/web/auth/views/index.pug | 66 +++++++++++++++
bin/web/module.js | 138 ++++++++++++++++++++++++++++++++
res/web/auth/css/stylesheet.css | 8 ++
8 files changed, 317 insertions(+)
create mode 100644 bin/web/app/routes/main.js
create mode 100644 bin/web/app/views/index.pug
create mode 100644 bin/web/auth/routes/api.js
create mode 100644 bin/web/auth/routes/main.js
create mode 100644 bin/web/auth/routes/static.js
create mode 100644 bin/web/auth/views/index.pug
create mode 100644 bin/web/module.js
create mode 100644 res/web/auth/css/stylesheet.css
diff --git a/bin/web/app/routes/main.js b/bin/web/app/routes/main.js
new file mode 100644
index 0000000..e09ce75
--- /dev/null
+++ b/bin/web/app/routes/main.js
@@ -0,0 +1,23 @@
+var express = require('express');
+var route = express.Router();
+
+
+route.use((req, res, next) => {
+ if(!req.session || !req.session.user) {
+ if(!req.path.startsWith('/api')) {
+ let path = (global['gds'].cfg.web.rootUrl+'/auth');
+
+ if(global['gds'].cfg.web.doubleSlashCheck) path = path.replace(/\/+/g, "/");
+
+ res.redirect(path);
+ } else {
+ res.redirect('/auth'+req.path);
+ }
+ } else next();
+});
+
+route.get('/', (req, res) => {
+ res.status(202).end('test');
+});
+
+module.exports = route;
diff --git a/bin/web/app/views/index.pug b/bin/web/app/views/index.pug
new file mode 100644
index 0000000..e69de29
diff --git a/bin/web/auth/routes/api.js b/bin/web/auth/routes/api.js
new file mode 100644
index 0000000..91405f9
--- /dev/null
+++ b/bin/web/auth/routes/api.js
@@ -0,0 +1,51 @@
+var express = require('express');
+var route = express.Router();
+
+route.get('/register', (req, res) => {
+ if(!global['gds'].cfg.web.registration) {
+ return res.type('json').status(400).end(JSON.stringify({status: 400, message: "msg.auth.registration.deactivated"}));
+ }
+ // TODO: register
+});
+
+route.get('/login', (req, res) => {
+ // TODO: login
+ let a = global['modules'].sso.createAuthentication({
+ url: global['gds'].cfg.sso.authenticator,
+ appId: global['gds'].cfg.sso.appId
+ })
+ console.log(a);
+ res.end('login');
+});
+
+route.post('/authenticate', (req, res) => {
+ // TODO: authenticate
+});
+
+route.get('/logout', (req, res) => {
+ if(!req.session.user) {
+ return res.type('json').end(JSON.stringify({
+ status: 401,
+ message: 'msg.auth.login.required'
+ }));
+ } else {
+ res.clearCookie('RememberMe');
+ req.session.destroy();
+ return res.type('json').end(JSON.stringify({
+ status: 200,
+ message: 'msg.auth.logout.successful'
+ }));
+ }
+});
+
+if(global['gds'].debug) {
+ // DEBUG info
+ route.get('/info', (req, res) => {
+ let obj = {};
+ if(req.session) obj.session = req.session;
+ if(req.cookies) obj.cookie = req.cookies;
+ res.type('json').end(JSON.stringify(obj));
+ });
+}
+
+module.exports = route;
diff --git a/bin/web/auth/routes/main.js b/bin/web/auth/routes/main.js
new file mode 100644
index 0000000..c36b2c0
--- /dev/null
+++ b/bin/web/auth/routes/main.js
@@ -0,0 +1,8 @@
+var express = require('express');
+var route = express.Router();
+
+
+route.use('/api', require(global['__dirname']+'/bin/web/auth/routes/api'));
+route.use('/', require(global['__dirname']+'/bin/web/auth/routes/static'));
+
+module.exports = route;
diff --git a/bin/web/auth/routes/static.js b/bin/web/auth/routes/static.js
new file mode 100644
index 0000000..7144484
--- /dev/null
+++ b/bin/web/auth/routes/static.js
@@ -0,0 +1,23 @@
+var express = require('express');
+var route = express.Router();
+
+
+route.all('/', function(req, res, next) {
+ // TODO: show login page or dashboard
+ // res.end('login or dashboard');
+ res.render('auth/views/index');
+});
+
+route.all('/*', (req, res, next) => {
+ // passthrough to next route
+ if(req.path.startsWith('/api'))
+ return next();
+
+ // TODO: try to login
+ // TODO: role-based authorization
+ // TODO: show login page or page
+
+ res.end('500 - LEL');
+});
+
+module.exports = route;
diff --git a/bin/web/auth/views/index.pug b/bin/web/auth/views/index.pug
new file mode 100644
index 0000000..29f98ee
--- /dev/null
+++ b/bin/web/auth/views/index.pug
@@ -0,0 +1,66 @@
+//- variables
+- var appName = global['gds'].cfg.app.name || "SSObaseApp";
+- var title = "Dashboard";
+
+mixin navItem(name, id, symbol, href)
+ li(title=name, id=id)
+ a(href=href)
+ i.fa-fw(class=symbol)
+ span= name
+
+doctype(html)
+html(lang='en')
+ head
+ meta(charset="utf-8")
+ meta(http-equiv="X-UA-Compatible", content="IE=edge")
+ meta(name="viewport", content="width=device-width, initial-scale=1, shrink-to-fit=no")
+
+ meta(name="author", content="Ruben Meyer")
+ meta(name="description" content="auth.rxbn.de")
+ if(title)
+ title=""+appName+" - "+title
+ else
+ title=appName
+
+ block css
+ //- UIkit CSS
+ link(href="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.1.5/css/uikit.min.css", rel="stylesheet")
+
+ //- Custom Stylesheet
+ link(href="/res/css/stylesheet.css", rel="stylesheet")
+
+ //- Custom fonts for this template
+ link(href="https://use.fontawesome.com/releases/v5.1.1/css/all.css" integrity="sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ", crossorigin="anonymous", rel="stylesheet")
+ body
+ //- Navigation
+ nav(uk-navbar).uk-navbar-container
+ .uk-navbar-left.uk-margin-left
+ ul.uk-navbar-nav
+ li(title=appName)
+ a(href="/", style="text-transform: unset")
+ span=appName
+ .uk-navbar-right.uk-margin-right
+ ul.uk-navbar-nav
+ +navItem("Register", "register", "fas fa-user-plus", "/api/register")
+ +navItem("Login", "login", "far fa-arrow-alt-circle-right", "/api/login")
+ div
+ .uk-flex.uk-margin-medium-top.uk-margin-medium-bottom
+ div(class="uk-width-auto uk-width-1-4@s")
+ .uk-flex.uk-flex-auto.uk-flex-column.uk-flex-center.uk-margin-left.uk-margin-right
+ h1 Please login
+ p You need to be logged in to use this service
+ a(href="/api/login").uk-button.uk-button-default Login
+ div(class="uk-width-auto uk-width-1-4@s")
+ footer
+ .uk-text-center
+ small Copyright © Ruben Meyer 2019
+
+ block scripts
+ //- UIkit JS
+ script(src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.1.5/js/uikit.min.js")
+ script(src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.1.5/js/uikit-icons.min.js")
+
+ //- Custom scripts for this template
+ script(src="/res/js/locales.js")
+ script(src="/res/js/custom.js")
+
diff --git a/bin/web/module.js b/bin/web/module.js
new file mode 100644
index 0000000..350f1f4
--- /dev/null
+++ b/bin/web/module.js
@@ -0,0 +1,138 @@
+// init
+var methods = {};
+
+/**
+ * start web server
+ * @author Ruben Meyer
+ * @return {Void}
+ */
+methods.start = () => {
+ // init express framework
+ let express = require('express');
+ let session_handler = require('express-session');
+
+ // utilities
+ let fs = require('fs');
+ let path = require('path');
+ let mime = require('mime-types');
+
+ // app variable
+ let app = express();
+ app.set('view engine', 'pug'); // page engine
+ app.set('views', global['__dirname']+'/bin/web');
+
+ let bp = require('body-parser'); // POST Body parser
+ let cp = require('cookie-parser'); // Cookie handler
+
+ // Access Control Headers
+ app.use( (req, res, next) => {
+ res.set({
+ 'X-Powered-By': global['gds'].cfg
+ });
+ res.header("Access-Control-Allow-Origin", "*");
+ res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
+ next();
+ });
+
+ // BodyParser & CookieParser
+ app.use(bp.json());
+ app.use(bp.urlencoded({
+ extended: true
+ }));
+ app.use(cp(global['gds'].cfg.web.cookieKey));
+
+ // Pretty print
+ app.locals.pretty = true;
+
+ // Sessions
+ session_options = {
+ secret: global['gds'].cfg.web.sessionKey,
+ resave: false,
+ saveUninitialized: false, cookie: {}};
+ if(app.get('env') === 'production') {
+ session_options.cookie.secure = true;
+ }
+ app.use(session_handler(session_options));
+
+ //static files
+ app.use('/res', (req, res, next) => {
+ if(typeof global['gds'].cache.web == 'undefined') global['gds'].cache.web = {};
+
+ let dir = global['__dirname'] + '/res/web';
+
+
+ if(!req.session || !req.session.user) dir += '/auth';
+ else dir += '/app';
+
+ let joined_path = path.join(dir, /^[^?]+/.exec(req.url)[0]);
+
+ // path already cached; not exist
+ if(global['gds'].cache.web[joined_path] == false) {
+ res.status(404).end();
+ // path already cached; exist
+ } else if(global['gds'].cache.web[joined_path] == true){
+ let contentType = mime.contentType(path.extname(joined_path));
+ res.setHeader('Content-Type', contentType);
+ fs.createReadStream(joined_path).pipe(res);
+ // check path
+ } else {
+ fs.exists(joined_path, (exists) => {
+ global['gds'].cache.web[joined_path] = exists;
+ if(exists) {
+ let contentType = mime.contentType(path.extname(joined_path));
+ res.setHeader('Content-Type', contentType);
+
+ fs.createReadStream(joined_path).pipe(res);
+ } else {
+ res.status(404).end();
+ }
+ });
+ }
+ });
+
+ // web routes
+ app.use('/auth', require(global['__dirname']+'/bin/web/auth/routes/main'));
+ app.use('/', require(global['__dirname']+'/bin/web/app/routes/main'));
+
+ // start server
+ app.listen(global['gds'].cfg.web.port, () => {
+ global['modules'].logs.log("Server is listening on port: "+global['gds'].cfg.web.port);
+ });
+
+ // DEBUG OUTPUT: list all routes with HTTP method
+ setTimeout(function () {
+ function print (path, layer) {
+ if (layer.route) {
+ layer.route.stack.forEach(print.bind(null, path.concat(split(layer.route.path))))
+ } else if (layer.name === 'router' && layer.handle.stack) {
+ layer.handle.stack.forEach(print.bind(null, path.concat(split(layer.regexp))))
+ } else if (layer.method) {
+ console.log('%s /%s',
+ layer.method.toUpperCase(),
+ path.concat(split(layer.regexp)).filter(Boolean).join('/')
+ );
+ }
+ }
+
+ function split (thing) {
+ if (typeof thing === 'string') {
+ return thing.split('/')
+ } else if (thing.fast_slash) {
+ return ''
+ } else {
+ var match = thing.toString()
+ .replace('\\/?', '')
+ .replace('(?=\\/|$)', '$')
+ .match(/^\/\^((?:\\[.*+?^${}()|[\]\\\/]|[^.*+?^${}()|[\]\\\/])*)\$\//)
+
+ return match
+ ? match[1].replace(/\\(.)/g, '$1').split('/')
+ : ''
+ }
+ }
+
+ app._router.stack.forEach(print.bind(null, []))
+ }, 1500);
+};
+
+module.exports = methods;
diff --git a/res/web/auth/css/stylesheet.css b/res/web/auth/css/stylesheet.css
new file mode 100644
index 0000000..23684d8
--- /dev/null
+++ b/res/web/auth/css/stylesheet.css
@@ -0,0 +1,8 @@
+.uk-navbar a i {
+ margin-right: .2em;
+ transition: all .1s cubic-bezier(0.65, 0.05, 0.36, 1);
+}
+.uk-navbar a:hover i {
+ margin-top: .1em;
+ font-size: 1.4em;
+}