diff --git a/core/client b/core/client index 8688e659edcc..9a12450bae20 160000 --- a/core/client +++ b/core/client @@ -1 +1 @@ -Subproject commit 8688e659edcc56779178e545488a583d16d5f07d +Subproject commit 9a12450bae20a938f236b930993aa70437cd4193 diff --git a/core/server/api/shared/http.js b/core/server/api/shared/http.js index 2b685b7f2d4c..5d2330e78458 100644 --- a/core/server/api/shared/http.js +++ b/core/server/api/shared/http.js @@ -1,6 +1,7 @@ const debug = require('ghost-ignition').debug('api:shared:http'); const shared = require('../shared'); const models = require('../../models'); +const _ = require('lodash'); /** * @description HTTP wrapper. @@ -18,6 +19,16 @@ const http = (apiImpl) => { let apiKey = null; let integration = null; let user = null; + let rc_uid = null; + let rc_token = null; + + if(req.headers && req.headers.cookie) + _.forEach(req.headers.cookie.split(';'), (v)=>{ + if(v.includes('rc_uid')) + rc_uid = v.split('=')[1]; + if(v.includes('rc_token')) + rc_token = v.split('=')[1]; + }); if (req.api_key) { apiKey = { @@ -39,6 +50,8 @@ const http = (apiImpl) => { file: req.file, files: req.files, query: req.query, + rc_uid: rc_uid, + rc_token: rc_token, params: req.params, user: req.user, context: { @@ -93,7 +106,6 @@ const http = (apiImpl) => { docName: frame.docName, method: frame.method }; - next(err); }); }; diff --git a/core/server/api/v0.1/authentication.js b/core/server/api/v0.1/authentication.js index 284795bd5c02..8ee6fb3417b7 100644 --- a/core/server/api/v0.1/authentication.js +++ b/core/server/api/v0.1/authentication.js @@ -9,6 +9,7 @@ const Promise = require('bluebird'), mail = require('../../services/mail'), urlService = require('../../services/url'), localUtils = require('./utils'), + rcUtils = require('../v2/utils/rc-utils'), models = require('../../models'), web = require('../../web'), mailAPI = require('./mail'), @@ -61,14 +62,21 @@ function setupTasks(setupData) { let tasks; function validateData(setupData) { - return localUtils.checkObject(setupData, 'setup').then((checked) => { - const data = checked.setup[0]; - + const id = setupData['setup'][0].rc_id; + const token = setupData['setup'][0].rc_token; + const blogTitle = setupData['setup'][0].blogTitle; + const rcUrl = setupData['setup'][0].rc_url; + return rcUtils.checkAdmin(rcUrl, id, token).then((data) => { + const email = data.emails[0].address; return { name: data.name, - email: data.email, - password: data.password, - blogTitle: data.blogTitle, + rc_id: id, + rc_username: data.username, + profile_image: data.avatarUrl, + email: email, + password: "qwe123qwe123",//TODO set random password + blogTitle: blogTitle, + serverUrl: rcUrl, status: 'active' }; }); @@ -97,6 +105,7 @@ function setupTasks(setupData) { function doSettings(data) { const user = data.user, blogTitle = data.userData.blogTitle, + serverUrl = data.userData.serverUrl, context = {context: {user: data.user.id}}; let userSettings; @@ -106,6 +115,7 @@ function setupTasks(setupData) { } userSettings = [ + {key: 'server_url', value: serverUrl}, {key: 'title', value: blogTitle.trim()}, {key: 'description', value: common.i18n.t('common.api.authentication.sampleBlogDescription')} ]; @@ -437,6 +447,92 @@ authentication = { return pipeline(tasks, invitation); }, + /** + * ### Add Users + * @param {Object} invitation an invitation object + * @returns {Promise} + */ + addUser(invitation, option) { + let tasks, + invite; + const options = {context: {internal: true}, withRelated: ['roles']}; + const localOptions = {context: {internal: true}}; + // 1. if admin adds user, option. + // 2. if user creating account, invitation. + const rc_uid = option.rc_uid || invitation.user[0].rc_uid; + const rc_token = option.rc_token || invitation.user[0].rc_token; + + function validateInvitation(invitation) { + return models.Settings.findOne({ key: 'invite_only' }, localOptions) + .then((setting) => { + const inviteOnly = setting.attributes.value; + return rcUtils.getMe(rc_uid, rc_token) + .then((invitedBy) => { + if (!invitedBy.success) { + throw new common.errors.NotFoundError({ message: "User not found. Make Sure you are logged in on RC." }); + } + if (inviteOnly) { //Check that rc_uid is of Owner/Admin + return models.User.findOne({ rc_id: rc_uid, role: 'Owner'||'Administrator', status: 'all'}, options) + .then((user) => { + if (user) { + return invitation; + } else { + throw new common.errors.NotFoundError({ message: "You are not authorized to add new authors" }); + } + }); + } else {// Self Invitation + return invitation; + } + }); + }); + } + + function processInvitation(invitation) { + const data = invitation.user[0]; + return rcUtils.getUser(rc_uid, rc_token, data.rc_username) + .then((user) => { + if (user.success && user.user) { + const u = user.user; + if(!u.emails){ + throw new common.errors.NotFoundError({ message: "Cannot create account without email." }); + } + const email = u.emails[0].address; + const role = data.role.name||'Author'; + return models.Role.findOne({name: role}) + .then((r) => { + return models.User.add({ + rc_id: u._id, + rc_username: u.username, + email: email, + name: u.name, + password: "qwe123qwe123",//TODO Random password + roles: [r] + }, localOptions); + }); + } else { + throw new common.errors.NotFoundError({message: "User not found. Make Sure you are logged in on RC."}); + } + }); + } + + function formatResponse() { + return { + invitation: [ + {message: 'User Added'} + ] + }; + } + + tasks = [ + assertSetupCompleted(true), + validateInvitation, + processInvitation, + formatResponse + ]; + + return pipeline(tasks, invitation); + }, + /** * ### Check for invitation * @param {Object} options @@ -513,7 +609,7 @@ authentication = { }, /** - * Executes the setup tasks and sends an email to the owner + * Executes the setup tasks and get access_token and user_id and verify with rc-utils * @param {Object} setupDetails * @return {Promise} a user api payload */ @@ -566,7 +662,8 @@ authentication = { tasks = [ assertSetupCompleted(false), doSetup, - sendNotification, + // TODO: add mail service from RC. + // sendNotification, formatResponse ]; diff --git a/core/server/api/v0.1/index.js b/core/server/api/v0.1/index.js index 17ed4f112536..7667c92675af 100644 --- a/core/server/api/v0.1/index.js +++ b/core/server/api/v0.1/index.js @@ -4,7 +4,7 @@ // Ghost's JSON API is integral to the workings of Ghost, regardless of whether you want to access data internally, // from a theme, an app, or from an external app, you'll use the Ghost JSON API to do so. -const {isEmpty} = require('lodash'); +const {isEmpty, forEach} = require('lodash'); const Promise = require('bluebird'); const models = require('../../models'); const urlService = require('../../services/url'); @@ -272,6 +272,14 @@ const http = (apiMethod) => { } }); + if(req.headers && req.headers.cookie) + forEach(req.headers.cookie.split(';'), (v)=>{ + if(v.includes('rc_uid')) + options.rc_uid = v.split('=')[1]; + if(v.includes('rc_token')) + options.rc_token = v.split('=')[1]; + }); + if (req.files) { options.files = req.files; } diff --git a/core/server/api/v2/index.js b/core/server/api/v2/index.js index 11e7872e6908..5a99230ee1b3 100644 --- a/core/server/api/v2/index.js +++ b/core/server/api/v2/index.js @@ -47,6 +47,10 @@ module.exports = { return shared.pipeline(require('./invites'), localUtils); }, + get rcapi() { + return shared.pipeline(require('./rcapi'), localUtils); + }, + get mail() { return shared.pipeline(require('./mail'), localUtils); }, diff --git a/core/server/api/v2/rcapi.js b/core/server/api/v2/rcapi.js new file mode 100644 index 000000000000..3c23b5a3ce30 --- /dev/null +++ b/core/server/api/v2/rcapi.js @@ -0,0 +1,33 @@ +const rcUtils = require('./utils/rc-utils'); +const ALLOWED_INCLUDES = []; + +module.exports = { + docName: 'rcapi', + + browse: { + options: [ + 'include', + 'name', + 'page', + 'limit', + 'fields', + 'filter', + 'order', + 'debug' + ], + validation: { + options: { + include: ALLOWED_INCLUDES, + } + }, + permissions: false, + query(frame) { + let username = frame.options.name; + + return rcUtils.validateUser(frame.original.rc_uid, frame.original.rc_token, username) + .then((user) =>{ + return user; + }) + } + } +}; diff --git a/core/server/api/v2/session.js b/core/server/api/v2/session.js index 63b0405d7f85..04b5cb5a0d92 100644 --- a/core/server/api/v2/session.js +++ b/core/server/api/v2/session.js @@ -1,6 +1,7 @@ const Promise = require('bluebird'); const common = require('../../lib/common'); const models = require('../../models'); +const rcUtils = require('../v2/utils/rc-utils'); const auth = require('../../services/auth'); const session = { @@ -13,26 +14,39 @@ const session = { */ return models.User.findOne({id: options.context.user}); }, + add(object) { - if (!object || !object.username || !object.password) { + if (!object || !object.rc_id || !object.rc_token) { return Promise.reject(new common.errors.UnauthorizedError({ message: common.i18n.t('errors.middleware.auth.accessDenied') })); } - return models.User.check({ - email: object.username, - password: object.password + return models.User.findOne({ + rc_id: object.rc_id }).then((user) => { - return Promise.resolve((req, res, next) => { - req.brute.reset(function (err) { - if (err) { - return next(err); + if (!user){ + throw new common.errors.UnauthorizedError({ + message: common.i18n.t('errors.middleware.auth.accessDenied') + }); + } + return rcUtils.getMe(object.rc_id, object.rc_token) + .then((u) => { + if (!u.success) { + throw new common.errors.UnauthorizedError({ + message: common.i18n.t('errors.middleware.auth.accessDenied') + }); } - req.user = user; - auth.session.createSession(req, res, next); + return Promise.resolve((req, res, next) => { + req.brute.reset(function (err) { + if (err) { + return next(err); + } + req.user = user; + auth.session.createSession(req, res, next); + }); + }); }); - }); }).catch((err) => { throw new common.errors.UnauthorizedError({ message: common.i18n.t('errors.middleware.auth.accessDenied'), @@ -40,6 +54,7 @@ const session = { }); }); }, + delete() { return Promise.resolve((req, res, next) => { auth.session.destroySession(req, res, next); diff --git a/core/server/api/v2/settings.js b/core/server/api/v2/settings.js index b0ffa13a5d6e..13d0a1d95b0d 100644 --- a/core/server/api/v2/settings.js +++ b/core/server/api/v2/settings.js @@ -44,6 +44,34 @@ module.exports = { } }, + // TODO: find a better way to check if this setting + // Maybe add a setting in RC and keep both in sync. + inviteOnly: { + options: [], + validation: { + options: { + include: [] + } + }, + permissions: false, + query(frame) { + const key = 'invite_only'; + let setting = settingsCache.get(key, {resolve: false}); + + if (!setting) { + return Promise.reject(new common.errors.NotFoundError({ + message: common.i18n.t('errors.api.settings.problemFindingSetting', { + key: key + }) + })); + } + + return { + [key]: setting + }; + } + }, + read: { options: ['key'], validation: { diff --git a/core/server/api/v2/users.js b/core/server/api/v2/users.js index bbfbb3914d2e..8214280d8325 100644 --- a/core/server/api/v2/users.js +++ b/core/server/api/v2/users.js @@ -1,6 +1,7 @@ const Promise = require('bluebird'); const common = require('../../lib/common'); const models = require('../../models'); +const rcUtils = require('./utils/rc-utils'); const permissionsService = require('../../services/permissions'); const ALLOWED_INCLUDES = ['count.posts', 'permissions', 'roles', 'roles.permissions']; const UNSAFE_ATTRS = ['status', 'roles']; @@ -31,6 +32,35 @@ module.exports = { } }, + // TODO: find a better way to check if this setting + // Maybe add a setting in RC and keep both in sync. + exist: { + options: [], + data: [ + 'user' + ], + validation: { + options: { + include: [] + } + }, + permissions: false, + query(frame) { + const option = frame.data.user[0]; + const localOptions = {context: {internal: true}}; + return rcUtils.getMe(option.rc_uid, option.rc_token) + .then((u) => { + if (!u.success) { + throw new common.errors.NotFoundError({ message: "User not found. Make Sure you are logged in on RC." }); + } + return models.User.findOne({ rc_id: u._id }, localOptions) + .then((model) => { + return model; + }); + }); + } + }, + read: { options: [ 'include', diff --git a/core/server/api/v2/utils/rc-utils.js b/core/server/api/v2/utils/rc-utils.js new file mode 100644 index 000000000000..5acff7ff4224 --- /dev/null +++ b/core/server/api/v2/utils/rc-utils.js @@ -0,0 +1,143 @@ +const Promise = require('bluebird'); +const request = require('request'); +const { forEach } = require('lodash'); +const models = require('../../../models'); +const auth = require('../../../services/auth'); +const settingsCache = require('../../../services/settings/cache'); +const common = require('../../../lib/common'); + +function getRCUrl() { + return settingsCache.get('server_url'); +} + +function buildMeUrl(url = null) { + const base = url || getRCUrl(); + return base + '/api/v1/me'; +} + +function buildUserQuery(username) { + return getRCUrl() + '/api/v1/users.info?' + `username=${username}`; +} + +function getHeader(id, token) { + return { + 'X-Auth-Token': token, + 'X-User-Id': id + }; +} + +function getIdToken(req) { + let id, token; + forEach(req.headers.cookie.split(';'), (v) => { + if (v.includes('rc_uid')) + id = v.split('=')[1]; + if (v.includes('rc_token')) + token = v.split('=')[1]; + }); + return { id, token }; +} + +module.exports = { + checkAdmin(url, id, token) { + let user; + return new Promise((resolve) => { + request.get({ url: buildMeUrl(url), headers: getHeader(id, token) }, function (e, r, body) { + user = JSON.parse(body); + if (user.success) { + if (user.roles.indexOf('admin') == -1) { + //callee is not admin on RC + return Promise.reject(new common.errors.GhostError({ + message: 'Callee is not an admin, cannot Setup Ghost' + })); + } + } else { + return Promise.reject(new common.errors.GhostError({ + message: 'Unable to fetch the details' + })); + } + resolve(user); + }); + }) + }, + + getUser(id, token, username) { + let user; + return new Promise((resolve) => { + request.get({ url: buildUserQuery(username), headers: getHeader(id, token) }, function (e, r, body) { + let result; + if (body) + result = JSON.parse(body); + if (result && result.success) { + user = result; + } else { + user = { + success: false, + }; + } + resolve(user); + }); + }); + }, + + getMe(id, token) { + let user; + return new Promise((resolve) => { + request.get({ url: buildMeUrl(), headers: getHeader(id, token) }, function (e, r, body) { + let result; + if (body) + result = JSON.parse(body); + if (result && result.success) { + user = result; + } else { + user = { + success: false, + }; + } + resolve(user); + }); + }); + }, + + validateUser(id, token, userName) { + let user; + return new Promise((resolve) => { + request.get({ url: buildUserQuery(userName), headers: getHeader(id, token) }, function (e, r, body) { + let result; + if (body) + result = JSON.parse(body); + if (result && result.success) { + u = result.user; + user = { + exist: true, + rid: u._id, + username: u.username, + }; + } else { + user = { + exist: false, + }; + } + resolve(user); + }); + }); + }, + + createSession(req) { + const { id, token } = getIdToken(req); + if (!id || !token) + return req; + return models.User.findOne({ rc_id: id }).then((user) => { + if (!user) { + return req; + } + return this.getMe(id, token) + .then((u) => { + if (!u.success) { + return req; + } + req.user = user; + return req; + }); + }); + } +}; diff --git a/core/server/api/v2/utils/serializers/output/index.js b/core/server/api/v2/utils/serializers/output/index.js index c709aae653b6..5ddf4c612ee6 100644 --- a/core/server/api/v2/utils/serializers/output/index.js +++ b/core/server/api/v2/utils/serializers/output/index.js @@ -39,6 +39,10 @@ module.exports = { return require('./invites'); }, + get rcapi() { + return require('./rcapi'); + }, + get settings() { return require('./settings'); }, diff --git a/core/server/api/v2/utils/serializers/output/rcapi.js b/core/server/api/v2/utils/serializers/output/rcapi.js new file mode 100644 index 000000000000..eddd0b94eec4 --- /dev/null +++ b/core/server/api/v2/utils/serializers/output/rcapi.js @@ -0,0 +1,17 @@ +const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:rcapi'); + +module.exports = { + all(models, apiConfig, frame) { + debug('all'); + + if (!models) { + return; + } + + frame.response = { + rc_users: [models] + }; + + debug(frame.response); + } +}; diff --git a/core/server/api/v2/utils/serializers/output/settings.js b/core/server/api/v2/utils/serializers/output/settings.js index f750a92e9e14..dc5cae6e6c5d 100644 --- a/core/server/api/v2/utils/serializers/output/settings.js +++ b/core/server/api/v2/utils/serializers/output/settings.js @@ -50,6 +50,10 @@ module.exports = { this.browse(...arguments); }, + inviteOnly() { + this.browse(...arguments); + }, + edit(models, apiConfig, frame) { const settingsKeyedJSON = _.keyBy(_.invokeMap(models, 'toJSON'), 'key'); this.browse(settingsKeyedJSON, apiConfig, frame); diff --git a/core/server/api/v2/utils/serializers/output/users.js b/core/server/api/v2/utils/serializers/output/users.js index 5936b8ba67f0..85e1c835ee34 100644 --- a/core/server/api/v2/utils/serializers/output/users.js +++ b/core/server/api/v2/utils/serializers/output/users.js @@ -14,6 +14,19 @@ module.exports = { debug(frame.response); }, + exist(model, apiConfig, frame) { + debug('exist'); + + frame.response = { + users: [{ + exist: model?true:false, + status: model?model.status:'inactive' + }] + }; + + debug(frame.response); + }, + read(model, apiConfig, frame) { debug('read'); diff --git a/core/server/api/v2/utils/validators/input/schemas/posts.json b/core/server/api/v2/utils/validators/input/schemas/posts.json index cdb2bee83a4f..72418d094a71 100644 --- a/core/server/api/v2/utils/validators/input/schemas/posts.json +++ b/core/server/api/v2/utils/validators/input/schemas/posts.json @@ -17,6 +17,13 @@ "type": "string", "maxLength": 191 }, + "room_name": { + "type": ["string", "null"], + "maxLength": 980 + }, + "to_announce": { + "type": "boolean" + }, "mobiledoc": { "type": ["string", "null"], "maxLength": 1000000000 diff --git a/core/server/data/schema/default-settings.json b/core/server/data/schema/default-settings.json index 32291c95b1a9..83369938eb4f 100644 --- a/core/server/data/schema/default-settings.json +++ b/core/server/data/schema/default-settings.json @@ -106,6 +106,51 @@ "defaultValue": "casper" } }, + "rc_settings": { + "server_url": { + "defaultValue": "http://localhost:4000" + }, + "room": { + "defaultValue": "articles", + "validations": { + "isLength": { + "max": 150 + } + } + }, + "room_id": { + "defaultValue": "ridOfAboveRoom", + "validations": { + "isLength": { + "max": 17 + } + } + }, + "is_announced": { + "defaultValue": "false", + "validations": { + "isIn": [["true", "false"]] + } + }, + "is_authors_rooms": { + "defaultValue": "false", + "validations": { + "isIn": [["true", "false"]] + } + }, + "is_comments": { + "defaultValue": "false", + "validations": { + "isIn": [["true", "false"]] + } + }, + "invite_only": { + "defaultValue": "false", + "validations": { + "isIn": [["true", "false"]] + } + } + }, "private": { "is_private": { "defaultValue": "false", diff --git a/core/server/data/schema/fixtures/fixtures.json b/core/server/data/schema/fixtures/fixtures.json index 904fe2dcc880..7e0a3a24e96d 100644 --- a/core/server/data/schema/fixtures/fixtures.json +++ b/core/server/data/schema/fixtures/fixtures.json @@ -429,6 +429,8 @@ "entries": [ { "id": 1, + "rc_id": "fakeid", + "rc_username": "fakeusername", "name": "Ghost", "email": "ghost@example.com", "status": "inactive", @@ -436,6 +438,8 @@ }, { "id": "5951f5fca366002ebd5dbef7", + "rc_id": "ghostrcfakeid", + "rc_username": "ghostfakeusername", "name": "Ghost", "email": "ghost-author@example.com", "status": "active", diff --git a/core/server/data/schema/schema.js b/core/server/data/schema/schema.js index 7f0cf495aa96..d78218d32abc 100644 --- a/core/server/data/schema/schema.js +++ b/core/server/data/schema/schema.js @@ -7,10 +7,13 @@ * Text = length 65535 (64 KiB) * Long text = length 1,000,000,000 */ +//TODO change NULLABLE, UNIQUE, module.exports = { posts: { id: {type: 'string', maxlength: 24, nullable: false, primary: true}, uuid: {type: 'string', maxlength: 36, nullable: false, validations: {isUUID: true}}, + room_id: {type: 'string', maxlength: 17, nullable: true, unique: false}, + room_name: {type: 'string', maxlength: 980, nullable: true, unique: false}, title: {type: 'string', maxlength: 2000, nullable: false, validations: {isLength: {max: 255}}}, slug: {type: 'string', maxlength: 191, nullable: false, unique: true}, mobiledoc: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true}, @@ -61,6 +64,9 @@ module.exports = { }, users: { id: {type: 'string', maxlength: 24, nullable: false, primary: true}, + // rc_username can change but rc_id will never change + rc_id: {type: 'string', maxlength: 17, nullable: false, unique: true}, + rc_username: {type: 'string', maxlength: 17, nullable: false, unique: true}, name: {type: 'string', maxlength: 191, nullable: false}, slug: {type: 'string', maxlength: 191, nullable: false, unique: true}, ghost_auth_access_token: {type: 'string', maxlength: 32, nullable: true}, @@ -148,7 +154,7 @@ module.exports = { maxlength: 50, nullable: false, defaultTo: 'core', - validations: {isIn: [['core', 'blog', 'theme', 'app', 'plugin', 'private', 'members']]} + validations: {isIn: [['core', 'blog', 'theme', 'app', 'plugin', 'private', 'members', 'rc_settings']]} }, created_at: {type: 'dateTime', nullable: false}, created_by: {type: 'string', maxlength: 24, nullable: false}, diff --git a/core/server/lib/constants.js b/core/server/lib/constants.js index 28a5912180f3..bd45920cf271 100644 --- a/core/server/lib/constants.js +++ b/core/server/lib/constants.js @@ -9,6 +9,7 @@ module.exports = { ONE_DAY_MS: 86400000, ONE_WEEK_MS: 604800000, ONE_MONTH_MS: 2628000000, + THREE_MONTH_MS: 7795200000, SIX_MONTH_MS: 15768000000, ONE_YEAR_MS: 31536000000 }; diff --git a/core/server/services/auth/session/middleware.js b/core/server/services/auth/session/middleware.js index e702122d3711..34f347ec9674 100644 --- a/core/server/services/auth/session/middleware.js +++ b/core/server/services/auth/session/middleware.js @@ -3,6 +3,7 @@ const session = require('express-session'); const common = require('../../../lib/common'); const constants = require('../../../lib/constants'); const config = require('../../../config'); +const rcUtils = require('../../../api/v2/utils/rc-utils'); const settingsCache = require('../../settings/cache'); const models = require('../../../models'); const SessionStore = require('./store'); @@ -37,9 +38,9 @@ const getSession = (req, res, next) => { saveUninitialized: false, name: 'ghost-admin-api-session', cookie: { - maxAge: constants.SIX_MONTH_MS, - httpOnly: true, - path: urlService.utils.getSubdir() + '/ghost', + maxAge: constants.THREE_MONTH_MS, + httpOnly: false, + path: urlService.utils.getSubdir() + '/', sameSite: 'lax', secure: urlService.utils.isSSL(config.get('url')) } @@ -85,10 +86,11 @@ const cookieCsrfProtection = (req) => { const origin = getOrigin(req); - if (req.session.origin !== origin) { + // Check the origin allow Ghost and RC server_url + if (req.session.origin !== origin && settingsCache.get('server_url') !== origin) { throw new common.errors.BadRequestError({ message: common.i18n.t('errors.middleware.auth.mismatchedOrigin', { - expected: req.session.origin, + expected: req.session.origin + ' OR ' + settingsCache.get('server_url'), actual: origin }) }); @@ -116,7 +118,27 @@ const authenticate = (req, res, next) => { if (!req.session || !req.session.user_id) { req.user = null; - return next(); + return rcUtils.createSession(req) + .then((req) => { + if(req.user) { + getSession(req, res, function (err) { + if (err) { + return next(err); + } + const origin = getOrigin(req); + if (!origin) { + return next(new common.errors.BadRequestError({ + message: common.i18n.t('errors.middleware.auth.unknownOrigin') + })); + } + req.session.user_id = req.user.id; + req.session.origin = origin; + req.session.user_agent = req.get('user-agent'); + req.session.ip = req.ip; + }); + } + return next(); + }); } models.User.findOne({id: req.session.user_id}) diff --git a/core/server/web/api/v2/admin/routes.js b/core/server/web/api/v2/admin/routes.js index 412bcefe94ce..63b783732991 100644 --- a/core/server/web/api/v2/admin/routes.js +++ b/core/server/web/api/v2/admin/routes.js @@ -67,6 +67,7 @@ module.exports = function apiRoutes() { router.get('/settings', mw.authAdminApi, http(apiv2.settings.browse)); router.get('/settings/:key', mw.authAdminApi, http(apiv2.settings.read)); + router.get('/invitesetting', http(apiv2.settings.inviteOnly)); router.put('/settings', mw.authAdminApi, http(apiv2.settings.edit)); // ## Users @@ -75,7 +76,8 @@ module.exports = function apiRoutes() { router.get('/users/slug/:slug', mw.authAdminApi, http(apiv2.users.read)); // NOTE: We don't expose any email addresses via the public api. router.get('/users/email/:email', mw.authAdminApi, http(apiv2.users.read)); - + // Public call: if user exist, based on rc_uid, and rc_token + router.get('/userexist', http(apiv2.users.exist)); router.put('/users/password', mw.authAdminApi, http(apiv2.users.changePassword)); router.put('/users/owner', mw.authAdminApi, http(apiv2.users.transferOwnership)); router.put('/users/:id', mw.authAdminApi, http(apiv2.users.edit)); @@ -189,6 +191,7 @@ module.exports = function apiRoutes() { ); router.put('/authentication/passwordreset', shared.middlewares.brute.globalBlock, api.http(api.authentication.resetPassword)); router.post('/authentication/invitation', api.http(api.authentication.acceptInvitation)); + router.post('/authentication/adduser', api.http(api.authentication.addUser)); router.get('/authentication/invitation', api.http(api.authentication.isInvitation)); router.post('/authentication/setup', api.http(api.authentication.setup)); router.put('/authentication/setup', mw.authAdminApi, api.http(api.authentication.updateSetup)); @@ -209,6 +212,9 @@ module.exports = function apiRoutes() { router.post('/invites', mw.authAdminApi, http(apiv2.invites.add)); router.del('/invites/:id', mw.authAdminApi, http(apiv2.invites.destroy)); + // ## RC Api + router.get('/rcapi', mw.authAdminApi, http(apiv2.rcapi.browse)); // No need for checking persmissions + // ## Redirects (JSON based) router.get('/redirects/json', mw.authAdminApi, http(apiv2.redirects.download)); router.post('/redirects/json',