#6930 - Use ScopedToken when share Multimedia files #2094
|
@ -29,7 +29,8 @@ module.exports = Self => {
|
|||
http: {
|
||||
path: `/:id/downloadFile`,
|
||||
verb: 'GET'
|
||||
}
|
||||
},
|
||||
accessScopes: ['read:multimedia']
|
||||
});
|
||||
|
||||
Self.downloadFile = async function(ctx, id) {
|
||||
|
|
|
@ -42,7 +42,8 @@ module.exports = Self => {
|
|||
http: {
|
||||
path: `/:id/download`,
|
||||
verb: 'GET'
|
||||
}
|
||||
},
|
||||
accessScopes: ['read:multimedia']
|
||||
});
|
||||
|
||||
Self.download = async function(id, fileCabinet, filter) {
|
||||
|
|
|
@ -47,7 +47,8 @@ module.exports = Self => {
|
|||
http: {
|
||||
path: `/:collection/:size/:id/download`,
|
||||
verb: 'GET'
|
||||
}
|
||||
},
|
||||
accessScopes: ['read:multimedia']
|
||||
});
|
||||
|
||||
Self.download = async function(ctx, collection, size, id) {
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('shareToken', {
|
||||
description: 'Returns token to view files or images and share it',
|
||||
accessType: 'WRITE',
|
||||
accepts: [],
|
||||
returns: {
|
||||
type: 'Object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/shareToken`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.shareToken = async function(ctx) {
|
||||
const {accessToken: token} = ctx.req;
|
||||
|
||||
const user = await Self.findById(token.userId);
|
||||
const multimediaToken = await user.accessTokens.create({
|
||||
scopes: ['read:multimedia']
|
||||
});
|
||||
|
||||
return {multimediaToken};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
const {models} = require('vn-loopback/server/server');
|
||||
describe('Share Token', () => {
|
||||
let ctx = null;
|
||||
beforeAll(async() => {
|
||||
const unAuthCtx = {
|
||||
req: {
|
||||
headers: {},
|
||||
connection: {
|
||||
remoteAddress: '127.0.0.1'
|
||||
},
|
||||
getLocale: () => 'en'
|
||||
},
|
||||
args: {}
|
||||
};
|
||||
let login = await models.VnUser.signIn(unAuthCtx, 'salesAssistant', 'nightmare');
|
||||
let accessToken = await models.AccessToken.findById(login.token);
|
||||
ctx = {req: {accessToken: accessToken}};
|
||||
});
|
||||
|
||||
it('should renew token', async() => {
|
||||
const multimediaToken = await models.VnUser.shareToken(ctx);
|
||||
|
||||
expect(Object.keys(multimediaToken).length).toEqual(1);
|
||||
expect(multimediaToken.multimediaToken.userId).toEqual(ctx.req.accessToken.userId);
|
||||
expect(multimediaToken.multimediaToken.scopes[0]).toEqual('read:multimedia');
|
||||
});
|
||||
});
|
|
@ -13,6 +13,7 @@ module.exports = function(Self) {
|
|||
require('../methods/vn-user/privileges')(Self);
|
||||
require('../methods/vn-user/validate-auth')(Self);
|
||||
require('../methods/vn-user/renew-token')(Self);
|
||||
require('../methods/vn-user/share-token')(Self);
|
||||
require('../methods/vn-user/update-user')(Self);
|
||||
|
||||
Self.definition.settings.acls = Self.definition.settings.acls.filter(acl => acl.property !== 'create');
|
||||
|
|
|
@ -1,129 +1,140 @@
|
|||
{
|
||||
"name": "VnUser",
|
||||
"base": "User",
|
||||
"validateUpsert": true,
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "account.user"
|
||||
}
|
||||
},
|
||||
"name": "VnUser",
|
||||
"base": "User",
|
||||
"validateUpsert": true,
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "account.user"
|
||||
}
|
||||
},
|
||||
"mixins": {
|
||||
"Loggable": true
|
||||
},
|
||||
"resetPasswordTokenTTL": "604800",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number",
|
||||
"id": true
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number",
|
||||
"id": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"roleFk": {
|
||||
"type": "number",
|
||||
"mysql": {
|
||||
"columnName": "role"
|
||||
}
|
||||
},
|
||||
"nickname": {
|
||||
"type": "string"
|
||||
},
|
||||
"lang": {
|
||||
"type": "string"
|
||||
},
|
||||
"active": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"emailVerified": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"created": {
|
||||
"type": "date"
|
||||
},
|
||||
"updated": {
|
||||
"type": "date"
|
||||
},
|
||||
"image": {
|
||||
"type": "string"
|
||||
},
|
||||
"hasGrant": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"roleFk": {
|
||||
"type": "number",
|
||||
"mysql": {
|
||||
"columnName": "role"
|
||||
}
|
||||
},
|
||||
"nickname": {
|
||||
"type": "string"
|
||||
},
|
||||
"lang": {
|
||||
"type": "string"
|
||||
},
|
||||
"active": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"emailVerified": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"created": {
|
||||
"type": "date"
|
||||
},
|
||||
"updated": {
|
||||
"type": "date"
|
||||
},
|
||||
"image": {
|
||||
"type": "string"
|
||||
},
|
||||
"hasGrant": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"passExpired": {
|
||||
"type": "date"
|
||||
},
|
||||
"twoFactor": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
"role": {
|
||||
"type": "belongsTo",
|
||||
"model": "VnRole",
|
||||
"foreignKey": "roleFk"
|
||||
},
|
||||
"roles": {
|
||||
"type": "hasMany",
|
||||
"model": "RoleRole",
|
||||
"foreignKey": "role",
|
||||
"primaryKey": "roleFk"
|
||||
},
|
||||
"emailUser": {
|
||||
"type": "hasOne",
|
||||
"model": "EmailUser",
|
||||
"foreignKey": "userFk"
|
||||
},
|
||||
"worker": {
|
||||
"type": "hasOne",
|
||||
"model": "Worker",
|
||||
"foreignKey": "id"
|
||||
},
|
||||
"userConfig": {
|
||||
"type": "hasOne",
|
||||
"model": "UserConfig",
|
||||
"foreignKey": "userFk"
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
{
|
||||
"property": "signIn",
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}, {
|
||||
"property": "recoverPassword",
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}, {
|
||||
"property": "validateAuth",
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}, {
|
||||
"property": "privileges",
|
||||
"accessType": "*",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$authenticated",
|
||||
"permission": "ALLOW"
|
||||
}, {
|
||||
"property": "renewToken",
|
||||
"accessType": "WRITE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$authenticated",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
],
|
||||
"twoFactor": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
"role": {
|
||||
"type": "belongsTo",
|
||||
"model": "VnRole",
|
||||
"foreignKey": "roleFk"
|
||||
},
|
||||
"roles": {
|
||||
"type": "hasMany",
|
||||
"model": "RoleRole",
|
||||
"foreignKey": "role",
|
||||
"primaryKey": "roleFk"
|
||||
},
|
||||
"emailUser": {
|
||||
"type": "hasOne",
|
||||
"model": "EmailUser",
|
||||
"foreignKey": "userFk"
|
||||
},
|
||||
"worker": {
|
||||
"type": "hasOne",
|
||||
"model": "Worker",
|
||||
"foreignKey": "id"
|
||||
},
|
||||
"userConfig": {
|
||||
"type": "hasOne",
|
||||
"model": "UserConfig",
|
||||
"foreignKey": "userFk"
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
{
|
||||
"property": "signIn",
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
},
|
||||
{
|
||||
"property": "recoverPassword",
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
},
|
||||
{
|
||||
"property": "validateAuth",
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
},
|
||||
{
|
||||
"property": "privileges",
|
||||
"accessType": "*",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$authenticated",
|
||||
"permission": "ALLOW"
|
||||
},
|
||||
{
|
||||
"property": "renewToken",
|
||||
"accessType": "WRITE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$authenticated",
|
||||
"permission": "ALLOW"
|
||||
},
|
||||
{
|
||||
"property": "shareToken",
|
||||
"accessType": "WRITE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$authenticated",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
],
|
||||
"scopes": {
|
||||
"preview": {
|
||||
"fields": [
|
||||
|
@ -140,7 +151,7 @@
|
|||
"hasGrant",
|
||||
"realm",
|
||||
"email",
|
||||
"emailVerified"
|
||||
"emailVerified"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
-- Place your SQL code here
|
||||
|
||||
|
|
@ -83,22 +83,27 @@ export default class Auth {
|
|||
}
|
||||
|
||||
onLoginOk(json, now, remember) {
|
||||
this.vnToken.set(json.data.token, now, json.data.ttl, remember);
|
||||
|
||||
return this.loadAcls().then(() => {
|
||||
let continueHash = this.$state.params.continue;
|
||||
if (continueHash)
|
||||
this.$window.location = continueHash;
|
||||
else
|
||||
this.$state.go('home');
|
||||
});
|
||||
return this.$http.get('VnUsers/ShareToken', {
|
||||
headers: {Authorization: json.data.token}
|
||||
}).then(({data}) => {
|
||||
this.vnToken.set(json.data.token, data.multimediaToken.id, now, json.data.ttl, remember);
|
||||
this.loadAcls().then(() => {
|
||||
let continueHash = this.$state.params.continue;
|
||||
if (continueHash)
|
||||
this.$window.location = continueHash;
|
||||
else
|
||||
this.$state.go('home');
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.$http.post('Accounts/logout', null, {headers: {'Authorization': this.vnToken.tokenMultimedia},
|
||||
}).catch(() => {});
|
||||
|
||||
let promise = this.$http.post('VnUsers/logout', null, {
|
||||
headers: {Authorization: this.vnToken.token}
|
||||
}).catch(() => {});
|
||||
|
||||
this.vnToken.unset();
|
||||
this.loggedIn = false;
|
||||
this.vnModules.reset();
|
||||
|
|
|
@ -19,7 +19,7 @@ function interceptor($q, vnApp, $translate) {
|
|||
|
||||
if (config.url.charAt(0) !== '/' && apiPath)
|
||||
config.url = `${apiPath}${config.url}`;
|
||||
if (token)
|
||||
if (token && !config.headers.Authorization)
|
||||
config.headers.Authorization = token;
|
||||
if ($translate.use())
|
||||
config.headers['Accept-Language'] = $translate.use();
|
||||
|
|
|
@ -24,21 +24,22 @@ export default class Token {
|
|||
} catch (e) {}
|
||||
}
|
||||
|
||||
set(token, created, ttl, remember) {
|
||||
set(token, tokenMultimedia, created, ttl, remember) {
|
||||
this.unset();
|
||||
|
||||
Object.assign(this, {
|
||||
token,
|
||||
tokenMultimedia,
|
||||
created,
|
||||
ttl,
|
||||
remember
|
||||
});
|
||||
this.vnInterceptor.setToken(token);
|
||||
this.vnInterceptor.setToken(token, tokenMultimedia);
|
||||
try {
|
||||
if (remember)
|
||||
this.setStorage(localStorage, token, created, ttl);
|
||||
this.setStorage(localStorage, token, tokenMultimedia, created, ttl);
|
||||
else
|
||||
this.setStorage(sessionStorage, token, created, ttl);
|
||||
this.setStorage(sessionStorage, token, tokenMultimedia, created, ttl);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
@ -46,6 +47,7 @@ export default class Token {
|
|||
|
||||
unset() {
|
||||
this.token = null;
|
||||
this.tokenMultimedia = null;
|
||||
this.created = null;
|
||||
this.ttl = null;
|
||||
this.remember = null;
|
||||
|
@ -57,13 +59,15 @@ export default class Token {
|
|||
|
||||
getStorage(storage) {
|
||||
this.token = storage.getItem('vnToken');
|
||||
this.tokenMultimedia = storage.getItem('vnTokenMultimedia');
|
||||
if (!this.token) return;
|
||||
const created = storage.getItem('vnTokenCreated');
|
||||
this.created = created && new Date(created);
|
||||
this.ttl = storage.getItem('vnTokenTtl');
|
||||
}
|
||||
|
||||
setStorage(storage, token, created, ttl) {
|
||||
setStorage(storage, token, tokenMultimedia, created, ttl) {
|
||||
storage.setItem('vnTokenMultimedia', tokenMultimedia);
|
||||
storage.setItem('vnToken', token);
|
||||
storage.setItem('vnTokenCreated', created.toJSON());
|
||||
storage.setItem('vnTokenTtl', ttl);
|
||||
|
@ -71,6 +75,7 @@ export default class Token {
|
|||
|
||||
removeStorage(storage) {
|
||||
storage.removeItem('vnToken');
|
||||
storage.removeItem('vnTokenMultimedia');
|
||||
storage.removeItem('vnTokenCreated');
|
||||
storage.removeItem('vnTokenTtl');
|
||||
}
|
||||
|
|
|
@ -23,8 +23,7 @@ export class Layout extends Component {
|
|||
if (!this.$.$root.user) return;
|
||||
|
||||
const userId = this.$.$root.user.id;
|
||||
const token = this.vnToken.token;
|
||||
return `/api/Images/user/160x160/${userId}/download?access_token=${token}`;
|
||||
return `/api/Images/user/160x160/${userId}/download?access_token=${this.vnToken.tokenMultimedia}`;
|
||||
}
|
||||
|
||||
refresh() {
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
ng-click="$ctrl.showDescriptor($event, userLog)">
|
||||
<img
|
||||
ng-if="::userLog.user.image"
|
||||
ng-src="/api/Images/user/160x160/{{::userLog.userFk}}/download?access_token={{::$ctrl.vnToken.token}}">
|
||||
ng-src="/api/Images/user/160x160/{{::userLog.userFk}}/download?access_token={{::$ctrl.vnToken.tokenMultimedia}}">
|
||||
</img>
|
||||
</vn-avatar>
|
||||
</div>
|
||||
|
@ -181,7 +181,7 @@
|
|||
val="{{::nickname}}">
|
||||
<img
|
||||
ng-if="::image"
|
||||
ng-src="/api/Images/user/160x160/{{::id}}/download?access_token={{::$ctrl.vnToken.token}}">
|
||||
ng-src="/api/Images/user/160x160/{{::id}}/download?access_token={{::$ctrl.vnToken.tokenMultimedia}}">
|
||||
</img>
|
||||
</vn-avatar>
|
||||
<div>
|
||||
|
|
|
@ -13,7 +13,7 @@ export function run($window, $rootScope, vnAuth, vnApp, vnToken, $state) {
|
|||
if (!collection || !size || !id) return;
|
||||
|
||||
const basePath = `/api/Images/${collection}/${size}/${id}`;
|
||||
return `${basePath}/download?access_token=${vnToken.token}`;
|
||||
return `${basePath}/download?access_token=${vnToken.tokenMultimedia}`;
|
||||
};
|
||||
|
||||
$window.validations = {};
|
||||
|
|
|
@ -15,7 +15,8 @@ module.exports = Self => {
|
|||
http: {
|
||||
path: `/logout`,
|
||||
verb: 'POST'
|
||||
}
|
||||
},
|
||||
accessScopes: ['DEFAULT', 'read:multimedia']
|
||||
});
|
||||
|
||||
Self.logout = async ctx => Self.app.models.VnUser.logout(ctx.req.accessToken.id);
|
||||
|
|
|
@ -32,7 +32,8 @@ module.exports = Self => {
|
|||
http: {
|
||||
path: `/:id/downloadFile`,
|
||||
verb: 'GET'
|
||||
}
|
||||
},
|
||||
accessScopes: ['read:multimedia']
|
||||
});
|
||||
|
||||
Self.downloadFile = async function(ctx, id) {
|
||||
|
|
|
@ -114,7 +114,7 @@
|
|||
<vn-td center shrink>
|
||||
<a ng-show="balance.hasPdf"
|
||||
target="_blank"
|
||||
href="api/InvoiceOuts/{{::balance.id}}/download?access_token={{::$ctrl.vnToken.token}}">
|
||||
href="api/InvoiceOuts/{{::balance.id}}/download?access_token={{::$ctrl.vnToken.tokenMultimedia}}">
|
||||
<vn-icon-button
|
||||
icon="cloud_download"
|
||||
title="{{'Download PDF' | translate}}">
|
||||
|
|
|
@ -31,7 +31,8 @@ module.exports = Self => {
|
|||
http: {
|
||||
path: '/:id/download',
|
||||
verb: 'GET'
|
||||
}
|
||||
},
|
||||
accessScopes: ['read:multimedia']
|
||||
});
|
||||
|
||||
Self.download = async function(ctx, id, options) {
|
||||
|
|
|
@ -31,7 +31,8 @@ module.exports = Self => {
|
|||
http: {
|
||||
path: '/downloadZip',
|
||||
verb: 'GET'
|
||||
}
|
||||
},
|
||||
accessScopes: ['read:multimedia']
|
||||
});
|
||||
|
||||
Self.downloadZip = async function(ctx, ids, options) {
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
<vn-menu vn-id="showInvoiceMenu">
|
||||
<vn-list>
|
||||
<a class="vn-item"
|
||||
href="api/InvoiceOuts/{{$ctrl.id}}/download?access_token={{$ctrl.vnToken.token}}"
|
||||
href="api/InvoiceOuts/{{$ctrl.id}}/download?access_token={{$ctrl.vnToken.tokenMultimedia}}"
|
||||
target="_blank"
|
||||
name="showInvoicePdf"
|
||||
translate>
|
||||
|
|
|
@ -25,7 +25,7 @@ export default class Controller extends Section {
|
|||
openPdf() {
|
||||
if (this.checked.length <= 1) {
|
||||
const [invoiceOutId] = this.checked;
|
||||
const url = `api/InvoiceOuts/${invoiceOutId}/download?access_token=${this.vnToken.token}`;
|
||||
const url = `api/InvoiceOuts/${invoiceOutId}/download?access_token=${this.vnToken.tokenMultimedia}`;
|
||||
window.open(url, '_blank');
|
||||
} else {
|
||||
const invoiceOutIds = this.checked;
|
||||
|
|
|
@ -11,6 +11,7 @@ module.exports = Self => {
|
|||
path: `/download`,
|
||||
verb: 'POST',
|
||||
},
|
||||
accessScopes: ['read:multimedia']
|
||||
});
|
||||
|
||||
Self.download = async() => {
|
||||
|
|
|
@ -29,7 +29,8 @@ module.exports = Self => {
|
|||
http: {
|
||||
path: '/downloadCmrsZip',
|
||||
verb: 'GET'
|
||||
}
|
||||
},
|
||||
accessScopes: ['read:multimedia']
|
||||
});
|
||||
|
||||
Self.downloadCmrsZip = async function(ctx, ids, options) {
|
||||
|
|
|
@ -29,7 +29,8 @@ module.exports = Self => {
|
|||
http: {
|
||||
path: '/downloadZip',
|
||||
verb: 'GET'
|
||||
}
|
||||
},
|
||||
accessScopes: ['read:multimedia']
|
||||
});
|
||||
|
||||
Self.downloadZip = async function(ctx, id, options) {
|
||||
|
|
|
@ -34,7 +34,9 @@ module.exports = Self => {
|
|||
http: {
|
||||
path: '/:id/driver-route-pdf',
|
||||
verb: 'GET'
|
||||
}
|
||||
},
|
||||
accessScopes: ['read:multimedia']
|
||||
|
||||
});
|
||||
|
||||
Self.driverRoutePdf = (ctx, id) => Self.printReport(ctx, id, 'driver-route');
|
||||
|
|
|
@ -40,7 +40,7 @@ export default class Controller extends Section {
|
|||
const stringRoutesIds = routesIds.join(',');
|
||||
|
||||
if (this.checked.length <= 1) {
|
||||
const url = `api/Routes/${stringRoutesIds}/driver-route-pdf?access_token=${this.vnToken.token}`;
|
||||
const url = `api/Routes/${stringRoutesIds}/driver-route-pdf?access_token=${this.vnToken.tokenMultimedia}`;
|
||||
window.open(url, '_blank');
|
||||
} else {
|
||||
const serializedParams = this.$httpParamSerializer({
|
||||
|
|
|
@ -29,7 +29,8 @@ module.exports = Self => {
|
|||
http: {
|
||||
path: `/:id/downloadFile`,
|
||||
verb: 'GET'
|
||||
}
|
||||
},
|
||||
accessScopes: ['read:multimedia']
|
||||
});
|
||||
|
||||
Self.downloadFile = async function(ctx, id) {
|
||||
|
|
Loading…
Reference in New Issue