test passed
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
parent
e16d1761f9
commit
ea1cc68d29
|
@ -5,6 +5,7 @@ e2e/dms/*/
|
|||
!e2e/dms/c4c
|
||||
!e2e/dms/c81
|
||||
!e2e/dms/ecc
|
||||
!e2e/dms/a87
|
||||
npm-debug.log
|
||||
.eslintcache
|
||||
datasources.*.json
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
const UserError = require('vn-loopback/util/user-error');
|
||||
|
||||
checkRole = async function(ctx, id) {
|
||||
const models = Self.app.models;
|
||||
const dms = await Self.findById(id);
|
||||
|
||||
const hasReadRole = await models.DmsType.hasReadRole(ctx, dms.dmsTypeFk);
|
||||
if (!hasReadRole)
|
||||
throw new UserError(`You don't have enough privileges`);
|
||||
|
||||
return true;
|
||||
};
|
|
@ -1,7 +1,4 @@
|
|||
|
||||
const checkRole = require('./checkRole');
|
||||
const getfile = require('./getfile');
|
||||
|
||||
const UserError = require('vn-loopback/util/user-error');
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('downloadFile', {
|
||||
description: 'Download a document',
|
||||
|
@ -36,7 +33,8 @@ module.exports = Self => {
|
|||
});
|
||||
|
||||
Self.downloadFile = async function(ctx, id) {
|
||||
await checkRole(ctx, id);
|
||||
return await getfile(ctx, id);
|
||||
if (!await Self.checkRole(ctx, id))
|
||||
throw new UserError(`You don't have enough privileges`);
|
||||
return await Self.getFile(id);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
const UserError = require('vn-loopback/util/user-error');
|
||||
|
||||
getFile = async function(ctx, id) {
|
||||
const storageConnector = Self.app.dataSources.storage.connector;
|
||||
const models = Self.app.models;
|
||||
const dms = await Self.findById(id);
|
||||
|
||||
const hasReadRole = await models.DmsType.hasReadRole(ctx, dms.dmsTypeFk);
|
||||
if (!hasReadRole)
|
||||
|
||||
throw new UserError(`You don't have enough privileges`);
|
||||
|
||||
const pathHash = storageConnector.getPathHash(dms.id);
|
||||
try {
|
||||
await models.Container.getFile(pathHash, dms.file);
|
||||
} catch (e) {
|
||||
if (e.code != 'ENOENT')
|
||||
throw e;
|
||||
|
||||
const error = new UserError(`File doesn't exists`);
|
||||
error.statusCode = 404;
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
const stream = models.Container.downloadStream(pathHash, dms.file);
|
||||
|
||||
return [stream, dms.contentType, `filename="${dms.file}"`];
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
fdescribe('dms downloadFile()', () => {
|
||||
describe('dms downloadFile()', () => {
|
||||
let dmsId = 1;
|
||||
|
||||
it('should return a response for an employee with text content-type', async() => {
|
||||
|
@ -11,7 +11,7 @@ fdescribe('dms downloadFile()', () => {
|
|||
expect(result[1]).toEqual('text/plain');
|
||||
});
|
||||
|
||||
it(`should return an error for a user without enough privileges`, async() => {
|
||||
it('should return an error for a user without enough privileges', async() => {
|
||||
let clientId = 101;
|
||||
let ctx = {req: {accessToken: {userId: clientId}}};
|
||||
|
||||
|
|
|
@ -1,6 +1,37 @@
|
|||
const UserError = require('vn-loopback/util/user-error');
|
||||
|
||||
module.exports = Self => {
|
||||
require('../methods/dms/downloadFile')(Self);
|
||||
require('../methods/dms/uploadFile')(Self);
|
||||
require('../methods/dms/removeFile')(Self);
|
||||
require('../methods/dms/updateFile')(Self);
|
||||
|
||||
Self.checkRole = async function(ctx, id) {
|
||||
const models = Self.app.models;
|
||||
const dms = await Self.findById(id);
|
||||
|
||||
return await models.DmsType.hasReadRole(ctx, dms.dmsTypeFk);
|
||||
};
|
||||
|
||||
Self.getFile = async function(id) {
|
||||
const storageConnector = Self.app.dataSources.storage.connector;
|
||||
const models = Self.app.models;
|
||||
const dms = await Self.findById(id);
|
||||
const pathHash = storageConnector.getPathHash(dms.id);
|
||||
try {
|
||||
await models.Container.getFile(pathHash, dms.file);
|
||||
} catch (e) {
|
||||
if (e.code != 'ENOENT')
|
||||
throw e;
|
||||
|
||||
const error = new UserError(`File doesn't exists`);
|
||||
error.statusCode = 404;
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
const stream = models.Container.downloadStream(pathHash, dms.file);
|
||||
|
||||
return [stream, dms.contentType, `filename="${dms.file}"`];
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
describe('Dms', () => {
|
||||
const Dms = app.models.Dms;
|
||||
|
||||
describe('getFile()', () => {
|
||||
it('should return a response with text content-type', async() => {
|
||||
const result = await Dms.getFile(1);
|
||||
|
||||
expect(result[1]).toEqual('text/plain');
|
||||
});
|
||||
|
||||
it('should return an error for a file does not exists', async() => {
|
||||
let error = {};
|
||||
try {
|
||||
await Dms.getFile(6);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
expect(error.statusCode).toBe(404);
|
||||
});
|
||||
|
||||
it('should return an error for a record does not exists', async() => {
|
||||
let error = {};
|
||||
try {
|
||||
await app.models.Dms.getFile('NotExistentId');
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
expect(error.statusCode).not.toBe(404);
|
||||
expect(error).toEqual(jasmine.any(Error));
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkRole()', () => {
|
||||
const dmsId = 1;
|
||||
it('should return a true for an employee with permission', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 107}}};
|
||||
const result = await Dms.checkRole(ctx, dmsId);
|
||||
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return false for an employee without permission', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 101}}};
|
||||
const result = await Dms.checkRole(ctx, dmsId);
|
||||
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||
VALUES
|
||||
('WorkerDms', 'filter', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('WorkerDms', 'downloadFile', 'READ', 'ALLOW', 'ROLE', 'employee');
|
||||
DELETE FROM `salix`.`ACL` WHERE (`id` = '205');
|
|
@ -1957,7 +1957,9 @@ INSERT INTO `vn`.`dms`(`id`, `dmsTypeFk`, `file`, `contentType`, `workerFk`, `wa
|
|||
(2, 5, '2.txt', 'text/plain', 5, 1, 442, 1, TRUE, 'Client:104', 'Client:104 dms for the client', CURDATE()),
|
||||
(3, 5, '3.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'Client: 104', 'Client:104 readme', CURDATE()),
|
||||
(4, 3, '4.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'Worker: 106', 'Worker:106 readme', CURDATE()),
|
||||
(5, 5, '5.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'travel: 1', 'dmsForThermograph', CURDATE());
|
||||
(5, 5, '5.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'travel: 1', 'dmsForThermograph', CURDATE()),
|
||||
(6, 5, '6.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'NotExists', 'DoesNotExists', CURDATE());
|
||||
|
||||
|
||||
INSERT INTO `vn`.`ticketDms`(`ticketFk`, `dmsFk`)
|
||||
VALUES
|
||||
|
@ -1968,9 +1970,10 @@ INSERT INTO `vn`.`clientDms`(`clientFk`, `dmsFk`)
|
|||
(104, 2),
|
||||
(104, 3);
|
||||
|
||||
INSERT INTO `vn`.`workerDocument`(`id`, `worker`, `document`)
|
||||
INSERT INTO `vn`.`workerDocument`(`id`, `worker`, `document`,`isReadableByWorker`)
|
||||
VALUES
|
||||
(1, 106, 4);
|
||||
(1, 106, 4, TRUE),
|
||||
(2, 107, 3, FALSE);
|
||||
|
||||
INSERT INTO `vn`.`device` (`sn`, `model`, `userFk`)
|
||||
VALUES
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
File: 4.txt. It works!
|
|
@ -0,0 +1,40 @@
|
|||
const UserError = require('vn-loopback/util/user-error');
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('downloadFile', {
|
||||
description: 'Download a worker document',
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'Number',
|
||||
description: 'The document id',
|
||||
http: {source: 'path'}
|
||||
}
|
||||
],
|
||||
returns: [
|
||||
{
|
||||
arg: 'body',
|
||||
type: 'file',
|
||||
root: true
|
||||
}, {
|
||||
arg: 'Content-Type',
|
||||
type: 'String',
|
||||
http: {target: 'header'}
|
||||
}, {
|
||||
arg: 'Content-Disposition',
|
||||
type: 'String',
|
||||
http: {target: 'header'}
|
||||
}
|
||||
],
|
||||
http: {
|
||||
path: `/:id/downloadFile`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.downloadFile = async function(ctx, id) {
|
||||
if (!await Self.app.models.Dms.checkRole(ctx, id) && !await Self.isMine(ctx, id))
|
||||
throw new UserError(`You don't have enough privileges`);
|
||||
return await Self.app.models.Dms.getFile(id);
|
||||
};
|
||||
};
|
|
@ -0,0 +1,53 @@
|
|||
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('filter', {
|
||||
description: 'Find all instances of the model matched by filter from the data source.',
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'filter',
|
||||
type: 'Object',
|
||||
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
||||
http: {source: 'query'}
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: ['Object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/filter`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.filter = async(ctx, filter) => {
|
||||
const conn = Self.dataSource.connector;
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
|
||||
const account = await Self.app.models.Account.findById(userId);
|
||||
const stmt = new ParameterizedSQL(
|
||||
`SELECT d.id dmsFk, d.reference, d.description, d.file, d.created
|
||||
FROM workerDocument wd
|
||||
JOIN dms d ON d.id = wd.document
|
||||
JOIN dmsType dt ON dt.id = d.dmsTypeFk
|
||||
LEFT JOIN account.roleRole rr ON rr.inheritsFrom = dt.readRoleFk AND rr.role = ?
|
||||
`, [account.roleFk]
|
||||
);
|
||||
const oldWhere = filter.where;
|
||||
const yourOwnDms = {and: [{isReadableByWorker: true}, {worker: userId}]};
|
||||
|
||||
filter.where = {
|
||||
and: [{
|
||||
or: [yourOwnDms, {
|
||||
role: {
|
||||
neq: null
|
||||
}
|
||||
}]
|
||||
}, oldWhere]};
|
||||
stmt.merge(conn.makeSuffix(filter));
|
||||
|
||||
return await conn.executeStmt(stmt);
|
||||
};
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
describe('worker-dms downloadFile()', () => {
|
||||
let dmsId = 4;
|
||||
|
||||
it('should return a response for an employee with text content-type', async() => {
|
||||
let workerId = 106;
|
||||
let ctx = {req: {accessToken: {userId: workerId}}};
|
||||
const result = await app.models.WorkerDms.downloadFile(ctx, dmsId);
|
||||
|
||||
expect(result[1]).toEqual('text/plain');
|
||||
});
|
||||
|
||||
it('should return an error for a user without enough privileges', async() => {
|
||||
let clientId = 1;
|
||||
let ctx = {req: {accessToken: {userId: clientId}}};
|
||||
|
||||
let error;
|
||||
try {
|
||||
await app.models.WorkerDms.downloadFile(ctx, dmsId);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
});
|
|
@ -125,7 +125,6 @@ module.exports = Self => {
|
|||
LEFT JOIN account.emailUser mu ON mu.userFk = u.id`
|
||||
);
|
||||
|
||||
|
||||
stmt.merge(conn.makeSuffix(filter));
|
||||
let itemsIndex = stmts.push(stmt) - 1;
|
||||
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
module.exports = Self => {
|
||||
require('../methods/worker-dms/downloadFile')(Self);
|
||||
require('../methods/worker-dms/removeFile')(Self);
|
||||
require('../methods/worker-dms/allowedContentTypes')(Self);
|
||||
// require('../methods/worker-dms/filter')(Self);
|
||||
require('../methods/worker-dms/filter')(Self);
|
||||
|
||||
Self.isMine = async function(ctx, dmsId) {
|
||||
const myUserId = ctx.req.accessToken.userId;
|
||||
let workerDms = await Self.findOne({
|
||||
where: {
|
||||
workerFk: myUserId,
|
||||
dmsFk: dmsId
|
||||
}
|
||||
});
|
||||
return workerDms !== null;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="filter"
|
||||
link="{workerFk: $ctrl.$params.id}"
|
||||
filter="::$ctrl.filter"
|
||||
url="WorkerDms/filter"
|
||||
link="{worker: $ctrl.$params.id}"
|
||||
limit="20"
|
||||
data="$ctrl.workerDms"
|
||||
order="dmsFk DESC"
|
||||
|
@ -28,28 +27,28 @@
|
|||
<vn-tbody>
|
||||
<vn-tr ng-repeat="document in $ctrl.workerDms">
|
||||
<vn-td number shrink>{{::document.dmsFk}}</vn-td>
|
||||
<vn-td shrink>
|
||||
<span title="{{::document.dms.reference}}">
|
||||
{{::document.dms.reference}}
|
||||
<vn-td expand>
|
||||
<span title="{{::document.reference}}">
|
||||
{{::document.reference}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td expand>
|
||||
<span title="{{::document.dms.description}}">
|
||||
{{::document.dms.description}}
|
||||
<span title="{{::document.description}}">
|
||||
{{::document.description}}
|
||||
</span>
|
||||
</vn-td>
|
||||
</vn-td >
|
||||
<vn-td shrink>
|
||||
<a target="_blank"
|
||||
title="{{'Download file' | translate}}"
|
||||
href="api/dms/{{::document.dmsFk}}/downloadFile?access_token={{::$ctrl.vnToken.token}}">{{::document.dms.file}}
|
||||
href="api/workerDms/{{::document.dmsFk}}/downloadFile?access_token={{::$ctrl.vnToken.token}}">{{::document.file}}
|
||||
</a>
|
||||
</vn-td>
|
||||
<vn-td>
|
||||
{{::document.dms.created | date:'dd/MM/yyyy HH:mm'}}
|
||||
{{::document.created | date:'dd/MM/yyyy HH:mm'}}
|
||||
</vn-td>
|
||||
<vn-td shrink>
|
||||
<a target="_blank"
|
||||
href="api/dms/{{::document.dmsFk}}/downloadFile?access_token={{::$ctrl.vnToken.token}}">
|
||||
href="api/workerDms/{{::document.dmsFk}}/downloadFile?access_token={{::$ctrl.vnToken.token}}">
|
||||
<vn-icon-button
|
||||
icon="cloud_download"
|
||||
title="{{'Download file' | translate}}">
|
||||
|
@ -57,7 +56,7 @@
|
|||
</a>
|
||||
</vn-td>
|
||||
<vn-td shrink>
|
||||
<vn-icon-button ui-sref="worker.card.dms.edit({dmsId: {{::document.dmsFk}}})"
|
||||
<vn-icon-button ui-sref="worker.card.edit({dmsId: {{::document.dmsFk}}})"
|
||||
icon="edit"
|
||||
title="{{'Edit file' | translate}}">
|
||||
</vn-icon-button>
|
||||
|
@ -78,7 +77,7 @@
|
|||
<vn-worker-descriptor-popover
|
||||
vn-id="workerDescriptor">
|
||||
</vn-worker-descriptor-popover>
|
||||
<a ui-sref="worker.card.dms.create"
|
||||
<a ui-sref="worker.card.create"
|
||||
vn-tooltip="Upload file"
|
||||
vn-bind="+"
|
||||
fixed-bottom-right>
|
||||
|
|
|
@ -99,7 +99,7 @@
|
|||
"state": "worker.card.dms.index",
|
||||
"component": "vn-worker-dms-index",
|
||||
"description": "My documentation",
|
||||
"acl": ["hr"]
|
||||
"acl": ["employee"]
|
||||
},
|
||||
{
|
||||
"url": "/create",
|
||||
|
|
Loading…
Reference in New Issue