Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 4146-claim_pickup_order
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Alex Moreno 2022-11-02 07:33:07 +01:00
commit fc7a39e910
44 changed files with 172 additions and 76 deletions

View File

@ -13,7 +13,7 @@ RUN apt-get update \
libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 \ libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 \
libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 \ libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 \
libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget \ libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget \
&& curl -sL https://deb.nodesource.com/setup_12.x | bash - \ && curl -sL https://deb.nodesource.com/setup_14.x | bash - \
&& apt-get install -y --no-install-recommends \ && apt-get install -y --no-install-recommends \
nodejs \ nodejs \
&& apt-get purge -y --auto-remove \ && apt-get purge -y --auto-remove \

View File

@ -29,6 +29,8 @@ module.exports = Self => {
}); });
Self.privileges = async function(ctx, id, roleFk, hasGrant, options) { Self.privileges = async function(ctx, id, roleFk, hasGrant, options) {
if (!(hasGrant != null || roleFk)) return;
const models = Self.app.models; const models = Self.app.models;
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
@ -37,22 +39,40 @@ module.exports = Self => {
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
const user = await models.Account.findById(userId, null, myOptions); const user = await models.Account.findById(userId, {fields: ['hasGrant']}, myOptions);
const userToUpdate = await models.Account.findById(id, {
fields: ['id', 'name', 'hasGrant', 'roleFk', 'password'],
include: {
relation: 'role',
scope: {
fields: ['name']
}
}
}, myOptions);
if (!user.hasGrant) if (!user.hasGrant)
throw new UserError(`You don't have enough privileges`); throw new UserError(`You don't have grant privilege`);
const hasRoleFromUser = await models.Account.hasRole(userId, userToUpdate.role().name, myOptions);
if (!hasRoleFromUser)
throw new UserError(`You don't own the role and you can't assign it to another user`);
const userToUpdate = await models.Account.findById(id);
if (hasGrant != null) if (hasGrant != null)
return await userToUpdate.updateAttribute('hasGrant', hasGrant, myOptions); userToUpdate.hasGrant = hasGrant;
if (!roleFk) return;
const role = await models.Role.findById(roleFk, null, myOptions); if (roleFk) {
const role = await models.Role.findById(roleFk, {fields: ['name']}, myOptions);
const hasRole = await models.Account.hasRole(userId, role.name, myOptions); const hasRole = await models.Account.hasRole(userId, role.name, myOptions);
if (!hasRole) if (!hasRole)
throw new UserError(`You don't have enough privileges`); throw new UserError(`You don't own the role and you can't assign it to another user`);
await userToUpdate.updateAttribute('roleFk', roleFk, myOptions); userToUpdate.roleFk = roleFk;
}
await userToUpdate.save(userToUpdate);
await models.UserAccount.sync(userToUpdate.name);
}; };
}; };

View File

