Skip to content

Commit

Permalink
Merge pull request #60 from bcgov/feature/perm-audit
Browse files Browse the repository at this point in the history
Permissions audit
  • Loading branch information
jujaga authored Mar 29, 2023
2 parents 37e5015 + 46d9c95 commit 687de7f
Show file tree
Hide file tree
Showing 13 changed files with 99 additions and 40 deletions.
9 changes: 3 additions & 6 deletions frontend/src/components/bucket/BucketTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ const showPermissions = async (bucketId: string, bucketName: string) => {
permissionsBucketId.value = bucketId;
permissionBucketName.value = bucketName;
};
const checkUserManageBucket = (bucket: Bucket) => {
return permissionStore.isBucketActionAllowed(bucket.bucketId, getUserId.value, Permissions.MANAGE );
};
</script>

<template>
Expand Down Expand Up @@ -104,20 +100,21 @@ const checkUserManageBucket = (bucket: Bucket) => {
>
<template #body="{ data }">
<Button
v-if="checkUserManageBucket(data)"
v-if="permissionStore.isBucketActionAllowed(data.bucketId, getUserId, Permissions.UPDATE )"
class="p-button-lg p-button-text"
@click="showBucketConfig(data)"
>
<font-awesome-icon icon="fas fa-cog" />
</Button>
<Button
v-if="checkUserManageBucket(data)"
v-if="permissionStore.isBucketActionAllowed(data.bucketId, getUserId, Permissions.MANAGE )"
class="p-button-lg p-button-text"
@click="showPermissions(data.bucketId, data.bucketName)"
>
<font-awesome-icon icon="fa-solid fa-users" />
</Button>
<Button
v-if="permissionStore.isBucketActionAllowed(data.bucketId, getUserId, Permissions.READ )"
class="p-button-lg p-button-rounded p-button-text"
@click="showSidebarInfo(data.bucketId)"
>
Expand Down
6 changes: 2 additions & 4 deletions frontend/src/components/object/ObjectFileDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ onBeforeMount( async () => {
let isPublic = head?.status === 204;
await permissionStore.fetchBucketPermissions({userId: getUserId.value, objectPerms: true});
await objectStore.fetchObjects({objectId: props.objectId});
await objectStore.fetchObjects({objectId: props.objectId, userId: getUserId.value, bucketPerms: true});
obj.value = objectStore.findObjectById(props.objectId);
const bucketId = obj.value?.bucketId;
Expand Down Expand Up @@ -106,12 +106,10 @@ watch( [props, getObjects], () => {
class="action-buttons"
>
<ShareObjectButton
v-if="permissionStore.isObjectActionAllowed(
props.objectId, getUserId, Permissions.READ, bucketId)"
:id="props.objectId"
/>
<DownloadObjectButton
v-if="permissionStore.isObjectActionAllowed(
v-if="obj.public || permissionStore.isObjectActionAllowed(
props.objectId, getUserId, Permissions.READ, bucketId)"
:mode="ButtonMode.ICON"
:ids="[props.objectId]"
Expand Down
27 changes: 20 additions & 7 deletions frontend/src/components/object/ObjectList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
ObjectTable,
ObjectUpload
} from '@/components/object';
import { Button } from '@/lib/primevue';
import { Button, useToast } from '@/lib/primevue';
import {
useAuthStore,
useBucketStore,
Expand All @@ -21,6 +21,7 @@ import { ButtonMode } from '@/utils/enums';
import type { Ref } from 'vue';
// Props
type Props = {
bucketId?: string
};
Expand All @@ -42,7 +43,13 @@ const { getUserId } = storeToRefs(useAuthStore());
const displayUpload = ref(false);
const objectInfoId: Ref<string | undefined> = ref(undefined);
const selectedObjectIds = computed(() => {
return getSelectedObjects.value.map((o) => o.id);
});
// Actions
const toast = useToast();
const showObjectInfo = async (objectId: string | undefined) => {
objectInfoId.value = objectId;
};
Expand All @@ -68,17 +75,22 @@ const closeUpload = () => {
// }
// };
// Download
const selectedObjectIds = computed(() => {
return getSelectedObjects.value.map((o) => o.id);
});
function onDeletedSuccess() {
toast.add({
severity: 'success',
summary: 'Success',
detail: 'File(s) deleted',
life: 3000
});
}
onMounted(async () => {
// Removed for now
// updateBreadcrumb();
await bucketStore.fetchBuckets({ userId: getUserId.value, objectPerms: true });
await objectStore.fetchObjects({ bucketId: props.bucketId });
// TODO: userId+bucketPerms bringing back deleted files??
await objectStore.fetchObjects({ bucketId: props.bucketId, userId: getUserId.value, bucketPerms: true });
});
</script>
Expand All @@ -96,7 +108,7 @@ onMounted(async () => {
</div>
<div>
<Button
v-if="permissionStore.isBucketActionAllowed(props.bucketId as string, getUserId, Permissions.CREATE )"
v-if="permissionStore.isBucketActionAllowed(props.bucketId as string, getUserId, Permissions.CREATE)"
class="mr-2"
:disabled="displayUpload"
@click="showUpload"
Expand All @@ -115,6 +127,7 @@ onMounted(async () => {
:disabled="displayUpload"
:ids="selectedObjectIds"
:mode="ButtonMode.BUTTON"
@on-deleted-success="onDeletedSuccess"
/>
</div>

Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/object/ObjectPermission.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ const removeObjectUser = (userId: string) => {
const updateObjectPermission = (value: boolean, userId: string, permCode: string) => {
if (value) {
permissionStore.addObjectPermission(props.objectId, userId, permCode);
} else {
}
else {
const managers = getMappedObjectToUserPermissions.value.filter( (x: UserPermissions) => x.manage );
// Disallow removable of final MANAGE permission
Expand Down
16 changes: 12 additions & 4 deletions frontend/src/components/object/ObjectSidebar.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { watch } from 'vue';
import {
ObjectMetadata,
ObjectProperties,
ObjectTag
} from '@/components/object';
import { Button } from '@/lib/primevue';
import { useTagStore } from '@/store';
import { RouteNames } from '@/utils/constants';
import { useAuthStore, useObjectStore, usePermissionStore, useTagStore } from '@/store';
import { Permissions, RouteNames } from '@/utils/constants';
// Props
type Props = {
Expand All @@ -21,15 +21,23 @@ const props = withDefaults(defineProps<Props>(), {});
const emit = defineEmits(['close-object-info']);
// Store
const objectStore = useObjectStore();
const permissionStore = usePermissionStore();
const tagStore = useTagStore();
const { getUserId } = storeToRefs(useAuthStore());
// Actions
const closeObjectInfo = async () => {
emit('close-object-info');
};
watch( props, () => {
tagStore.fetchTagging({objectId: props.objectInfoId});
const obj = objectStore.findObjectById(props.objectInfoId);
if( obj &&
(obj.public || permissionStore.isObjectActionAllowed(obj.id, getUserId.value, Permissions.READ, obj.bucketId)))
{
tagStore.fetchTagging({objectId: props.objectInfoId});
}
}, { immediate: true });
</script>

Expand Down
38 changes: 30 additions & 8 deletions frontend/src/components/object/ObjectTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
ObjectPermission,
ShareObjectButton
} from '@/components/object';
import { Button, Column, DataTable, Dialog, FilterMatchMode, InputText, InputSwitch } from '@/lib/primevue';
import { Button, Column, DataTable, Dialog, FilterMatchMode, InputText, InputSwitch, useToast } from '@/lib/primevue';
import { useAuthStore, useAppStore, useMetadataStore, useObjectStore, usePermissionStore } from '@/store';
import { Permissions } from '@/utils/constants';
import { ButtonMode } from '@/utils/enums';
Expand Down Expand Up @@ -50,6 +50,8 @@ const selectedObjects: Ref<Array<COMSObject>> = ref([]);
const tableData: Ref<Array<COMSObjectDataSource>> = ref([]);
// Actions
const toast = useToast();
const formatShortUuid = (uuid: string) => {
return uuid?.slice(0,8) ?? uuid;
};
Expand All @@ -59,6 +61,8 @@ const showInfo = async (id: string) => {
};
const showPermissions = async (objectId: string) => {
await permissionStore.fetchObjectPermissions({objectId});
permissionsVisible.value = true;
permissionsObjectId.value = objectId;
permissionsObjectName.value = metadataStore.findValue(objectId, 'name') || '';
Expand All @@ -68,13 +72,30 @@ const togglePublic = async (objectId: string, isPublic: boolean) => {
await objectStore.togglePublic(objectId, isPublic);
};
function onDeletedSuccess() {
toast.add({
severity: 'success',
summary: 'Success',
detail: 'File deleted',
life: 3000
});
}
watch( getObjects, async () => {
// Filter object cache to this specific bucket
const objs: Array<COMSObjectDataSource> = getObjects.value
.filter( (x: COMSObject) => x.bucketId === props.bucketId ) as COMSObjectDataSource[];
// update metadata store
await metadataStore.fetchMetadata({objectId: objs.map( (x: COMSObject) => x.id )});
// Update metadata store with metadata user has access to
const objIds: Array<string> = [];
objs.forEach( (x: COMSObject) => {
if( x.public || permissionStore.isObjectActionAllowed(
x.id, getUserId.value, Permissions.READ, props.bucketId as string))
{
objIds.push(x.id);
}
});
await metadataStore.fetchMetadata({objectId: objIds});
tableData.value = objs.map( (x: COMSObjectDataSource) => {
x.name = metadataStore.findValue(x.id, 'name');
Expand All @@ -91,7 +112,7 @@ const filters = ref({
// Need this till PrimeVue gets it together to un-break this again
// TODO: Revisit with PrimeVue 2.37+
// @ts-ignore
global: { value: null, matchMode: FilterMatchMode.CONTAINS }
global: { value: null, matchMode: FilterMatchMode.CONTAINS }
});
</script>

Expand Down Expand Up @@ -123,7 +144,7 @@ const filters = ref({
placeholder="Search File Names"
/>
</span>

<Button
class="ml-2"
icon="pi pi-refresh"
Expand Down Expand Up @@ -207,12 +228,10 @@ const filters = ref({
>
<template #body="{ data }">
<ShareObjectButton
v-if="permissionStore.isObjectActionAllowed(
data.id, getUserId, Permissions.READ, props.bucketId as string)"
:id="data.id"
/>
<DownloadObjectButton
v-if="permissionStore.isObjectActionAllowed(
v-if="data.public || permissionStore.isObjectActionAllowed(
data.id, getUserId, Permissions.READ, props.bucketId as string)"
:mode="ButtonMode.ICON"
:ids="[data.id]"
Expand All @@ -226,6 +245,8 @@ const filters = ref({
<font-awesome-icon icon="fa-solid fa-users" />
</Button>
<Button
v-if="data.public || permissionStore.isObjectActionAllowed(
data.id, getUserId, Permissions.READ, props.bucketId as string)"
class="p-button-lg p-button-rounded p-button-text"
@click="showInfo(data.id)"
>
Expand All @@ -236,6 +257,7 @@ const filters = ref({
data.id, getUserId, Permissions.DELETE, props.bucketId as string)"
:mode="ButtonMode.ICON"
:ids="[data.id]"
@on-deleted-success="onDeletedSuccess"
/>
</template>
</Column>
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/components/object/ObjectUpload.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { ref } from 'vue';
import ObjectUploadFile from '@/components/object/ObjectUploadFile.vue';
import { Button, FileUpload, useToast } from '@/lib/primevue';
import { useObjectStore } from '@/store';
import { useAuthStore, useObjectStore } from '@/store';
import type { Ref } from 'vue';
Expand All @@ -19,6 +20,7 @@ const props = withDefaults(defineProps<Props>(), {
// Store
const objectStore = useObjectStore();
const { getUserId } = storeToRefs(useAuthStore());
// State
const pendingFiles: Ref<Array<File>> = ref([]);
Expand Down Expand Up @@ -58,7 +60,7 @@ const onUpload = async (event: any) => {
pendingFiles.value = [];
// Update object store
await objectStore.fetchObjects({ bucketId: bucketId });
await objectStore.fetchObjects({ bucketId: bucketId, userId: getUserId.value, bucketPerms: true });
} else {
toast.add({ severity: 'error', summary: 'Error', detail: 'Failed to acquire bucket ID', life: 3000 });
}
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/store/bucketStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ export const useBucketStore = defineStore('bucket', () => {
// Get a unique list of bucket IDs the user has access to
const permResponse = await permissionStore.fetchBucketPermissions(params);
if (permResponse) {
const uniqueIds: string[] = [...new Set<string>(permResponse.map((x: { bucketId: string }) => x.bucketId))];
const uniqueIds: Array<string> = [
...new Set<string>(permResponse.map((x: { bucketId: string }) => x.bucketId))
];

let response = Array<Bucket>();
if (uniqueIds.length) {
Expand Down
13 changes: 9 additions & 4 deletions frontend/src/store/objectStore.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { defineStore } from 'pinia';
import { defineStore, storeToRefs } from 'pinia';
import { computed, ref } from 'vue';

import { useToast } from '@/lib/primevue';
import { objectService } from '@/services';
import { useAppStore, usePermissionStore } from '@/store';
import { useAppStore, useAuthStore, usePermissionStore } from '@/store';
import { partition } from '@/utils/utils';

import type { AxiosRequestConfig } from 'axios';
Expand All @@ -21,6 +21,7 @@ export const useObjectStore = defineStore('object', () => {
// Store
const appStore = useAppStore();
const permissionStore = usePermissionStore();
const { getUserId } = storeToRefs(useAuthStore());

// State
const state: ObjectStoreState = {
Expand Down Expand Up @@ -49,6 +50,8 @@ export const useObjectStore = defineStore('object', () => {
}

async function deleteObjects(objectIds: Array<string>) {
const bucketId = findObjectById(objectIds[0])?.bucketId;

try {
appStore.beginIndeterminateLoading();
await Promise.all(
Expand All @@ -61,7 +64,7 @@ export const useObjectStore = defineStore('object', () => {
toast.add({ severity: 'error', summary: 'Error deleting object', detail: error, life: 3000 });
}
finally {
fetchObjects();
fetchObjects({ bucketId: bucketId, userId: getUserId.value, bucketPerms: true });
appStore.endIndeterminateLoading();
}
}
Expand All @@ -87,7 +90,9 @@ export const useObjectStore = defineStore('object', () => {
const permResponse = await permissionStore.fetchObjectPermissions(params);

if (permResponse) {
const uniqueIds: string[] = [...new Set<string>(permResponse.map((x: { objectId: string }) => x.objectId))];
const uniqueIds: Array<string> = [
...new Set<string>(permResponse.map((x: { objectId: string }) => x.objectId))
];

let response = Array<COMSObject>();
if (uniqueIds.length) {
Expand Down
Loading

0 comments on commit 687de7f

Please sign in to comment.