Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: user data abstraction #515

Open
wants to merge 2 commits into
base: feat/hold-audience
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion bin/password.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
const Redis = require('ioredis');
const assert = require('assert');
const conf = require('../lib/config');
const UserData = require('../lib/utils/data/user');
const { updatePassword } = require('../lib/actions/updatePassword');

const config = conf.get('/', { env: process.env.NODE_ENV });
Expand Down Expand Up @@ -32,7 +33,8 @@ const main = async (username, password) => {

try {
await redis.connect();
await updatePassword({ redis }, username, password);
const userData = new UserData(redis);
await updatePassword({ userData }, username, password);
} catch (err) {
setImmediate(() => {
throw err;
Expand Down
14 changes: 2 additions & 12 deletions src/actions/activate.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
const { ActionTransport } = require('@microfleet/core');
const { HttpStatusError } = require('common-errors');
const Promise = require('bluebird');
const redisKey = require('../utils/key.js');
const jwt = require('../utils/jwt.js');
const { getInternalData } = require('../utils/userData');
const getMetadata = require('../utils/get-metadata');
Expand All @@ -10,7 +9,6 @@ const UserMetadata = require('../utils/metadata/user');

const {
USERS_INDEX,
USERS_DATA,
USERS_REFERRAL_INDEX,
USERS_PUBLIC_INDEX,
USERS_ACTIVE_FLAG,
Expand Down Expand Up @@ -119,17 +117,9 @@ async function activateAccount(data, metadata) {
const userId = data[USERS_ID_FIELD];
const alias = data[USERS_ALIAS_FIELD];
const referral = metadata[USERS_REFERRAL_FIELD];
const userKey = redisKey(userId, USERS_DATA);
const { defaultAudience, service } = this;
const { redis } = service;
// WARNING: `persist` is very important, otherwise we will lose user's information in 30 days
// set to active & persist
const pipeline = redis
.pipeline()
.hget(userKey, USERS_ACTIVE_FLAG)
.hset(userKey, USERS_ACTIVE_FLAG, 'true')
.persist(userKey)
.sadd(USERS_INDEX, userId);
const pipeline = service.userData.activate(userId);
pipeline.sadd(USERS_INDEX, userId);

UserMetadata
.using(userId, defaultAudience, pipeline)
Expand Down
5 changes: 1 addition & 4 deletions src/actions/alias.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ const { getInternalData } = require('../utils/userData');
const isActive = require('../utils/is-active');
const isBanned = require('../utils/is-banned');
const DetailedHttpStatusError = require('../utils/detailed-error');
const key = require('../utils/key');
const handlePipeline = require('../utils/pipeline-error');
const UserMetadata = require('../utils/metadata/user');

const {
USERS_DATA,
USERS_ALIAS_TO_ID,
USERS_ID_FIELD,
USERS_ALIAS_FIELD,
Expand Down Expand Up @@ -72,9 +70,8 @@ async function assignAlias({ params }) {
return Promise.reject(err);
}

const pipeline = redis.pipeline();
const pipeline = this.userData.setAlias(userId, alias);

pipeline.hset(key(userId, USERS_DATA), USERS_ALIAS_FIELD, alias);
UserMetadata
.using(userId, defaultAudience, pipeline)
.update(USERS_ALIAS_FIELD, JSON.stringify(alias));
Expand Down
16 changes: 5 additions & 11 deletions src/actions/ban.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,15 @@ const { getInternalData } = require('../utils/userData');
const handlePipeline = require('../utils/pipeline-error');
const UserMetadata = require('../utils/metadata/user');

const {
USERS_DATA, USERS_BANNED_FLAG, USERS_TOKENS, USERS_BANNED_DATA,
} = require('../constants.js');
const { USERS_TOKENS, USERS_BANNED_DATA } = require('../constants.js');

// helper
const stringify = (data) => JSON.stringify(data);

function lockUser({
id, reason, whom, remoteip,
}) {
const { redis, config } = this;
const { config } = this;
const { jwt: { defaultAudience } } = config;
const data = {
banned: true,
Expand All @@ -26,9 +24,7 @@ function lockUser({
remoteip: remoteip || '',
},
};
const pipeline = redis.pipeline();

pipeline.hset(redisKey(id, USERS_DATA), USERS_BANNED_FLAG, 'true');
const pipeline = this.userData.lock(id);
// set .banned on metadata for filtering & sorting users by that field
UserMetadata
.using(id, defaultAudience, pipeline)
Expand All @@ -39,11 +35,9 @@ function lockUser({
}

function unlockUser({ id }) {
const { redis, config } = this;
const { config } = this;
const { jwt: { defaultAudience } } = config;
const pipeline = redis.pipeline();

pipeline.hdel(redisKey(id, USERS_DATA), USERS_BANNED_FLAG);
const pipeline = this.userData.unLock(id);
// remove .banned on metadata for filtering & sorting users by that field
UserMetadata
.using(id, defaultAudience, pipeline)
Expand Down
10 changes: 5 additions & 5 deletions src/actions/mfa/attach.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ const Promise = require('bluebird');

const redisKey = require('../../utils/key');
const handlePipeline = require('../../utils/pipeline-error');
const UserData = require('../../utils/data/user');
const { checkMFA, generateRecoveryCodes } = require('../../utils/mfa');
const {
USERS_DATA,
USERS_MFA_FLAG,
USERS_MFA_RECOVERY,
MFA_TYPE_DISABLED,
} = require('../../constants');
Expand All @@ -15,11 +14,12 @@ async function storeData(userId) {
const { redis, secret } = this;
const recoveryCodes = generateRecoveryCodes();

return redis
const pipeline = redis
.pipeline()
.del(redisKey(userId, USERS_MFA_RECOVERY))
.sadd(redisKey(userId, USERS_MFA_RECOVERY), recoveryCodes)
.hset(redisKey(userId, USERS_DATA), USERS_MFA_FLAG, secret)
.sadd(redisKey(userId, USERS_MFA_RECOVERY), recoveryCodes);

return UserData.setMFA(userId, pipeline, secret)
.exec()
.then(handlePipeline)
.return({ recoveryCodes, enabled: true });
Expand Down
10 changes: 5 additions & 5 deletions src/actions/mfa/detach.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ const { ActionTransport } = require('@microfleet/core');
const Promise = require('bluebird');
const redisKey = require('../../utils/key');
const handlePipeline = require('../../utils/pipeline-error');
const UserData = require('../../utils/data/user');
const { checkMFA } = require('../../utils/mfa');
const {
USERS_DATA,
USERS_MFA_FLAG,
USERS_MFA_RECOVERY,
MFA_TYPE_REQUIRED,
} = require('../../constants');

async function removeData(userId) {
return this.redis
const pipeline = this.redis
.pipeline()
.del(redisKey(userId, USERS_MFA_RECOVERY))
.hdel(redisKey(userId, USERS_DATA), USERS_MFA_FLAG)
.del(redisKey(userId, USERS_MFA_RECOVERY));

return UserData.delMFA(userId, pipeline)
.exec()
.then(handlePipeline)
.return({ enabled: false });
Expand Down
10 changes: 3 additions & 7 deletions src/actions/register.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ const checkLimits = require('../utils/check-ip-limits');
const challenge = require('../utils/challenges/challenge');
const handlePipeline = require('../utils/pipeline-error');
const hashPassword = require('../utils/register/password/hash');
const UserData = require('../utils/data/user');
const {
USERS_REF,
USERS_REF_METADATA,
USERS_INDEX,
USERS_SSO_TO_ID,
USERS_DATA,
USERS_USERNAME_TO_ID,
USERS_ACTIVE_FLAG,
USERS_ID_FIELD,
Expand Down Expand Up @@ -211,13 +211,9 @@ async function performRegistration({ service, params }) {
pipeline.hset(USERS_SSO_TO_ID, uid, userId);
}

const userDataKey = redisKey(userId, USERS_DATA);
pipeline.hmset(userDataKey, basicInfo);
pipeline.hset(USERS_USERNAME_TO_ID, username, userId);
UserData.register(userId, pipeline, basicInfo, activate, config.deleteInactiveAccounts);

if (activate === false && config.deleteInactiveAccounts >= 0) {
pipeline.expire(userDataKey, config.deleteInactiveAccounts);
}
pipeline.hset(USERS_USERNAME_TO_ID, username, userId);

handlePipeline(await pipeline.exec());

Expand Down
8 changes: 4 additions & 4 deletions src/actions/remove.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ const { getInternalData } = require('../utils/userData');
const getMetadata = require('../utils/get-metadata');
const handlePipeline = require('../utils/pipeline-error');
const UserMetadata = require('../utils/metadata/user');
const UserData = require('../utils/data/user');
const {
USERS_INDEX,
USERS_PUBLIC_INDEX,
USERS_ALIAS_TO_ID,
USERS_SSO_TO_ID,
USERS_USERNAME_TO_ID,
USERS_USERNAME_FIELD,
USERS_DATA,
USERS_METADATA,
USERS_TOKENS,
USERS_ID_FIELD,
Expand Down Expand Up @@ -75,8 +75,8 @@ async function removeOrganizationUser(userId) {
*/
async function removeUser({ params }) {
const audience = this.config.jwt.defaultAudience;
const { redis } = this;
const context = { redis, audience };
const { redis, userData } = this;
const context = { redis, audience, userData };
const { username } = params;

const [internal, meta] = await Promise
Expand Down Expand Up @@ -116,7 +116,7 @@ async function removeUser({ params }) {
transaction.srem(USERS_INDEX, userId);

// remove metadata & internal data
transaction.del(key(userId, USERS_DATA));
UserData.deleteUserData(userId, transaction);
for (const metaAudience of metaAudiences) {
userMetadata.deleteMetadata(metaAudience);
}
Expand Down
4 changes: 2 additions & 2 deletions src/actions/token/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ function storeData(userId) {
*/
function createToken({ params }) {
const { username, name } = params;
const { redis, config } = this;
const context = { name, redis, config };
const { redis, config, userData } = this;
const context = { name, redis, config, userData };

return Promise
.bind(context, username)
Expand Down
4 changes: 2 additions & 2 deletions src/actions/token/erase.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ function eraseData(userId) {
*/
function eraseToken({ params }) {
const { username, token } = params;
const { redis, config } = this;
const context = { token, redis, config };
const { redis, config, userData } = this;
const context = { token, redis, config, userData };

return Promise
.bind(context, username)
Expand Down
4 changes: 2 additions & 2 deletions src/actions/token/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ function getList(userId) {
*/
function listTokens({ params }) {
const { username, page, pageSize } = params;
const { redis, config } = this;
const { redis, config, userData } = this;
const context = {
redis, config, page, pageSize,
redis, config, page, pageSize, userData,
};

return Promise
Expand Down
7 changes: 1 addition & 6 deletions src/actions/updatePassword.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
const { HttpStatusError } = require('common-errors');

const scrypt = require('../utils/scrypt');
const redisKey = require('../utils/key');
const jwt = require('../utils/jwt');
const { getInternalData } = require('../utils/userData');
const isActive = require('../utils/is-active');
const isBanned = require('../utils/is-banned');
const hasPassword = require('../utils/has-password');
const { getUserId } = require('../utils/userData');
const {
USERS_DATA,
USERS_ACTION_RESET,
USERS_PASSWORD_FIELD,
USERS_ID_FIELD,
} = require('../constants');
const UserLoginRateLimiter = require('../utils/rate-limiters/user-login-rate-limiter');
Expand Down Expand Up @@ -44,10 +41,8 @@ async function usernamePasswordReset(service, username, password) {
* @param {String} password
*/
async function setPassword(service, userId, password) {
const { redis } = service;
const hash = await scrypt.hash(password);

return redis.hset(redisKey(userId, USERS_DATA), USERS_PASSWORD_FIELD, hash);
return service.userData.setPassword(userId, hash);
}

/**
Expand Down
8 changes: 1 addition & 7 deletions src/auth/oauth/utils/attach.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
const get = require('lodash/get');
const redisKey = require('../../../utils/key');
const UserMetadata = require('../../../utils/metadata/user');
const handlePipeline = require('../../../utils/pipeline-error');
const {
USERS_SSO_TO_ID,
USERS_DATA,
} = require('../../../constants');

module.exports = async function attach(account, user) {
Expand All @@ -14,11 +12,7 @@ module.exports = async function attach(account, user) {
uid, provider, internals, profile,
} = account;
const audience = get(config, 'jwt.defaultAudience');
const userDataKey = redisKey(userId, USERS_DATA);
const pipeline = redis.pipeline();

// inject private info to user internal data
pipeline.hset(userDataKey, provider, JSON.stringify(internals));
const pipeline = this.userData.attachProvider(userId, provider, internals);

// link uid to user id
pipeline.hset(USERS_SSO_TO_ID, uid, userId);
Expand Down
10 changes: 2 additions & 8 deletions src/auth/oauth/utils/detach.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,23 @@
const Errors = require('common-errors');

const get = require('../../../utils/get-value');
const redisKey = require('../../../utils/key');
const UserMetadata = require('../../../utils/metadata/user');
const handlePipeline = require('../../../utils/pipeline-error');

const {
USERS_SSO_TO_ID,
USERS_DATA,
} = require('../../../constants');
const { USERS_SSO_TO_ID } = require('../../../constants');

module.exports = async function detach(provider, userData) {
const { id: userId } = userData;
const { redis, config } = this;
const audience = get(config, 'jwt.defaultAudience');
const userDataKey = redisKey(userId, USERS_DATA);
const pipeline = redis.pipeline();

const uid = get(userData, [provider, 'uid'], { default: false });
if (!uid) {
throw Errors.HttpStatusError(412, `${provider} account not found`);
}

// delete internal account data
pipeline.hdel(userDataKey, provider);
const pipeline = this.userData.delProvider(userId, provider);

// delete account reference
pipeline.hdel(USERS_SSO_TO_ID, uid);
Expand Down
14 changes: 2 additions & 12 deletions src/auth/oauth/utils/refresh.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
const redisKey = require('../../../utils/key');
const { USERS_DATA } = require('../../../constants');

module.exports = function refresh(account, user) {
const { redis } = this;
const { userId } = user;
const { provider, internals } = account;
const userDataKey = redisKey(userId, USERS_DATA);

return redis
.hset(userDataKey, provider, JSON.stringify(internals))
.return(true);
module.exports = function refresh({ provider, internals }, { userId }) {
return this.userData.refresh(userId, provider, internals);
};
Loading