@ -4,7 +4,9 @@ describe('account privileges()', () => {
const employeeId = 1; const employeeId = 1;
const developerId = 9; const developerId = 9;
const sysadminId = 66; const sysadminId = 66;
const bruceWayneId = 1101; const itBossId = 104;
const rootId = 100;
const clarkKent = 1103;
it('should throw an error when user not has privileges', async() => { it('should throw an error when user not has privileges', async() => {
const ctx = {req: {accessToken: {userId: developerId}}}; const ctx = {req: {accessToken: {userId: developerId}}};
@ -22,7 +24,7 @@ describe('account privileges()', () => {
await tx.rollback(); await tx.rollback();
} }
expect(error.message).toContain(`You don't have enough privileges`); expect(error.message).toContain(`You don't have grant privilege`);
}); });
it('should throw an error when user has privileges but not has the role', async() => { it('should throw an error when user has privileges but not has the role', async() => {
@ -33,12 +35,7 @@ describe('account privileges()', () => {
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
const root = await models.Role.findOne({ await models.Account.privileges(ctx, employeeId, rootId, null, options);
where: {
name: 'root'
}
}, options);
await models.Account.privileges(ctx, employeeId, root.id, null, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
@ -46,7 +43,26 @@ describe('account privileges()', () => {
await tx.rollback(); await tx.rollback();
} }
expect(error.message).toContain(`You don't have enough privileges`); expect(error.message).toContain(`You don't own the role and you can't assign it to another user`);
});
it('should throw an error when user has privileges but not has the role from user', async() => {
const ctx = {req: {accessToken: {userId: sysadminId}}};
const tx = await models.Account.beginTransaction({});
let error;
try {
const options = {transaction: tx};
await models.Account.privileges(ctx, itBossId, developerId, null, options);
await tx.rollback();
} catch (e) {
error = e;
await tx.rollback();
}
expect(error.message).toContain(`You don't own the role and you can't assign it to another user`);
}); });
it('should change role', async() => { it('should change role', async() => {
@ -63,8 +79,8 @@ describe('account privileges()', () => {
let error; let error;
let result; let result;
try { try {
await models.Account.privileges(ctx, bruceWayneId, agency.id, null, options); await models.Account.privileges(ctx, clarkKent, agency.id, null, options);
result = await models.Account.findById(bruceWayneId, null, options); result = await models.Account.findById(clarkKent, null, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
@ -84,8 +100,8 @@ describe('account privileges()', () => {
let result; let result;
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
await models.Account.privileges(ctx, bruceWayneId, null, true, options); await models.Account.privileges(ctx, clarkKent, null, true, options);
result = await models.Account.findById(bruceWayneId, null, options); result = await models.Account.findById(clarkKent, null, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -102,6 +102,13 @@
"principalType": "ROLE", "principalType": "ROLE",
"principalId": "$authenticated", "principalId": "$authenticated",
"permission": "ALLOW" "permission": "ALLOW"
},
{
"property": "privileges",
"accessType": "*",
"principalType": "ROLE",
"principalId": "$authenticated",
"permission": "ALLOW"
} }
] ]
} }

View File

@ -45,8 +45,8 @@ INSERT INTO `account`.`roleConfig`(`id`, `mysqlPassword`, `rolePrefix`, `userPre
CALL `account`.`role_sync`; CALL `account`.`role_sync`;
INSERT INTO `account`.`user`(`id`,`name`, `nickname`, `password`,`role`,`active`,`email`, `lang`, `image`) INSERT INTO `account`.`user`(`id`,`name`, `nickname`, `password`,`role`,`active`,`email`, `lang`, `image`, `bcryptPassword`)
SELECT id, name, CONCAT(name, 'Nick'),MD5('nightmare'), id, 1, CONCAT(name, '@mydomain.com'), 'en', '4fa3ada0-3ac4-11eb-9ab8-27f6fc3b85fd' SELECT id, name, CONCAT(name, 'Nick'),MD5('nightmare'), id, 1, CONCAT(name, '@mydomain.com'), 'en', '4fa3ada0-3ac4-11eb-9ab8-27f6fc3b85fd', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2'
FROM `account`.`role` WHERE id <> 20 FROM `account`.`role` WHERE id <> 20
ORDER BY id; ORDER BY id;

View File

@ -29,4 +29,13 @@ describe('Account LDAP path', () => {
expect(message.text).toContain('Data saved!'); expect(message.text).toContain('Data saved!');
}); });
it('should reset data', async() => {
await page.waitToClick(selectors.accountLdap.checkEnable);
await page.waitToClick(selectors.accountLdap.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
}); });

View File

@ -29,4 +29,13 @@ describe('Account Samba path', () => {
expect(message.text).toContain('Data saved!'); expect(message.text).toContain('Data saved!');
}); });
it('should reset data', async() => {
await page.waitToClick(selectors.accountSamba.checkEnable);
await page.waitToClick(selectors.accountSamba.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
}); });

View File

@ -24,7 +24,7 @@ describe('Account privileges path', () => {
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
expect(message.text).toContain(`You don't have enough privileges`); expect(message.text).toContain(`You don't have grant privilege`);
}); });
it('should throw error when change role', async() => { it('should throw error when change role', async() => {
@ -33,7 +33,7 @@ describe('Account privileges path', () => {
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
expect(message.text).toContain(`You don't have enough privileges`); expect(message.text).toContain(`You don't have grant privilege`);
}); });
}); });
@ -56,7 +56,16 @@ describe('Account privileges path', () => {
expect(result).toBe('checked'); expect(result).toBe('checked');
}); });
it('should change role', async() => { it('should throw error when change role and not own role', async() => {
await page.autocompleteSearch(selectors.accountPrivileges.role, 'itBoss');
await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain(`You don't own the role and you can't assign it to another user`);
});
it('should change role to employee', async() => {
await page.autocompleteSearch(selectors.accountPrivileges.role, 'employee'); await page.autocompleteSearch(selectors.accountPrivileges.role, 'employee');
await page.waitToClick(selectors.accountPrivileges.save); await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
@ -67,6 +76,18 @@ describe('Account privileges path', () => {
expect(message.text).toContain(`Data saved!`); expect(message.text).toContain(`Data saved!`);
expect(result).toContain('employee'); expect(result).toContain('employee');
}); });
it('should return role to developer', async() => {
await page.autocompleteSearch(selectors.accountPrivileges.role, 'developer');
await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar();
await page.reloadSection('account.card.privileges');
const result = await page.waitToGetProperty(selectors.accountPrivileges.role, 'value');
expect(message.text).toContain(`Data saved!`);
expect(result).toContain('developer');
});
}); });
describe('as developer again', () => { describe('as developer again', () => {
@ -76,7 +97,12 @@ describe('Account privileges path', () => {
await page.waitToClick(selectors.accountPrivileges.checkHasGrant); await page.waitToClick(selectors.accountPrivileges.checkHasGrant);
await page.waitToClick(selectors.accountPrivileges.save); await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain(`Data saved!`);
});
it('should logIn in developer', async() => {
await page.reloadSection('account.card.privileges'); await page.reloadSection('account.card.privileges');
const result = await page.checkboxState(selectors.accountPrivileges.checkHasGrant); const result = await page.checkboxState(selectors.accountPrivileges.checkHasGrant);

View File

@ -134,5 +134,7 @@
"Password does not meet requirements": "Password does not meet requirements", "Password does not meet requirements": "Password does not meet requirements",
"You don't have privileges to change the zone": "You don't have privileges to change the zone or for these parameters there are more than one shipping options, talk to agencies", "You don't have privileges to change the zone": "You don't have privileges to change the zone or for these parameters there are more than one shipping options, talk to agencies",
"Not enough privileges to edit a client": "Not enough privileges to edit a client", "Not enough privileges to edit a client": "Not enough privileges to edit a client",
"Claim pickup order sent": "Claim pickup order sent [({{claimId}})]({{{claimUrl}}}) to client *{{clientName}}*" "Claim pickup order sent": "Claim pickup order sent [({{claimId}})]({{{claimUrl}}}) to client *{{clientName}}*",
"You don't have grant privilege": "You don't have grant privilege",
"You don't own the role and you can't assign it to another user": "You don't own the role and you can't assign it to another user"
} }

View File

@ -236,5 +236,7 @@
"Modifiable user details only by an administrator": "Detalles de usuario modificables solo por un administrador", "Modifiable user details only by an administrator": "Detalles de usuario modificables solo por un administrador",
"Modifiable password only via recovery or by an administrator": "Contraseña modificable solo a través de la recuperación o por un administrador", "Modifiable password only via recovery or by an administrator": "Contraseña modificable solo a través de la recuperación o por un administrador",
"Not enough privileges to edit a client": "No tienes suficientes privilegios para editar un cliente", "Not enough privileges to edit a client": "No tienes suficientes privilegios para editar un cliente",
"Claim pickup order sent": "Reclamación Orden de recogida enviada [({{claimId}})]({{{claimUrl}}}) al cliente *{{clientName}}*" "Claim pickup order sent": "Reclamación Orden de recogida enviada [({{claimId}})]({{{claimUrl}}}) al cliente *{{clientName}}*",
"You don't have grant privilege": "No tienes privilegios para dar privilegios",
"You don't own the role and you can't assign it to another user": "No eres el propietario del rol y no puedes asignarlo a otro usuario"
} }

View File

@ -1,2 +1,2 @@
Privileges: Privilegios Privileges: Privilegios
Has grant: Tiene privilegios Has grant: Puede delegar privilegios

View File

@ -8,8 +8,8 @@
}, },
"properties": { "properties": {
"id": { "id": {
"type": "number",
"id": true, "id": true,
"type": "number",
"description": "Identifier" "description": "Identifier"
}, },
"code": { "code": {

View File

@ -57,11 +57,11 @@
"model": "ClaimState", "model": "ClaimState",
"foreignKey": "claimStateFk" "foreignKey": "claimStateFk"
}, },
"claimRma": { "rmas": {
"type": "belongsTo", "type": "hasMany",
"model": "ClaimRma", "model": "ClaimRma",
"foreignKey": "rma", "foreignKey": "code",
"primaryKey": "code" "primaryKey": "rma"
}, },
"client": { "client": {
"type": "belongsTo", "type": "belongsTo",

View File

@ -425,6 +425,7 @@ module.exports = Self => {
account.observe('before save', async ctx => { account.observe('before save', async ctx => {
if (ctx.isNewInstance) return; if (ctx.isNewInstance) return;
if (ctx.currentInstance)
ctx.hookState.oldInstance = JSON.parse(JSON.stringify(ctx.currentInstance)); ctx.hookState.oldInstance = JSON.parse(JSON.stringify(ctx.currentInstance));
}); });
@ -432,7 +433,11 @@ module.exports = Self => {
const changes = ctx.data || ctx.instance; const changes = ctx.data || ctx.instance;
if (!ctx.isNewInstance && changes) { if (!ctx.isNewInstance && changes) {
const oldData = ctx.hookState.oldInstance; const oldData = ctx.hookState.oldInstance;
const hasChanges = oldData.name != changes.name || oldData.active != changes.active; let hasChanges;
if (oldData)
hasChanges = oldData.name != changes.name || oldData.active != changes.active;
if (!hasChanges) return; if (!hasChanges) return;
const isClient = await Self.app.models.Client.count({id: oldData.id}); const isClient = await Self.app.models.Client.count({id: oldData.id});

View File

@ -38,7 +38,7 @@
"node-ssh": "^11.0.0", "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",
"puppeteer": "^19.2.0", "puppeteer": "^19.0.0",
"read-chunk": "^3.2.0", "read-chunk": "^3.2.0",
"require-yaml": "0.0.1", "require-yaml": "0.0.1",
"sharp": "^0.31.0", "sharp": "^0.31.0",

View File

@ -21,7 +21,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true required: true
}, },
from: { from: {

View File

@ -23,7 +23,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true required: true
} }
} }

View File

@ -23,7 +23,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true required: true
}, },
from: { from: {

View File

@ -18,7 +18,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true required: true
} }
} }

View File

@ -10,7 +10,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true required: true
} }
} }

View File

@ -10,7 +10,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true required: true
} }
} }

View File

@ -10,7 +10,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true required: true
} }
} }

View File

@ -23,12 +23,12 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true, required: true,
description: 'The client id' description: 'The client id'
}, },
companyId: { companyId: {
type: [Number, String], type: Number,
required: true required: true
} }
} }

View File

@ -18,7 +18,7 @@ module.exports = {
}, },
props: { props: {
reference: { reference: {
type: [Number, String], type: Number,
required: true required: true
} }
} }

View File

@ -34,11 +34,11 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true required: true
}, },
companyId: { companyId: {
type: [Number, String], type: Number,
required: true required: true
} }
} }

View File

@ -34,11 +34,11 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true required: true
}, },
companyId: { companyId: {
type: [Number, String], type: Number,
required: true required: true
}, },
} }

View File

@ -26,7 +26,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true, required: true,
description: 'The client id' description: 'The client id'
} }

View File

@ -24,7 +24,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true required: true
} }
} }

View File

@ -16,11 +16,11 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true required: true
}, },
companyId: { companyId: {
type: [Number, String], type: Number,
required: true required: true
} }
} }

View File

@ -21,7 +21,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true required: true
}, },
from: { from: {

View File

@ -25,7 +25,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true, required: true,
description: 'The client id' description: 'The client id'
}, },

View File

@ -32,7 +32,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true, required: true,
description: 'The claim id' description: 'The claim id'
} }

View File

@ -69,7 +69,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true, required: true,
description: 'The client id' description: 'The client id'
}, },

View File

@ -127,7 +127,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true, required: true,
description: 'The ticket id' description: 'The ticket id'
}, },

View File

@ -44,7 +44,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true, required: true,
description: 'The route id' description: 'The route id'
} }

View File

@ -40,7 +40,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true, required: true,
description: 'The entry id' description: 'The entry id'
} }

View File

@ -30,7 +30,7 @@ module.exports = {
}, },
props: { props: {
reference: { reference: {
type: [Number, String], type: Number,
required: true, required: true,
description: 'The invoice ref' description: 'The invoice ref'
} }

View File

@ -21,12 +21,12 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true, required: true,
description: 'The client id' description: 'The client id'
}, },
companyId: { companyId: {
type: [Number, String], type: Number,
required: true required: true
} }
} }

View File

@ -32,7 +32,7 @@ module.exports = {
}, },
props: { props: {
reference: { reference: {
type: [Number, String], type: Number,
required: true, required: true,
description: 'The invoice ref' description: 'The invoice ref'
} }

View File

@ -63,12 +63,12 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true, required: true,
description: 'The client id' description: 'The client id'
}, },
companyId: { companyId: {
type: [Number, String], type: Number,
required: true required: true
} }
} }

View File

@ -25,7 +25,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true, required: true,
description: 'Receipt id' description: 'Receipt id'
} }

View File

@ -40,12 +40,12 @@ const rptSepaCore = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true, required: true,
description: 'The client id' description: 'The client id'
}, },
companyId: { companyId: {
type: [Number, String], type: Number,
required: true required: true
} }
} }

View File

@ -49,7 +49,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true, required: true,
description: 'The supplier id' description: 'The supplier id'
}, },

View File

@ -13,7 +13,7 @@ module.exports = {
}, },
props: { props: {
id: { id: {
type: [Number, String], type: Number,
required: true, required: true,
description: 'The zone id' description: 'The zone id'
} }