Skip to content

Commit

Permalink
Merge branch 'next' into release
Browse files Browse the repository at this point in the history
  • Loading branch information
ar2rsawseen committed Jul 18, 2019
2 parents 5b24ca0 + d267d93 commit 42728a9
Show file tree
Hide file tree
Showing 28 changed files with 754 additions and 67 deletions.
2 changes: 1 addition & 1 deletion api/lib/countly.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ function getPeriodObject() {
fromDate.setTimezone(_appTimezone);
toDate.setTimezone(_appTimezone);

if (fromDate === toDate) {
if (fromDate.getTime() === toDate.getTime()) {
cycleDuration = moment.duration(1, "day");
Object.assign(periodObject, {
dateString: "D MMM, HH:mm",
Expand Down
8 changes: 6 additions & 2 deletions api/parts/mgmt/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
var usersApi = {},
common = require('./../../utils/common.js'),
mail = require('./mail.js'),
countlyConfig = require('./../../../frontend/express/config.js'),
plugins = require('../../../plugins/pluginManager.js');

//for password checking when deleting own account. Could be removed after merging with next
Expand Down Expand Up @@ -238,7 +239,9 @@ usersApi.createUser = function(params) {
**/
async function createUser() {
var passwordNoHash = newMember.password;
newMember.password = await common.argon2Hash(newMember.password);
var secret = countlyConfig.passwordSecret || "";

newMember.password = await common.argon2Hash(newMember.password + secret);
newMember.password_changed = 0;
newMember.created_at = Math.floor(((new Date()).getTime()) / 1000); //TODO: Check if UTC
newMember.admin_of = newMember.admin_of || [];
Expand Down Expand Up @@ -384,8 +387,9 @@ usersApi.updateUser = async function(params) {
}

if (updatedMember.password) {
var secret = countlyConfig.passwordSecret || "";
passwordNoHash = updatedMember.password;
updatedMember.password = await common.argon2Hash(updatedMember.password);
updatedMember.password = await common.argon2Hash(updatedMember.password + secret);
if (params.member._id !== params.qstring.args.user_id) {
updatedMember.password_changed = 0;
}
Expand Down
44 changes: 41 additions & 3 deletions api/utils/authorizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
var authorizer = {};
var common = require("./common.js");
var crypto = require("crypto");
const log = require('./log.js')('core:authorizer');


/**
Expand Down Expand Up @@ -169,6 +170,7 @@ authorizer.extend_token = function(options) {
* @param {object} options - options for the task
* @param {object} options.db - database connection
* @param {string} options.token - token to validate
* @param {string} options.qstring - params.qstring. If not passed and there is limitation for this token on params - token will not be valid
* @param {function} options.callback - function called when verifying was completed or errored, providing error object as first param and true as second if extending successful
* @param {boolean} return_owner states if in callback owner shold be returned. If return_owner==false, returns true or false.
*/
Expand All @@ -194,10 +196,44 @@ var verify_token = function(options, return_owner) {
}
valid_endpoint = false;
if (options.req_path !== "") {
var my_regexp = "";
for (var p = 0; p < res.endpoint.length; p++) {
var my_regexp = new RegExp(res.endpoint[p]);
if (my_regexp.test(options.req_path)) {
valid_endpoint = true;
if (res.endpoint[p] && res.endpoint[p].endpoint) {
var missing_param = false;
if (res.endpoint[p].params) {
for (var k in res.endpoint[p].params) {
try {
var my_regexp2 = new RegExp(res.endpoint[p].params[k]);
if (!options.qstring || !options.qstring[k] || !my_regexp2.test(options.qstring[k])) {
missing_param = true;
}
}
catch (e) {
log.e("Invalid regex: '" + res.endpoint[p].params[k] + "'");
missing_param = true;
}
}
}
try {
my_regexp = new RegExp(res.endpoint[p].endpoint);
if (!missing_param && my_regexp.test(options.req_path)) {
valid_endpoint = true;
}
}
catch (e) {
log.e("Invalid regex: '" + res.endpoint[p].endpoint + "'");
}
}
else {
try {
my_regexp = new RegExp(res.endpoint[p]);
if (my_regexp.test(options.req_path)) {
valid_endpoint = true;
}
}
catch (e) {
log.e("Invalid regex: '" + res.endpoint[p] + "'");
}
}
}
}
Expand Down Expand Up @@ -248,6 +284,7 @@ var verify_token = function(options, return_owner) {
* @param {object} options - options for the task
* @param {object} options.db - database connection
* @param {string} options.token - token to verify
* @param {string} options.qstring - params.qstring. If not passed and there is limitation for this token on params - token will not be valid
* @param {string} options.req_path - current request path
* @param {function} options.callback - function called when verifying was completed, providing 1 argument, true if could verify token and false if couldn't
*/
Expand All @@ -260,6 +297,7 @@ authorizer.verify = function(options) {
* @param {object} options - options for the task
* @param {object} options.db - database connection
* @param {string} options.token - token to verify
* @param {string} options.qstring - params.qstring. If not passed and there is limitation for this token on params - token will not be valid
* @param {string} options.req_path - current request path
* @param {function} options.callback - function called when verifying was completed, providing 1 argument, true if could verify token and false if couldn't
*/
Expand Down
16 changes: 15 additions & 1 deletion api/utils/requestProcessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -1604,9 +1604,23 @@ const processRequest = (params) => {
apps = params.qstring.apps.split(',');
}

if (params.qstring.endpoint) {
if (params.qstring.endpointquery && params.qstring.endpointquery !== "") {
try {
endpoint = JSON.parse(params.qstring.endpointquery); //structure with also info for qstring params.
}
catch (ex) {
if (params.qstring.endpoint) {
endpoint = params.qstring.endpoint.split(',');
}
else {
endpoint = "";
}
}
}
else if (params.qstring.endpoint) {
endpoint = params.qstring.endpoint.split(',');
}

if (params.qstring.purpose) {
purpose = params.qstring.purpose;
}
Expand Down
5 changes: 5 additions & 0 deletions frontend/express/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ process.title = "countly: dashboard node " + process.argv[1];

var versionInfo = require('./version.info'),
COUNTLY_VERSION = versionInfo.version,
COUNTLY_COMPANY = versionInfo.company || '',
COUNTLY_TYPE = versionInfo.type,
COUNTLY_PAGE = versionInfo.page = (!versionInfo.title) ? "http://count.ly" : null,
COUNTLY_NAME = versionInfo.title = versionInfo.title || "Countly",
Expand Down Expand Up @@ -197,6 +198,8 @@ function isArgon2Hash(hashedStr) {
* @param {Function} callback | Callback function
*/
function verifyMemberArgon2Hash(username, password, callback) {
var secret = countlyConfig.passwordSecret || "";
password = password + secret;
countlyDb.collection('members').findOne({$and: [{ $or: [ {"username": username}, {"email": username}]}]}, (err, member) => {
if (member) {
if (isArgon2Hash(member.password)) {
Expand Down Expand Up @@ -573,6 +576,7 @@ app.use(function(req, res, next) {
req.template.css = "";
req.template.form = "";
req.countly = {
company: COUNTLY_COMPANY,
version: COUNTLY_VERSION,
type: COUNTLY_TYPE,
page: COUNTLY_PAGE,
Expand Down Expand Up @@ -787,6 +791,7 @@ function renderDashboard(req, res, next, member, adminOfApps, userOfApps, countl
_.extend(req.config, configs);
var countlyGlobal = {
countlyTitle: req.countly.title,
company: req.countly.company,
languages: languages,
countlyVersion: req.countly.version,
countlyFavicon: req.countly.favicon,
Expand Down
7 changes: 6 additions & 1 deletion frontend/express/config.sample.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,12 @@ var countlyConfig = {
* Legacy value, not supported
* @type {string}
*/
cdn: ""
cdn: "",
/**
* Additional password secret for safer autentification. This secret will be added on changing and creating password. Changing value in configs will result in invalid passwords for existing users.
* Default value is "".
*/
passwordSecret: ""
};

module.exports = require('../../api/configextender')('FRONTEND', countlyConfig, process.env);
12 changes: 10 additions & 2 deletions frontend/express/libs/members.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,10 @@ membersUtility.login = function(req, res, callback) {
if (req.body.username && req.body.password) {
var countlyConfig = membersUtility.countlyConfig;
req.body.username = (req.body.username + "").trim();

var secret = membersUtility.countlyConfig.passwordSecret || "";
req.body.password = req.body.password + secret;

verifyMemberArgon2Hash(req.body.username, req.body.password, membersUtility.db, (err, member) => {
if (member) {
if (member.locked) {
Expand Down Expand Up @@ -542,7 +546,8 @@ membersUtility.setup = function(req, callback) {
if (!err && memberCount === 0) {
var countlyConfig = membersUtility.countlyConfig;
if (req.body.full_name && req.body.username && req.body.password && req.body.email) {
argon2Hash(req.body.password).then(password => {
var secret = membersUtility.countlyConfig.passwordSecret || "";
argon2Hash(req.body.password + secret).then(password => {
req.body.email = (req.body.email + "").trim();
req.body.username = (req.body.username + "").trim();
var doc = {"full_name": req.body.full_name, "username": req.body.username, "password": password, "email": req.body.email, "global_admin": true, created_at: Math.floor(((new Date()).getTime()) / 1000), password_changed: Math.floor(((new Date()).getTime()) / 1000)};
Expand Down Expand Up @@ -698,7 +703,8 @@ membersUtility.reset = function(req, callback) {
if (result === false) {
if (req.body.password && req.body.again && req.body.prid) {
req.body.prid += "";
argon2Hash(req.body.password).then(password => {
var secret = membersUtility.countlyConfig.passwordSecret || "";
argon2Hash(req.body.password + secret).then(password => {
membersUtility.db.collection('password_reset').findOne({ prid: req.body.prid }, function(err, passwordReset) {
membersUtility.db.collection('members').findAndModify({ _id: passwordReset.user_id }, {}, { '$set': { "password": password } }, function(err2, member) {
member = member && member.ok ? member.value : null;
Expand Down Expand Up @@ -764,6 +770,8 @@ membersUtility.settings = function(req, callback) {
callback(false, "username-exists");
}
else {
var secret = membersUtility.countlyConfig.passwordSecret || "";
req.body.new_pwd = req.body.new_pwd + secret;
if (req.body.old_pwd && req.body.old_pwd.length) {
if (isArgon2Hash(member.password)) {
var match;
Expand Down
Loading

0 comments on commit 42728a9

Please sign in to comment.