initial commit.

This commit is contained in:
mntmn
2017-04-07 01:29:05 +02:00
commit 7ff2926578
258 changed files with 83743 additions and 0 deletions

19
middlewares/404.js Normal file
View File

@@ -0,0 +1,19 @@
'use strict';
require('../models/schema');
var config = require('config');
module.exports = (req, res, next) => {
var err = new Error('Not Found');
if (req.accepts('text/html')) {
res.status(404).render('not_found', {
title: 'Page Not Found.'
});
} else if (req.accepts('application/json')) {
res.status(404).json({
"error": "not_found"
});
} else {
res.status(404).send("Not Found.");
}
}

10
middlewares/500.js Normal file
View File

@@ -0,0 +1,10 @@
'use strict';
module.exports = (err, req, res, next) => {
console.error(err);
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
}

View File

@@ -0,0 +1,55 @@
'use strict';
require('../models/schema');
var config = require('config');
const redis = require('../helpers/redis');
var saveAction = (actionKey, object) => {
if (object.constructor.modelName == "Space")
return;
let attr = {
action: actionKey,
space: object.space_id || object.space,
user: object.user_id || object.user,
editor_name: object.editor_name,
object: object.toJSON()
};
let action = new Action(attr);
action.save(function(err) {
if (err)
console.error("saved create action err:", err);
});
};
module.exports = (req, res, next) => {
res.header("Cache-Control", "no-cache");
req['channelId'] = req.headers['x-spacedeck-channel'];
req['spacePassword'] = req.headers['x-spacedeck-spacepassword'];
req['spaceAuth'] = req.query['spaceAuth'] || req.headers['x-spacedeck-space-auth'];
res['distributeCreate'] = function(model, object) {
if (!object) return;
redis.sendMessage("create", model, object.toJSON(), req.channelId);
this.status(201).json(object.toJSON());
saveAction("create", object);
};
res['distributeUpdate'] = function(model, object) {
if (!object) return;
redis.sendMessage("update", model, object.toJSON(), req.channelId);
this.status(200).json(object.toJSON());
saveAction("update", object);
};
res['distributeDelete'] = function(model, object) {
if (!object) return;
redis.sendMessage("delete", model, object.toJSON(), req.channelId);
this.sendStatus(204);
saveAction("delete", object);
};
next();
}

View File

@@ -0,0 +1,22 @@
'use strict';
require('../models/schema');
var config = require('config');
module.exports = (req, res, next) => {
var artifactId = req.params.artifact_id;
Artifact.findOne({
"_id": artifactId
}, (err, artifact) => {
if (err) {
res.status(400).json(err);
} else {
if (artifact) {
req['artifact'] = artifact;
next();
} else {
res.sendStatus(404);
}
}
});
};

48
middlewares/cors.js Normal file
View File

@@ -0,0 +1,48 @@
'use strict';
require('../models/schema');
const config = require('config');
const url = require('url');
function respond(origin, req, res, next) {
res.header('Access-Control-Allow-Origin', origin);
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Max-Age', 60 * 60 * 24);
res.header('Access-Control-Expose-Headers', 'Accepts, Content-Type, X-Spacedeck-Space-Role, X-Spacedeck-Channel, X-Spacedeck-Spacepassword, X-Spacedeck-Auth, X-Spacedeck-Space-Auth');
res.header('Access-Control-Allow-Headers', 'Accepts, Accept-Language, Accept-Encoding, Accept-Language, Content-Type, X-Spacedeck-Space-Auth, X-Spacedeck-Space-Role, X-Spacedeck-Channel, X-Spacedeck-Spacepassword, X-Spacedeck-Auth');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
if (req.method == 'OPTIONS') {
res.sendStatus(204);
} else {
next();
}
}
module.exports = (req, res, next) => {
const origin = req.headers.origin;
if (origin) {
const parsedUrl = url.parse(origin, true, true);
// FIXME
if (parsedUrl.hostname == "cdn.spacedeck.com") {
res.header('Cache-Control', "max-age");
res.header('Expires', "30d");
res.removeHeader("Pragma");
respond(origin, req, res, next);
} else {
Team.getTeamForHost(parsedUrl.hostname, (err, team, subdomain) => {
if (team) {
respond(origin, req, res, next);
} else {
next();
}
});
}
} else {
next();
}
}

View File

@@ -0,0 +1,16 @@
'use strict';
module.exports = (req, res, next) => {
res.bad_request = (msg) => {
if (req.accepts('text/html')) {
res.status(400).render('error', {
message: msg
});
} else {
res.status(400).json({
"error": msg
});
}
}
next();
}

17
middlewares/i18n.js Normal file
View File

@@ -0,0 +1,17 @@
'use strict';
require('../models/schema');
var config = require('config');
module.exports = (req, res, next) => {
req.i18n.setLocale(req.i18n.prefLocale);
if (req.cookies.spacedeck_locale) {
req.i18n.setLocaleFromCookie();
}
if (req.user && req.user.preferences.language) {
req.i18n.setLocale(req.user.preferences.language);
}
next();
}

38
middlewares/setuser.js Normal file
View File

@@ -0,0 +1,38 @@
'use strict';
require('../models/schema');
var config = require('config');
module.exports = (req, res, next) => {
const token = req.cookies["sdsession"];
if (token && token != "null" && token !== null) {
User.findOne({
"sessions.token": token
}).populate('team').exec((err, user) => {
if (!user) {
// FIXME
var domain = "localhost";
res.clearCookie('sdsession', {
domain: domain
});
if (req.accepts("text/html")) {
res.redirect("/");
} else if (req.accepts('application/json')) {
res.status(403).json({
"error": "token_not_found"
});
} else {
res.redirect("/");
}
} else {
req["token"] = token;
req["user"] = user;
next();
}
});
} else {
next();
}
}

