feat(useAcl): create checkUrl
This commit is contained in:
parent
1150739de7
commit
bb85e8b3cb
|
@ -3,6 +3,8 @@ import qFormMixin from './qformMixin';
|
||||||
import mainShortcutMixin from './mainShortcutMixin';
|
import mainShortcutMixin from './mainShortcutMixin';
|
||||||
import keyShortcut from './keyShortcut';
|
import keyShortcut from './keyShortcut';
|
||||||
import useNotify from 'src/composables/useNotify.js';
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
import { AccessError } from 'src/utils/errors';
|
||||||
|
|
||||||
const { notify } = useNotify();
|
const { notify } = useNotify();
|
||||||
|
|
||||||
export default boot(({ app }) => {
|
export default boot(({ app }) => {
|
||||||
|
@ -11,6 +13,12 @@ export default boot(({ app }) => {
|
||||||
app.directive('shortcut', keyShortcut);
|
app.directive('shortcut', keyShortcut);
|
||||||
app.config.errorHandler = function (err) {
|
app.config.errorHandler = function (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
notify('globals.error', 'negative', 'error');
|
switch (err.constructor) {
|
||||||
|
case AccessError:
|
||||||
|
notify('errors.statusUnauthorized', 'negative', 'account_circle_off');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
notify('globals.error', 'negative', 'error');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useState } from './useState';
|
import { useState } from './useState';
|
||||||
|
import { AccessError } from 'src/utils/errors';
|
||||||
|
|
||||||
export function useAcl() {
|
export function useAcl() {
|
||||||
const state = useState();
|
const state = useState();
|
||||||
|
@ -8,31 +9,86 @@ export function useAcl() {
|
||||||
const { data } = await axios.get('VnUsers/acls');
|
const { data } = await axios.get('VnUsers/acls');
|
||||||
const acls = {};
|
const acls = {};
|
||||||
data.forEach((acl) => {
|
data.forEach((acl) => {
|
||||||
acls[acl.model] = acls[acl.model] || {};
|
const model = acl.model.toLowerCase();
|
||||||
acls[acl.model][acl.property] = acls[acl.model][acl.property] || {};
|
const property = acl.property.toLowerCase();
|
||||||
acls[acl.model][acl.property][acl.accessType] = true;
|
acls[model] = acls[model] || {};
|
||||||
|
acls[model][property] = acls[model][property] || {};
|
||||||
|
acls[model][property][acl.accessType] = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
state.setAcls(acls);
|
state.setAcls(acls);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasAny(acls) {
|
function hasAny(acls) {
|
||||||
|
let result = false;
|
||||||
for (const acl of acls) {
|
for (const acl of acls) {
|
||||||
let { model, props, accessType } = acl;
|
let { model, props, accessType } = acl;
|
||||||
const modelAcls = state.getAcls().value[model];
|
const modelAcls = state.getAcls().value[model.toLowerCase()];
|
||||||
Array.isArray(props) || (props = [props]);
|
Array.isArray(props) || (props = [props]);
|
||||||
if (modelAcls)
|
if (modelAcls) {
|
||||||
return ['*', ...props].some((key) => {
|
result = ['*', ...props].some((key) => {
|
||||||
const acl = modelAcls[key];
|
const acl = modelAcls[key.toLowerCase()];
|
||||||
return acl && (acl['*'] || acl[accessType]);
|
return acl && (acl['*'] || acl[accessType]);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
if (result) return result;
|
||||||
}
|
}
|
||||||
return false;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkRead(model, urlSplit) {
|
||||||
|
const acls = [];
|
||||||
|
let props = urlSplit[1] ?? 'find';
|
||||||
|
if (typeof +props == 'number') {
|
||||||
|
props = urlSplit[2] ?? 'findById';
|
||||||
|
acls.push({ model, props: `__get__${props}`, accessType: 'READ' });
|
||||||
|
}
|
||||||
|
|
||||||
|
acls.push({ model, props, accessType: 'READ' });
|
||||||
|
console.log('acls: ', acls);
|
||||||
|
console.log('hasAny(acls): ', hasAny(acls));
|
||||||
|
return hasAny(acls);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkWrite(model, urlSplit, type) {
|
||||||
|
const acls = [];
|
||||||
|
let props = urlSplit[1] ?? (type != 'post' ? 'upsert' : 'create');
|
||||||
|
|
||||||
|
if (typeof +props == 'number') {
|
||||||
|
if (!urlSplit[2]) props = 'updateAttributes';
|
||||||
|
else if (['post', 'delete'].includes(type)) {
|
||||||
|
let prefix = 'create';
|
||||||
|
if (type == 'delete') prefix = type;
|
||||||
|
acls.push({ model, props: `__${prefix}__${props}`, accessType: 'WRITE' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
acls.push({ model, props, accessType: 'WRITE' });
|
||||||
|
console.log('acls: ', acls);
|
||||||
|
console.log('hasAny(acls): ', hasAny(acls));
|
||||||
|
return hasAny(acls);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkUrl(url, type = 'get', throwError, model) {
|
||||||
|
if (!url) return true;
|
||||||
|
|
||||||
|
const urlSplit = url.split('/');
|
||||||
|
model ??= urlSplit[0]?.slice(1, -1);
|
||||||
|
type = type.toLowerCase();
|
||||||
|
let hasPermission;
|
||||||
|
if (type == 'get') hasPermission = checkRead(model, urlSplit);
|
||||||
|
else hasPermission = checkWrite(model, urlSplit, type);
|
||||||
|
|
||||||
|
if (throwError && !hasPermission) throw new AccessError(url);
|
||||||
|
return hasPermission;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fetch,
|
fetch,
|
||||||
hasAny,
|
hasAny,
|
||||||
state,
|
state,
|
||||||
|
checkRead,
|
||||||
|
checkWrite,
|
||||||
|
checkUrl,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ export function useSession() {
|
||||||
headers: { Authorization: storage.getItem(key) },
|
headers: { Authorization: storage.getItem(key) },
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notify('errors.statusUnauthorized', 'negative');
|
notify('errors.statusUnauthorized', 'negative', 'account_circle_off');
|
||||||
} finally {
|
} finally {
|
||||||
storage.removeItem(key);
|
storage.removeItem(key);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ export const useArrayDataStore = defineStore('arrayDataStore', () => {
|
||||||
searchUrl: 'params',
|
searchUrl: 'params',
|
||||||
navigate: null,
|
navigate: null,
|
||||||
page: 1,
|
page: 1,
|
||||||
|
checkAcl: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
function get(key) {
|
function get(key) {
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
class AccessError extends Error {
|
||||||
|
constructor(details, message) {
|
||||||
|
super(message ?? 'Access denied');
|
||||||
|
this.name = AccessError.name;
|
||||||
|
this.details = details;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { AccessError };
|
Loading…
Reference in New Issue