2177-accountModule #373
|
@ -4,10 +4,6 @@ module.exports = Self => {
|
||||||
description: 'Changes the user password',
|
description: 'Changes the user password',
|
||||||
accepts: [
|
accepts: [
|
||||||
{
|
{
|
||||||
arg: 'ctx',
|
|
||||||
type: 'Object',
|
|
||||||
http: {source: 'context'}
|
|
||||||
}, {
|
|
||||||
arg: 'id',
|
arg: 'id',
|
||||||
type: 'Number',
|
type: 'Number',
|
||||||
description: 'The user id',
|
description: 'The user id',
|
||||||
|
@ -24,41 +20,15 @@ module.exports = Self => {
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: {
|
|
||||||
type: 'Boolean',
|
|
||||||
root: true
|
|
||||||
},
|
|
||||||
http: {
|
http: {
|
||||||
path: `/:id/changePassword`,
|
path: `/:id/changePassword`,
|
||||||
verb: 'PATCH'
|
verb: 'PATCH'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.changePassword = async function(ctx, id, oldPassword, newPassword) {
|
Self.changePassword = async function(id, oldPassword, newPassword) {
|
||||||
let params = [id, oldPassword, newPassword];
|
await Self.rawSql(`CALL account.user_changePassword(?, ?, ?)`,
|
||||||
await Self.rawSql(`CALL account.user_changePassword(?, ?, ?)`, params);
|
[id, oldPassword, newPassword]);
|
||||||
|
await Self.app.models.UserAccount.syncById(id, newPassword);
|
||||||
/*
|
|
||||||
const ldap = require('ldapjs');
|
|
||||||
|
|
||||||
let ldapConf = {
|
|
||||||
url: 'ldap://domain.local:389',
|
|
||||||
dn: 'cn=admin,dc=domain,dc=local',
|
|
||||||
password: '123456'
|
|
||||||
};
|
|
||||||
|
|
||||||
await new Promise((reject, resolve) => {
|
|
||||||
let client = ldap.createClient({url: ldapConf.url});
|
|
||||||
|
|
||||||
client.bind(ldapConf.dn, ldapConf.password, err => {
|
|
||||||
if (err)
|
|
||||||
reject(err);
|
|
||||||
else
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,9 +26,9 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.login = async function(user, password) {
|
Self.login = async function(user, password) {
|
||||||
|
let $ = Self.app.models;
|
||||||
let token;
|
let token;
|
||||||
let usesEmail = user.indexOf('@') !== -1;
|
let usesEmail = user.indexOf('@') !== -1;
|
||||||
let User = Self.app.models.User;
|
|
||||||
|
|
||||||
let loginInfo = {password};
|
let loginInfo = {password};
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ module.exports = Self => {
|
||||||
loginInfo.username = user;
|
loginInfo.username = user;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
token = await User.login(loginInfo, 'user');
|
token = await $.User.login(loginInfo, 'user');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code != 'LOGIN_FAILED' || usesEmail)
|
if (err.code != 'LOGIN_FAILED' || usesEmail)
|
||||||
throw err;
|
throw err;
|
||||||
|
@ -49,17 +49,8 @@ module.exports = Self => {
|
||||||
if (!instance || instance.password !== md5(password || ''))
|
if (!instance || instance.password !== md5(password || ''))
|
||||||
throw err;
|
throw err;
|
||||||
|
|
||||||
let where = {id: instance.id};
|
await $.UserAccount.sync(user, password);
|
||||||
let userData = {
|
token = await $.User.login(loginInfo, 'user');
|
||||||
id: instance.id,
|
|
||||||
username: user,
|
|
||||||
password: password,
|
|
||||||
email: instance.email,
|
|
||||||
created: instance.created,
|
|
||||||
updated: instance.updated
|
|
||||||
};
|
|
||||||
await User.upsertWithWhere(where, userData);
|
|
||||||
token = await User.login(loginInfo, 'user');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {token: token.id};
|
return {token: token.id};
|
||||||
|
|
|
@ -15,10 +15,6 @@ module.exports = Self => {
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: {
|
|
||||||
type: 'Boolean',
|
|
||||||
root: true
|
|
||||||
},
|
|
||||||
http: {
|
http: {
|
||||||
path: `/:id/setPassword`,
|
path: `/:id/setPassword`,
|
||||||
verb: 'PATCH'
|
verb: 'PATCH'
|
||||||
|
@ -26,8 +22,8 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.setPassword = async function(id, newPassword) {
|
Self.setPassword = async function(id, newPassword) {
|
||||||
let params = [id, newPassword];
|
await Self.rawSql(`CALL account.user_setPassword(?, ?)`,
|
||||||
await Self.rawSql(`CALL account.user_setPassword(?, ?)`, params);
|
[id, newPassword]);
|
||||||
return true;
|
await Self.app.models.UserAccount.syncById(id, newPassword);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "account.user"
|
"table": "account.user"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
|
@ -31,6 +31,9 @@
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
|
"bcryptPassword": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"active": {
|
"active": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
@ -42,46 +45,52 @@
|
||||||
},
|
},
|
||||||
"updated": {
|
"updated": {
|
||||||
"type": "date"
|
"type": "date"
|
||||||
|
},
|
||||||
|
"sync": {
|
||||||
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
"role": {
|
"role": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Role",
|
"model": "Role",
|
||||||
"foreignKey": "roleFk"
|
"foreignKey": "roleFk"
|
||||||
},
|
},
|
||||||
"emailUser": {
|
"roles": {
|
||||||
"type": "hasOne",
|
"type": "hasMany",
|
||||||
"model": "EmailUser",
|
"model": "RoleRole",
|
||||||
"foreignKey": "userFk"
|
"foreignKey": "role"
|
||||||
|
},
|
||||||
|
"emailUser": {
|
||||||
|
"type": "hasOne",
|
||||||
|
"model": "EmailUser",
|
||||||
|
"foreignKey": "userFk"
|
||||||
},
|
},
|
||||||
"worker": {
|
"worker": {
|
||||||
"type": "hasOne",
|
"type": "hasOne",
|
||||||
"model": "Worker",
|
"model": "Worker",
|
||||||
"foreignKey": "userFk"
|
"foreignKey": "userFk"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"acls": [
|
"acls": [
|
||||||
{
|
{
|
||||||
"property": "login",
|
"property": "login",
|
||||||
"accessType": "EXECUTE",
|
"accessType": "EXECUTE",
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
"principalId": "$everyone",
|
"principalId": "$everyone",
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
"property": "logout",
|
"property": "logout",
|
||||||
"accessType": "EXECUTE",
|
"accessType": "EXECUTE",
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
"principalId": "$authenticated",
|
"principalId": "$authenticated",
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
"property": "validateToken",
|
"property": "validateToken",
|
||||||
"accessType": "EXECUTE",
|
"accessType": "EXECUTE",
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
"principalId": "$authenticated",
|
"principalId": "$authenticated",
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,12 @@ ALTER TABLE `account`.`mailAliasAccount`
|
||||||
ADD `id` INT UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
|
ADD `id` INT UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
|
||||||
ADD PRIMARY KEY (`id`);
|
ADD PRIMARY KEY (`id`);
|
||||||
|
|
||||||
|
ALTER TABLE account.ldapConfig
|
||||||
|
ADD groupDn varchar(255) NULL;
|
||||||
|
|
||||||
|
UPDATE account.ldapConfig SET groupDn = 'ou=groups,dc=verdnatura,dc=es';
|
||||||
|
|
||||||
|
DROP PROCEDURE IF EXISTS account.user_syncPassword;
|
||||||
|
|
||||||
USE account;
|
USE account;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,5 @@ BEGIN
|
||||||
`password` = MD5(vPassword),
|
`password` = MD5(vPassword),
|
||||||
`recoverPass` = FALSE
|
`recoverPass` = FALSE
|
||||||
WHERE id = vSelf;
|
WHERE id = vSelf;
|
||||||
|
|
||||||
CALL user_syncPassword(vSelf, vPassword);
|
|
||||||
END$$
|
END$$
|
||||||
DELIMITER ;
|
DELIMITER ;
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -36,6 +36,9 @@ INSERT INTO `account`.`user`(`id`,`name`, `nickname`, `password`,`role`,`active`
|
||||||
FROM `account`.`role` WHERE id <> 20
|
FROM `account`.`role` WHERE id <> 20
|
||||||
ORDER BY id;
|
ORDER BY id;
|
||||||
|
|
||||||
|
INSERT INTO `account`.`account`(`id`)
|
||||||
|
SELECT id FROM `account`.`user`;
|
||||||
|
|
||||||
INSERT INTO `vn`.`worker`(`id`,`code`, `firstName`, `lastName`, `userFk`, `bossFk`)
|
INSERT INTO `vn`.`worker`(`id`,`code`, `firstName`, `lastName`, `userFk`, `bossFk`)
|
||||||
SELECT id,UPPER(LPAD(role, 3, '0')), name, name, id, 9
|
SELECT id,UPPER(LPAD(role, 3, '0')), name, name, id, 9
|
||||||
FROM `vn`.`user`;
|
FROM `vn`.`user`;
|
||||||
|
@ -65,20 +68,6 @@ INSERT INTO `account`.`user`(`id`,`name`,`nickname`, `password`,`role`,`active`,
|
||||||
(111, 'Missing', 'Missing', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en'),
|
(111, 'Missing', 'Missing', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en'),
|
||||||
(112, 'Trash', 'Trash', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en');
|
(112, 'Trash', 'Trash', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en');
|
||||||
|
|
||||||
|
|
||||||
INSERT INTO `account`.`userPassword` (`id`, `length`, `nAlpha`, `nUpper`, `nDigits`, `nPunct`)
|
|
||||||
VALUES
|
|
||||||
(1, 8, 1, 1, 1, 1);
|
|
||||||
|
|
||||||
INSERT INTO `account`.`account`(`id`)
|
|
||||||
VALUES
|
|
||||||
(101),
|
|
||||||
(102);
|
|
||||||
|
|
||||||
INSERT INTO `account`.`mailConfig` (`id`, `domain`)
|
|
||||||
VALUES
|
|
||||||
(1, 'verdnatura.es');
|
|
||||||
|
|
||||||
INSERT INTO `account`.`mailAlias`(`id`, `alias`, `description`, `isPublic`)
|
INSERT INTO `account`.`mailAlias`(`id`, `alias`, `description`, `isPublic`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'general', 'General mailing list', FALSE),
|
(1, 'general', 'General mailing list', FALSE),
|
||||||
|
|
|
@ -22,6 +22,9 @@ TABLES=(
|
||||||
role
|
role
|
||||||
roleInherit
|
roleInherit
|
||||||
roleRole
|
roleRole
|
||||||
|
userPassword
|
||||||
|
accountConfig
|
||||||
|
mailConfig
|
||||||
)
|
)
|
||||||
dump_tables ${TABLES[@]}
|
dump_tables ${TABLES[@]}
|
||||||
|
|
||||||
|
|
|
@ -141,5 +141,7 @@
|
||||||
"Role already assigned": "Role already assigned",
|
"Role already assigned": "Role already assigned",
|
||||||
"Invalid role name": "Invalid role name",
|
"Invalid role name": "Invalid role name",
|
||||||
"Role name must be written in camelCase": "Role name must be written in camelCase",
|
"Role name must be written in camelCase": "Role name must be written in camelCase",
|
||||||
"can't be set": "can't be set"
|
"can't be set": "can't be set",
|
||||||
|
"Email already exists": "Email already exists",
|
||||||
|
"User already exists": "User already exists"
|
||||||
}
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
const ldap = require('../../util/ldapjs-extra');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('sync', {
|
||||||
|
description: 'Synchronizes the user with the other user databases',
|
||||||
|
http: {
|
||||||
|
path: `/sync`,
|
||||||
|
verb: 'PATCH'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.sync = async function() {
|
||||||
|
let $ = Self.app.models;
|
||||||
|
|
||||||
|
let ldapConfig = await $.LdapConfig.findOne({
|
||||||
|
fields: ['host', 'rdn', 'password', 'groupDn']
|
||||||
|
});
|
||||||
|
let accountConfig = await $.AccountConfig.findOne({
|
||||||
|
fields: ['idBase']
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!ldapConfig) return;
|
||||||
|
|
||||||
|
// Connect
|
||||||
|
|
||||||
|
let client = ldap.createClient({
|
||||||
|
url: `ldap://${ldapConfig.host}:389`
|
||||||
|
});
|
||||||
|
|
||||||
|
let ldapPassword = Buffer
|
||||||
|
.from(ldapConfig.password, 'base64')
|
||||||
|
.toString('ascii');
|
||||||
|
await client.bind(ldapConfig.rdn, ldapPassword);
|
||||||
|
|
||||||
|
let err;
|
||||||
|
try {
|
||||||
|
// Delete roles
|
||||||
|
|
||||||
|
let opts = {
|
||||||
|
scope: 'sub',
|
||||||
|
attributes: ['dn'],
|
||||||
|
filter: 'objectClass=posixGroup'
|
||||||
|
};
|
||||||
|
res = await client.search(ldapConfig.groupDn, opts);
|
||||||
|
|
||||||
|
let reqs = [];
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
res.on('error', err => {
|
||||||
|
if (err.name === 'NoSuchObjectError')
|
||||||
|
err = new Error(`Object '${ldapConfig.groupDn}' does not exist`);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
res.on('searchEntry', e => {
|
||||||
|
reqs.push(client.del(e.object.dn));
|
||||||
|
});
|
||||||
|
res.on('end', resolve);
|
||||||
|
});
|
||||||
|
await Promise.all(reqs);
|
||||||
|
|
||||||
|
// Recreate roles
|
||||||
|
|
||||||
|
let roles = await $.Role.find({
|
||||||
|
fields: ['id', 'name']
|
||||||
|
});
|
||||||
|
let accounts = await $.UserAccount.find({
|
||||||
|
fields: ['id'],
|
||||||
|
include: {
|
||||||
|
relation: 'user',
|
||||||
|
scope: {
|
||||||
|
fields: ['name'],
|
||||||
|
include: {
|
||||||
|
relation: 'roles',
|
||||||
|
scope: {
|
||||||
|
fields: ['inheritsFrom']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let map = new Map();
|
||||||
|
for (let account of accounts) {
|
||||||
|
let user = account.user();
|
||||||
|
for (let inherit of user.roles()) {
|
||||||
|
let roleId = inherit.inheritsFrom;
|
||||||
|
if (!map.has(roleId)) map.set(roleId, []);
|
||||||
|
map.get(roleId).push(user.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reqs = [];
|
||||||
|
for (let role of roles) {
|
||||||
|
let newEntry = {
|
||||||
|
objectClass: ['top', 'posixGroup'],
|
||||||
|
cn: role.name,
|
||||||
|
gidNumber: accountConfig.idBase + role.id
|
||||||
|
};
|
||||||
|
|
||||||
|
let memberUid = map.get(role.id);
|
||||||
|
if (memberUid) newEntry.memberUid = memberUid;
|
||||||
|
|
||||||
|
let dn = `cn=${role.name},${ldapConfig.groupDn}`;
|
||||||
|
reqs.push(client.add(dn, newEntry));
|
||||||
|
}
|
||||||
|
await Promise.all(reqs);
|
||||||
|
} catch (e) {
|
||||||
|
err = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Cannot disconnect, hangs on undind() call
|
||||||
|
// await client.unbind();
|
||||||
|
if (err) throw err;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,27 @@
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('syncById', {
|
||||||
|
description: 'Synchronizes the user with the other user databases',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
description: 'The user id',
|
||||||
|
required: true
|
||||||
|
}, {
|
||||||
|
arg: 'password',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The password'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: `/:id/syncById`,
|
||||||
|
verb: 'PATCH'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.syncById = async function(id, password) {
|
||||||
|
let user = await Self.app.models.Account.findById(id, {fields: ['name']});
|
||||||
|
await Self.sync(user.name, password);
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,268 @@
|
||||||
|
const ldap = require('../../util/ldapjs-extra');
|
||||||
|
const nthash = require('smbhash').nthash;
|
||||||
|
const ssh = require('node-ssh');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('sync', {
|
||||||
|
description: 'Synchronizes the user with the other user databases',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'userName',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The user name',
|
||||||
|
required: true
|
||||||
|
}, {
|
||||||
|
arg: 'password',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The password'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: `/sync`,
|
||||||
|
verb: 'PATCH'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.sync = async function(userName, password) {
|
||||||
|
let $ = Self.app.models;
|
||||||
|
|
||||||
|
let user = await $.Account.findOne({
|
||||||
|
fields: ['id', 'sync'],
|
||||||
|
where: {name: userName}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (user && user.sync) return;
|
||||||
|
|
||||||
|
let accountConfig;
|
||||||
|
let mailConfig;
|
||||||
|
let extraParams;
|
||||||
|
let hasAccount = false;
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
accountConfig = await $.AccountConfig.findOne({
|
||||||
|
fields: ['homedir', 'shell', 'idBase']
|
||||||
|
});
|
||||||
|
mailConfig = await $.MailConfig.findOne({
|
||||||
|
fields: ['domain']
|
||||||
|
});
|
||||||
|
|
||||||
|
user = await $.Account.findById(user.id, {
|
||||||
|
fields: [
|
||||||
|
'id',
|
||||||
|
'nickname',
|
||||||
|
'email',
|
||||||
|
'lang',
|
||||||
|
'roleFk',
|
||||||
|
'sync',
|
||||||
|
'active',
|
||||||
|
'created',
|
||||||
|
'updated'
|
||||||
|
],
|
||||||
|
where: {name: userName},
|
||||||
|
include: {
|
||||||
|
relation: 'roles',
|
||||||
|
scope: {
|
||||||
|
include: {
|
||||||
|
relation: 'inherits',
|
||||||
|
scope: {
|
||||||
|
fields: ['name']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
extraParams = {
|
||||||
|
corporateMail: `${userName}@${mailConfig.domain}`,
|
||||||
|
uidNumber: accountConfig.idBase + user.id
|
||||||
|
};
|
||||||
|
|
||||||
|
hasAccount = user.active
|
||||||
|
&& await $.UserAccount.exists(user.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
let bcryptPassword = $.User.hashPassword(password);
|
||||||
|
await $.Account.upsertWithWhere({id: user.id}, {bcryptPassword});
|
||||||
|
|
||||||
|
await $.user.destroyById(user.id);
|
||||||
|
if (hasAccount) {
|
||||||
|
await $.user.upsert({
|
||||||
|
id: user.id,
|
||||||
|
username: userName,
|
||||||
|
password: bcryptPassword,
|
||||||
|
email: user.email,
|
||||||
|
created: user.created,
|
||||||
|
updated: user.updated
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SIP
|
||||||
|
|
||||||
|
if (hasAccount) {
|
||||||
|
await Self.rawSql('CALL pbx.sip_setPassword(?, ?)',
|
||||||
|
[user.id, password]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// LDAP
|
||||||
|
|
||||||
|
let ldapConfig = await $.LdapConfig.findOne({
|
||||||
|
fields: ['host', 'rdn', 'password', 'baseDn', 'groupDn']
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ldapConfig) {
|
||||||
|
let ldapClient = ldap.createClient({
|
||||||
|
url: `ldap://${ldapConfig.host}:389`
|
||||||
|
});
|
||||||
|
|
||||||
|
let ldapPassword = Buffer
|
||||||
|
.from(ldapConfig.password, 'base64')
|
||||||
|
.toString('ascii');
|
||||||
|
await ldapClient.bind(ldapConfig.rdn, ldapPassword);
|
||||||
|
|
||||||
|
let err;
|
||||||
|
try {
|
||||||
|
// Deletes user
|
||||||
|
|
||||||
|
try {
|
||||||
|
let dn = `uid=${userName},${ldapConfig.baseDn}`;
|
||||||
|
await ldapClient.del(dn);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.name !== 'NoSuchObjectError') throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes user from groups
|
||||||
|
|
||||||
|
let opts = {
|
||||||
|
scope: 'sub',
|
||||||
|
attributes: ['dn'],
|
||||||
|
filter: `&(memberUid=${userName})(objectClass=posixGroup)`
|
||||||
|
};
|
||||||
|
res = await ldapClient.search(ldapConfig.groupDn, opts);
|
||||||
|
|
||||||
|
let oldGroups = [];
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
res.on('error', reject);
|
||||||
|
res.on('searchEntry', e => oldGroups.push(e.object));
|
||||||
|
res.on('end', resolve);
|
||||||
|
});
|
||||||
|
|
||||||
|
let reqs = [];
|
||||||
|
for (oldGroup of oldGroups) {
|
||||||
|
let change = new ldap.Change({
|
||||||
|
operation: 'delete',
|
||||||
|
modification: {memberUid: userName}
|
||||||
|
});
|
||||||
|
reqs.push(ldapClient.modify(oldGroup.dn, change));
|
||||||
|
}
|
||||||
|
await Promise.all(reqs);
|
||||||
|
|
||||||
|
if (hasAccount) {
|
||||||
|
// Recreates user
|
||||||
|
|
||||||
|
let nameArgs = user.nickname.split(' ');
|
||||||
|
let sshaPassword = crypto
|
||||||
|
.createHash('sha1')
|
||||||
|
.update(password)
|
||||||
|
.digest('base64');
|
||||||
|
|
||||||
|
let dn = `uid=${userName},${ldapConfig.baseDn}`;
|
||||||
|
let newEntry = {
|
||||||
|
uid: userName,
|
||||||
|
objectClass: [
|
||||||
|
'inetOrgPerson',
|
||||||
|
'posixAccount',
|
||||||
|
'sambaSamAccount'
|
||||||
|
],
|
||||||
|
cn: user.nickname || userName,
|
||||||
|
displayName: user.nickname,
|
||||||
|
givenName: nameArgs[0],
|
||||||
|
sn: nameArgs[1] || 'Empty',
|
||||||
|
mail: extraParams.corporateMail,
|
||||||
|
userPassword: `{SSHA}${sshaPassword}`,
|
||||||
|
preferredLanguage: user.lang,
|
||||||
|
homeDirectory: `${accountConfig.homedir}/${userName}`,
|
||||||
|
loginShell: accountConfig.shell,
|
||||||
|
uidNumber: extraParams.uidNumber,
|
||||||
|
gidNumber: accountConfig.idBase + user.roleFk,
|
||||||
|
sambaSID: '-',
|
||||||
|
sambaNTPassword: nthash(password)
|
||||||
|
};
|
||||||
|
await ldapClient.add(dn, newEntry);
|
||||||
|
|
||||||
|
// Adds user to groups
|
||||||
|
|
||||||
|
let reqs = [];
|
||||||
|
for (let role of user.roles()) {
|
||||||
|
let change = new ldap.Change({
|
||||||
|
operation: 'add',
|
||||||
|
modification: {memberUid: userName}
|
||||||
|
});
|
||||||
|
let roleName = role.inherits().name;
|
||||||
|
let dn = `cn=${roleName},${ldapConfig.groupDn}`;
|
||||||
|
reqs.push(ldapClient.modify(dn, change));
|
||||||
|
}
|
||||||
|
await Promise.all(reqs);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
err = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Cannot disconnect, hangs on undind() call
|
||||||
|
// await ldapClient.unbind();
|
||||||
|
if (err) throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Samba
|
||||||
|
|
||||||
|
let sambaConfig = await $.SambaConfig.findOne({
|
||||||
|
fields: ['host', 'sshUser', 'sshPass']
|
||||||
|
});
|
||||||
|
|
||||||
|
if (sambaConfig) {
|
||||||
|
let sshPassword = Buffer
|
||||||
|
.from(sambaConfig.sshPass, 'base64')
|
||||||
|
.toString('ascii');
|
||||||
|
|
||||||
|
let sshClient = new ssh.NodeSSH();
|
||||||
|
await sshClient.connect({
|
||||||
|
host: sambaConfig.host,
|
||||||
|
username: sambaConfig.sshUser,
|
||||||
|
password: sshPassword
|
||||||
|
});
|
||||||
|
|
||||||
|
let commands;
|
||||||
|
|
||||||
|
if (hasAccount) {
|
||||||
|
commands = [
|
||||||
|
`samba-tool user create "${userName}" `
|
||||||
|
+ `--uid-number=${extraParams.uidNumber} `
|
||||||
|
+ `--mail-address="${extraParams.corporateMail}" `
|
||||||
|
+ `--random-password`,
|
||||||
|
`samba-tool user setexpiry "${userName}" `
|
||||||
|
+ `--noexpiry`,
|
||||||
|
`samba-tool user setpassword "${userName}" `
|
||||||
|
+ `--newpassword="${password}"`,
|
||||||
|
`mkhomedir_helper "${userName}" 0027`
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
commands = [
|
||||||
|
`samba-tool user delete "${userName}"`
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let command of commands)
|
||||||
|
await sshClient.execCommand(command);
|
||||||
|
|
||||||
|
await sshClient.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark as synchronized
|
||||||
|
|
||||||
|
// if (user)
|
||||||
|
// await $.Account.upsertWithWhere({id: user.id}, {sync: true});
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,10 +1,19 @@
|
||||||
{
|
{
|
||||||
|
"AccountConfig": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"LdapConfig": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"MailAlias": {
|
"MailAlias": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
"MailAliasAccount": {
|
"MailAliasAccount": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"MailConfig": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"MailForward": {
|
"MailForward": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
@ -14,11 +23,13 @@
|
||||||
"RoleRole": {
|
"RoleRole": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"SambaConfig": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"UserAccount": {
|
"UserAccount": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
"UserPassword": {
|
"UserPassword": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"name": "AccountConfig",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "account.accountConfig"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true
|
||||||
|
},
|
||||||
|
"homedir": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"shell": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"idBase": {
|
||||||
|
"type": "number",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"min": {
|
||||||
|
"type": "number",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"type": "number",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"warn": {
|
||||||
|
"type": "number",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"inact": {
|
||||||
|
"type": "number",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"name": "LdapConfig",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "account.ldapConfig"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true
|
||||||
|
},
|
||||||
|
"host": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"rdn": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"baseDn": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"filter": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"groupDn": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "MailConfig",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "account.mailConfig"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true
|
||||||
|
},
|
||||||
|
"domain": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,12 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
|
require('../methods/role-inherit/sync')(Self);
|
||||||
|
|
||||||
app.on('started', function() {
|
app.on('started', function() {
|
||||||
let hooks = ['after save', 'after delete'];
|
let hooks = ['after save', 'after delete'];
|
||||||
for (let hook of hooks) {
|
for (let hook of hooks) {
|
||||||
app.models.RoleInherit.observe(hook, async() => {
|
Self.observe(hook, async() => {
|
||||||
try {
|
try {
|
||||||
await Self.rawSql(`
|
await Self.rawSql(`
|
||||||
CREATE EVENT account.role_sync
|
CREATE EVENT account.role_sync
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"name": "SambaConfig",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "account.sambaConfig"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true
|
||||||
|
},
|
||||||
|
"host": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"sshUser": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sshPass": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
require('../methods/user-account/sync')(Self);
|
||||||
|
require('../methods/user-account/sync-by-id')(Self);
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
const ldap = require('ldapjs');
|
||||||
|
const promisifyObject = require('./promisify').promisifyObject;
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
createClient,
|
||||||
|
Change: ldap.Change
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a promisified version of LDAP client.
|
||||||
|
*
|
||||||
|
* @param {Object} opts Client options
|
||||||
|
* @return {Client} The promisified LDAP client
|
||||||
|
*/
|
||||||
|
function createClient(opts) {
|
||||||
|
let client = ldap.createClient(opts);
|
||||||
|
promisifyObject(client, [
|
||||||
|
'bind',
|
||||||
|
'add',
|
||||||
|
'compare',
|
||||||
|
'del',
|
||||||
|
'exop',
|
||||||
|
'modify',
|
||||||
|
'modifyDN',
|
||||||
|
'search',
|
||||||
|
'starttls',
|
||||||
|
'unbind'
|
||||||
|
]);
|
||||||
|
return client;
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
promisify,
|
||||||
|
promisifyObject
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promisifies a function wich follows the (err, res) => {} pattern as last
|
||||||
|
* function argument and returns the promisified version.
|
||||||
|
*
|
||||||
|
* @param {Function} fn Function to promisify
|
||||||
|
* @return {Function} The promisified function
|
||||||
|
*/
|
||||||
|
function promisify(fn) {
|
||||||
|
return function(...args) {
|
||||||
|
let thisArg = this;
|
||||||
|
let orgCb = args[args.length - 1];
|
||||||
|
if (typeof orgCb !== 'function') orgCb = null;
|
||||||
|
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
function cb(err, res) {
|
||||||
|
if (orgCb) orgCb(err, res);
|
||||||
|
err ? reject(err) : resolve(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orgCb)
|
||||||
|
args[args.length - 1] = cb;
|
||||||
|
else
|
||||||
|
args.push(cb);
|
||||||
|
|
||||||
|
fn.apply(thisArg, args);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promisifies object methods.
|
||||||
|
*
|
||||||
|
* @param {Object} obj Object to promisify
|
||||||
|
* @param {Array<String>} methods Array of method names to promisify
|
||||||
|
*/
|
||||||
|
function promisifyObject(obj, methods) {
|
||||||
|
for (let method of methods) {
|
||||||
|
let orgMethod = obj[method];
|
||||||
|
obj[method] = promisify(orgMethod);
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@
|
||||||
<vn-card class="vn-pa-lg">
|
<vn-card class="vn-pa-lg">
|
||||||
<vn-vertical>
|
<vn-vertical>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
label="Name"
|
label="User"
|
||||||
ng-model="$ctrl.user.name"
|
ng-model="$ctrl.user.name"
|
||||||
rule
|
rule
|
||||||
vn-focus>
|
vn-focus>
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
<vn-icon-button
|
<vn-icon-button
|
||||||
vn-click-stop="disconnect.show(row)"
|
vn-click-stop="disconnect.show(row)"
|
||||||
vn-tooltip="Kill session"
|
vn-tooltip="Kill session"
|
||||||
icon="eject">
|
icon="exit_to_app">
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
</vn-item-section>
|
</vn-item-section>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<vn-icon-button
|
<vn-icon-button
|
||||||
icon="delete"
|
icon="delete"
|
||||||
translate-attr="{title: 'Remove'}"
|
translate-attr="{title: 'Remove'}"
|
||||||
ng-click="removeConfirm.show(row)">
|
vn-click-stop="removeConfirm.show(row)">
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
</vn-item-section>
|
</vn-item-section>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -6887,6 +6887,11 @@
|
||||||
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
|
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"bigdecimal": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/bigdecimal/-/bigdecimal-0.6.1.tgz",
|
||||||
|
"integrity": "sha1-GFiNS08ia3cxDtBFdIWMA2pUSFs="
|
||||||
|
},
|
||||||
"bignumber.js": {
|
"bignumber.js": {
|
||||||
"version": "7.2.1",
|
"version": "7.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz",
|
||||||
|
@ -20459,6 +20464,33 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node-ssh": {
|
||||||
|
"version": "11.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-ssh/-/node-ssh-11.0.0.tgz",
|
||||||
|
"integrity": "sha512-zC8TuZX82/x/ZsH4GzE5jmUGQAS2ajcDOoD352x6W56A7+4ChuGdGYi+t9NnLKEtRYvbvNH4HO+LvlQVENmxog==",
|
||||||
|
"requires": {
|
||||||
|
"make-dir": "^3.1.0",
|
||||||
|
"sb-promise-queue": "^2.1.0",
|
||||||
|
"sb-scandir": "^3.1.0",
|
||||||
|
"shell-escape": "^0.2.0",
|
||||||
|
"ssh2": "^0.8.9"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"make-dir": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
|
||||||
|
"requires": {
|
||||||
|
"semver": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"semver": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node.extend": {
|
"node.extend": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/node.extend/-/node.extend-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/node.extend/-/node.extend-2.0.2.tgz",
|
||||||
|
@ -22644,6 +22676,19 @@
|
||||||
"xmlchars": "^2.2.0"
|
"xmlchars": "^2.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"sb-promise-queue": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sb-promise-queue/-/sb-promise-queue-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-zwq4YuP1FQFkGx2Q7GIkZYZ6PqWpV+bg0nIO1sJhWOyGyhqbj0MsTvK6lCFo5TQwX5pZr6SCQ75e8PCDCuNvkg=="
|
||||||
|
},
|
||||||
|
"sb-scandir": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sb-scandir/-/sb-scandir-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-70BVm2xz9jn94zSQdpvYrEG101/UV9TVGcfWr9T5iob3QhCK4lYXeculfBqPGFv3XTeKgx4dpWyYIDeZUqo4kg==",
|
||||||
|
"requires": {
|
||||||
|
"sb-promise-queue": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"schema-utils": {
|
"schema-utils": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
|
||||||
|
@ -22883,7 +22928,7 @@
|
||||||
},
|
},
|
||||||
"sha.js": {
|
"sha.js": {
|
||||||
"version": "2.4.11",
|
"version": "2.4.11",
|
||||||
"resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
|
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
|
||||||
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
|
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -22964,6 +23009,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
|
||||||
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
|
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
|
||||||
},
|
},
|
||||||
|
"shell-escape": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shell-escape/-/shell-escape-0.2.0.tgz",
|
||||||
|
"integrity": "sha1-aP0CXrBJC09WegJ/C/IkgLX4QTM="
|
||||||
|
},
|
||||||
"shellwords": {
|
"shellwords": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
|
||||||
|
@ -23062,6 +23112,14 @@
|
||||||
"is-fullwidth-code-point": "^2.0.0"
|
"is-fullwidth-code-point": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"smbhash": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/smbhash/-/smbhash-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-Pgtzz8bALUwMGamT6E5S4R/3oJk=",
|
||||||
|
"requires": {
|
||||||
|
"bigdecimal": ">= 0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"smtp-connection": {
|
"smtp-connection": {
|
||||||
"version": "2.12.0",
|
"version": "2.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/smtp-connection/-/smtp-connection-2.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/smtp-connection/-/smtp-connection-2.12.0.tgz",
|
||||||
|
@ -23474,6 +23532,24 @@
|
||||||
"options": "0.0.6"
|
"options": "0.0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ssh2": {
|
||||||
|
"version": "0.8.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-0.8.9.tgz",
|
||||||
|
"integrity": "sha512-GmoNPxWDMkVpMFa9LVVzQZHF6EW3WKmBwL+4/GeILf2hFmix5Isxm7Amamo8o7bHiU0tC+wXsGcUXOxp8ChPaw==",
|
||||||
|
"requires": {
|
||||||
|
"ssh2-streams": "~0.4.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ssh2-streams": {
|
||||||
|
"version": "0.4.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.4.10.tgz",
|
||||||
|
"integrity": "sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ==",
|
||||||
|
"requires": {
|
||||||
|
"asn1": "~0.2.0",
|
||||||
|
"bcrypt-pbkdf": "^1.0.2",
|
||||||
|
"streamsearch": "~0.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"sshpk": {
|
"sshpk": {
|
||||||
"version": "1.16.1",
|
"version": "1.16.1",
|
||||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
|
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
|
||||||
|
@ -23628,6 +23704,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
|
||||||
"integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
|
"integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
|
||||||
},
|
},
|
||||||
|
"streamsearch": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
|
||||||
|
},
|
||||||
"string-length": {
|
"string-length": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz",
|
||||||
|
|
|
@ -25,12 +25,14 @@
|
||||||
"loopback-connector-remote": "^3.4.1",
|
"loopback-connector-remote": "^3.4.1",
|
||||||
"loopback-context": "^3.4.0",
|
"loopback-context": "^3.4.0",
|
||||||
"md5": "^2.2.1",
|
"md5": "^2.2.1",
|
||||||
|
"node-ssh": "^11.0.0",
|
||||||
"object-diff": "0.0.4",
|
"object-diff": "0.0.4",
|
||||||
"object.pick": "^1.3.0",
|
"object.pick": "^1.3.0",
|
||||||
"request": "^2.88.0",
|
"request": "^2.88.0",
|
||||||
"request-promise-native": "^1.0.8",
|
"request-promise-native": "^1.0.8",
|
||||||
"require-yaml": "0.0.1",
|
"require-yaml": "0.0.1",
|
||||||
"sharp": "^0.25.4",
|
"sharp": "^0.25.4",
|
||||||
|
"smbhash": "0.0.1",
|
||||||
"soap": "^0.26.0",
|
"soap": "^0.26.0",
|
||||||
"strong-error-handler": "^2.3.2",
|
"strong-error-handler": "^2.3.2",
|
||||||
"uuid": "^3.3.3",
|
"uuid": "^3.3.3",
|
||||||
|
|
Loading…
Reference in New Issue