View File

@@ -0,0 +1,160 @@
'use strict';
require('../models/schema');
var config = require('config');
module.exports = (req, res, next) => {
let spaceId = req.params.id;
let finalizeReq = (space, role) => {
if (role === "none") {
res.status(403).json({
"error": "access denied"
});
} else {
req['space'] = space;
req['spaceRole'] = role;
res.header("x-spacedeck-space-role", req['spaceRole']);
next();
}
};
var rolePerUser = (originalSpace, user, cb) => {
originalSpace.path = [];
if (originalSpace._id.equals(req.user.home_folder_id) || (originalSpace.creator && originalSpace.creator._id.equals(req.user._id))) {
cb("admin");
} else {
var findMembershipsForSpace = function(space, allMemberships, prevRole) {
Membership.find({
"space": space._id
}, function(err, parentMemberships) {
var currentMemberships = parentMemberships.concat(allMemberships);
if (space.parent_space_id) {
Space.findOne({
"_id": space.parent_space_id
}, function(err, parentSpace) {
findMembershipsForSpace(parentSpace, currentMemberships, prevRole);
});
} else {
// reached the top
var role = prevRole;
space.memberships = currentMemberships;
if(role == "none"){
if(originalSpace.access_mode == "public") {
role = "viewer";
}
}
currentMemberships.forEach(function(m, i) {
if (m.user && m.user.equals(user._id)) {
role = m.role;
}
});
cb(role);
}
});
};
findMembershipsForSpace(originalSpace, [], "none");
}
};
var finalizeAnonymousLogin = function(space, spaceAuth) {
var role = "none";
if (spaceAuth && (spaceAuth === space.edit_hash)) {
role = "editor";
} else {
if (space.access_mode == "public") {
role = "viewer";
} else {
role = "none";
}
}
if (req.user) {
rolePerUser(space, req.user, function(newRole) {
if (newRole == "admin" && (role == "editor" || role == "viewer")) {
finalizeReq(space, newRole);
} else if (newRole == "editor" && (role == "viewer")) {
finalizeReq(space, newRole);
} else {
finalizeReq(space, role);
}
});
} else {
finalizeReq(space, role);
}
};
var userMapping = {
'_id': 1,
'nickname': 1,
'email': 1
};
Space.findOne({
"_id": spaceId
}).populate("creator", userMapping).exec(function(err, space) {
if (err) {
res.status(400).json(err);
} else {
if (space) {
if (space.access_mode == "public") {
if (space.password) {
if (req.spacePassword) {
if (req.spacePassword === space.password) {
finalizeAnonymousLogin(space, req["spaceAuth"]);
} else {
res.status(403).json({
"error": "password_wrong"
});
}
} else {
res.status(401).json({
"error": "password_required"
});
}
} else {
finalizeAnonymousLogin(space, req["spaceAuth"]);
}
} else {
// special permission for screenshot/pdf export from backend
if (req.query['api_token'] && req.query['api_token'] == config.get('phantom_api_secret')) {
finalizeReq(space, "viewer");
return;
}
if (req.user) {
rolePerUser(space, req.user, function(role) {
if (role == "none") {
finalizeAnonymousLogin(space, req["spaceAuth"]);
} else {
finalizeReq(space, role);
}
});
} else {
if (req.spaceAuth && space.edit_hash) {
finalizeAnonymousLogin(space, req["spaceAuth"]);
} else {
res.status(403).json({
"error": "auth_required"
});
}
}
}
} else {
res.status(404).json({
"error": "space_not_found"
});
}
}
});
}

33
middlewares/subdomain.js Normal file
View File

@@ -0,0 +1,33 @@
'use strict';
require('../models/schema');
var config = require('config');
module.exports = (req, res, next) => {
let host = req.headers.host;
Team.getTeamForHost(host, (err, team, subdomain) => {
if (subdomain) {
if (!err && team) {
req.subdomainTeam = team;
req.subdomain = subdomain;
next()
} else {
if (req.accepts('text/html')) {
res.status(404).render('not_found', {
title: 'Page Not Found.'
});
} else if (req.accepts('application/json')) {
res.status(404).json({
"error": "not_found"
});
} else {
res.status(404).render('not_found', {
title: 'Page Not Found.'
});
}
}
} else {
next();
}
});
}

View File

@@ -0,0 +1,23 @@
'use strict';
require('../models/schema');
var config = require('config');
module.exports = (req, res, next) => {
if (req.user) {
var isAdmin = req.user.team.admins.indexOf(req.user._id) >= 0;
var correctMethod = req.method == "GET" || (req.method == "DELETE" || req.method == "PUT" || req.method == "POST");
if (correctMethod && isAdmin) {
next();
} else {
res.status(403, {
"error": "not authorized"
});
}
} else {
res.status(403, {
"error": "not logged in"
});
}
}

31
middlewares/templates.js Normal file
View File

@@ -0,0 +1,31 @@
'use strict';
require('../models/schema');
var config = require('config');
var _ = require('underscore');
module.exports = (req, res, next) => {
res.oldRender = res.render;
res.render = function(template, params) {
var team = req.subdomainTeam;
if (team) {
team = _.pick(team.toObject(), ['_id', 'name', 'subdomain', 'avatar_original_uri']);
} else {
team = null;
}
const addParams = {
locale: req.i18n.locale,
config: config,
subdomain_team: team,
user: req.user,
csrf_token: "",
socket_auth: req.token
};
const all = _.extend(params, addParams);
res.oldRender(template, all);
};
next();
}