sails-hook-blacksails
Version:
A Sails Micro-app architecture framework
317 lines (285 loc) • 8.78 kB
JavaScript
import _ from 'lodash';
import jwt from 'jsonwebtoken';
import crypto from 'crypto';
const AuthService = {
isAuthenticated(req) {
if (req.session.authenticated) {
return true;
}
return false;
},
async isActive(id) {
const user = await User.findById(id);
const isActive = user ? user.isActive : null;
sails.log.info('--------- isActive ? --->', isActive);
return isActive;
},
getSessionUser(req) {
if (req.session.passport !== undefined && req.session.passport.user) {
return req.session.passport.user;
}
return null;
},
async isAdmin(req) {
const user = this.getSessionUser(req);
const result = await this.checkRole(user.id, ['admin']);
return result;
},
async isSuperAdmin(req) {
const user = this.getSessionUser(req);
const result = await this.checkRole(user.id, ['super-admin']);
return result;
},
async checkRole(id, roleNames) {
let authority = false;
// console.log('id=>', id);
const user = await User.findByIdWithRole(id);
if (user && user.Roles) {
user.Roles.forEach((role) => {
// console.log('role=>', role);
if (roleNames.some(e => e === role.authority)) {
authority = true;
}
});
}
if (authority) {
sails.log(`---- check UserName "${user.username}" is "${roleNames}"--> ${authority}`);
} else {
sails.log.warn(`---- check UserName "${user.username}" is "${roleNames}"--> ${authority}`);
}
return authority;
},
checkAdmin(user) {
let authority = false;
if (user && user.Roles) {
user.Roles.forEach((role) => {
if (role.authority === 'admin'
|| role.authority === 'super-admin') {
authority = true;
}
});
}
if (authority) {
sails.log(`---- check "${user.username}" isAdmin=>', ${authority}`);
} else {
sails.log.warn(`---- check "${user.username}" isAdmin=>', ${authority}`);
}
return authority;
},
isUser(req) {
try {
const user = this.getSessionUser(req);
let authority = false;
if (user && user.Roles) {
user.Roles.forEach((role) => {
if (role.authority === 'user') authority = true;
});
}
return authority;
} catch (e) {
sails.log.error('=== check isUser failed=>', e);
return false;
}
},
async isRole(req, targetRole) {
const session = this.getSessionUser(req);
try {
if (!session) {
throw new Error(MESSAGE.ERROR.NO_USER_LOGIN);
}
const user = await User.findOne({
where: { id: session.id },
include: [Role],
});
let isRole = false;
if (user && user.Roles) {
user.Roles.forEach((role) => {
if (role.authority === targetRole) isRole = true;
});
}
return isRole;
} catch (e) {
sails.log.error(e);
throw e;
}
},
isOnlyUser(req) {
try {
const user = this.getSessionUser(req);
let authority = true;
if (user && user.Roles) {
user.Roles.forEach((role) => {
if (role.authority !== 'user') authority = false;
});
} else {
authority = false;
}
return authority;
} catch (e) {
sails.log.error('=== check isOnlyUser failed=>', e);
return false;
}
},
getJWTToken(user, deviceToken) {
const jwtSecret = sails.config.session.secret;
// FIXME: JWT expiresIn https://github.com/auth0/node-jsonwebtoken
return jwt.sign(
JSON.stringify({
id: user.id,
deviceToken,
}),
jwtSecret,
// {
// expiresIn: '60d',
// },
);
},
getSessionEncodeToJWT(req, deviceToken) {
const user = this.getSessionUser(req);
let jwtToken = '';
if (user) {
const isWebView = this.isWebView(req.headers['user-agent']);
if ((req.session.needJwt || isWebView) && user) {
try {
jwtToken = this.getJWTToken(user, deviceToken);
} catch (e) {
sails.log.error(e);
throw new Error(e);
}
}
req.session.needJwt = false;
}
return jwtToken;
},
isWebView(userAgent) {
return userAgent.indexOf('React-Native') !== -1;
},
/**
* 用來檢查 user 的 roles 是否符合給予的 roles 陣列。
* @method
* @param {Request}
* @param {String[]} targetRoles - role 的名稱。
* @param {User} user - 要檢查的使用者(不帶入會從 Request 找)。
* return {String[]} 符合的 roles 陣列
*/
// matchedRoles(req, targetRoles, user) {
// if (!user) {
// user = this.getSessionUser(req);
// sails.log('matchedRoles user=>', user);
// }
// let matched = [];
// targetRoles = targetRoles.map(role => role.toLowerCase());
// if (user && user.Roles) {
// return matched = user.Roles.filter(role => targetRoles.indexOf(role.authority.toLowerCase()) !== -1);
// }
// return [];
// },
/**
* 用來檢查 user 的 roles 是否符合給予的 roles 陣列。
* @method
* @param {Request}
* @param {String[]} targetRoles - role 的名稱。
* @param {User} user - 要檢查的使用者(不帶入會從 Request 找)。
* return {Boolean} user role 是否符合 targetRoles 陣列
*/
isMatchedRoles(targetRoles, user) {
return user.Roles
? user.Roles.some(
role => targetRoles.some(
targetRole => role.authority.toLowerCase() === targetRole.toLowerCase(),
),
)
: false;
},
getHexToken(length) {
return crypto.randomBytes(length).toString('hex').substr(0, length);
},
getNumberToken(length) {
const range = Math.pow(10, (length + 1));
const num = Math.floor(Math.random() * range);
const numString = Array(length).join('0') + num.toString();
return numString.substr(numString.length - length, length);
},
async afterLogout(req, res, user) {
console.log('authService after login');
},
async isStaff(id) {
try {
const user = await User.findOne({
where: {
id,
},
include: [{
model: Role,
include: [Group],
}],
});
if (!user) {
return false;
}
const checkStaff = role => role.Groups.some(g => (g.authority === 'staff'
|| g.authority === 'adminuser'
|| g.authority === 'maintainer'));
const isStaff = user.Roles.some(role => checkStaff(role));
sails.log.info('--------- isStaff ? --->', isStaff);
return isStaff;
} catch (e) {
sails.log.error(e);
throw e;
}
},
verifyUser(user, url) {
/* offAuth 是個早期還沒有 mockAdmin 的時候用的測試開關,可以拔掉了 */
if (!user) {
return { success: false, reason: { message: 'no user' } };
}
// console.log('verifyUser user=>', user);
const policies = ConfigHelper.getAuthConfig();
const excludeRole = _.uniq((policies.ignoreRole) ? policies.ignoreRole : ['super-admin']);
sails.log.info('Roles below will be skipping check.', excludeRole);
const isExclude = this.isMatchedRoles(excludeRole, user);
// console.log('isExclude=>', isExclude);
if (isExclude) {
sails.log('ignore all check');
return { success: true, reason: { message: 'inExcludeArray' } };
}
const conditions = _.uniqBy(policies.verifyUser, 'name');
for (const condition of conditions) {
if (condition.enable !== true) {
// sails.log(`${condition.name} not enable`);
continue;
}
const isLoginRoleMatch = (
condition.enableRole[0] === '*'
|| this.isMatchedRoles(condition.enableRole, user)
);
if (isLoginRoleMatch !== true) {
sails.log(`${condition.name} not enable for this user`);
continue;
}
// console.log('condition=>', condition);
// console.log('url=>', url);
if (condition.skipPath && condition.skipPath.indexOf(url) >= 0) {
sails.log(`${condition.name} not enable for this url: ${url}`);
continue;
}
sails.log(`[!] Checking Rule "${condition.name}"...`);
for (const field of condition.verifyField) {
if (!user[field]) {
return {
success: false,
reason: { message: 'someCheckFailed', condition, field },
};
}
}
}
const expireCondition = policies;
if (expireCondition || expireCondition.enable !== true) {
return { success: true, reason: { message: 'allPassExcludeExpire' } };
}
// write expiration check here
return { success: true, reason: { message: 'allPass' } };
},
};
/** @module AuthService */
module.exports = AuthService;