Add permissions store (#393)

* Fetch role nested in user store

* Use nested role in presets

* Add permissions store

* Fix tests
This commit is contained in:
Rijk van Zanten
2020-04-13 11:08:21 -04:00
committed by GitHub
parent 33062b9dd9
commit b3b4417b26
9 changed files with 111 additions and 7 deletions

View File

@@ -7,6 +7,7 @@ import { useCollectionPresetsStore } from '@/stores/collection-presets/';
import { useSettingsStore } from '@/stores/settings/';
import { useProjectsStore } from '@/stores/projects/';
import { useLatencyStore } from '@/stores/latency';
import { usePermissionsStore } from '@/stores/permissions';
type GenericStore = {
id: string;
@@ -26,6 +27,7 @@ export function useStores(
useSettingsStore,
useProjectsStore,
useLatencyStore,
usePermissionsStore,
]
) {
return stores.map((useStore) => useStore()) as GenericStore[];
@@ -41,6 +43,11 @@ export async function hydrate(stores = useStores()) {
appStore.state.hydrating = true;
try {
/**
* @NOTE
* This will fetch the store data sequential. While this does prevent rate limiteres from
* kicking in, we could optimize it by running (some of) the requests in parallel
*/
for (const store of stores) {
await store.hydrate?.();
}

View File

@@ -30,7 +30,7 @@ describe('Compositions / Collection Presets', () => {
);
const userStore = useUserStore(req);
(userStore.state.currentUser as any) = { id: 15, role: 25 };
(userStore.state.currentUser as any) = { id: 15, role: { id: 25 } };
const projectsStore = useProjectsStore(req);
projectsStore.state.currentProjectKey = 'my-project';
const collectionPresetsStore = useCollectionPresetsStore(req);

View File

@@ -28,7 +28,7 @@ export const useCollectionPresetsStore = createStore({
// All role saved bookmarks and presets
api.get(`/${currentProjectKey}/collection_presets`, {
params: {
'filter[role][eq]': role,
'filter[role][eq]': role.id,
'filter[user][null]': 1,
},
}),
@@ -99,7 +99,7 @@ export const useCollectionPresetsStore = createStore({
const availablePresets = this.state.collectionPresets.filter((preset) => {
const userMatches = preset.user === userID || preset.user === null;
const roleMatches = preset.role === userRole || preset.role === null;
const roleMatches = preset.role === userRole.id || preset.role === null;
const collectionMatches = preset.collection === collection;
// Filter out all bookmarks
@@ -117,7 +117,7 @@ export const useCollectionPresetsStore = createStore({
const userPreset = availablePresets.find((preset) => preset.user === userID);
if (userPreset) return userPreset;
const rolePreset = availablePresets.find((preset) => preset.role === userRole);
const rolePreset = availablePresets.find((preset) => preset.role === userRole.id);
if (rolePreset) return rolePreset;
// If the other two already came up empty, we can assume there's only one preset. That

View File

@@ -0,0 +1,4 @@
import { usePermissionsStore } from './permissions';
export { usePermissionsStore };
export default usePermissionsStore;

View File

@@ -0,0 +1,66 @@
import { createStore } from 'pinia';
import useUserStore from '@/stores/user';
import api from '@/api';
import useProjectsStore from '@/stores/projects';
import { Permission } from './types';
const defaultAdminPermission: Partial<Permission> = {
role: 1,
create: 'full',
read: 'full',
update: 'full',
delete: 'full',
comment: 'full',
explain: 'none',
read_field_blacklist: [],
write_field_blacklist: [],
status_blacklist: [],
};
const defaultPermission: Partial<Permission> = {
create: 'none',
read: 'none',
update: 'none',
delete: 'none',
comment: 'none',
explain: 'none',
read_field_blacklist: [],
write_field_blacklist: [],
status_blacklist: [],
};
export const usePermissionsStore = createStore({
id: 'permissionsStore',
state: () => ({
permissions: [] as Permission[],
}),
actions: {
getPermissionsForCollection(collection: string) {
const userStore = useUserStore();
if (userStore.isAdmin.value) return defaultAdminPermission;
const permissions = this.state.permissions.filter(
(permission) => permission.collection === collection
);
return permissions ? permissions : defaultPermission;
},
async hydrate() {
const projectsStore = useProjectsStore();
const userStore = useUserStore();
if (userStore.isAdmin.value) return;
const currentProjectKey = projectsStore.state.currentProjectKey;
const permissionsResponse = await api.get(`/${currentProjectKey}/permissions`);
this.state.permissions = permissionsResponse.data.data;
},
async dehydrate() {
this.reset();
},
},
});

View File

@@ -0,0 +1,15 @@
export type Permission = {
id: number;
collection: string;
role: number;
status: string | null;
create: 'full' | 'none';
read: 'none' | 'mine' | 'role' | 'full';
update: 'none' | 'mine' | 'role' | 'full';
delete: 'none' | 'mine' | 'role' | 'full';
comment: 'none' | 'read' | 'create' | 'update' | 'full';
explain: 'none' | 'create' | 'update' | 'always';
read_field_blacklist: string[];
write_field_blacklist: string[];
status_blacklist: string[];
};

View File

@@ -24,7 +24,16 @@ export type User = {
external_id: string;
'2fa_secret': string;
theme: 'auto' | 'dark' | 'light';
role: number;
role: {
id: number;
name: string;
description: string;
collection_listing: null;
module_listing: null;
enforce_2fa: null | boolean;
external_id: null | string;
ip_whitelist: string[];
};
password_reset_token: string | null;
timezone: string;
locale: string;

View File

@@ -40,7 +40,7 @@ describe('Stores / User', () => {
expect(api.get).toHaveBeenCalledWith('/my-project/users/me', {
params: {
fields: '*,avatar.data',
fields: '*,avatar.data,role.*',
},
});
});

View File

@@ -17,6 +17,9 @@ export const useUserStore = createStore({
if (state.currentUser === null) return null;
return state.currentUser.first_name + ' ' + state.currentUser.last_name;
},
isAdmin(state) {
return state.currentUser?.role.id === 1;
},
},
actions: {
async hydrate() {
@@ -28,7 +31,7 @@ export const useUserStore = createStore({
try {
const { data } = await api.get(`/${currentProjectKey}/users/me`, {
params: {
fields: '*,avatar.data',
fields: '*,avatar.data,role.*',
},
});