Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions frontend/src/assets/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,12 @@ div:focus-visible {
font-size: .8rem;
padding: 0.25rem 0.75rem;
font-weight: 400;
line-height: 1;

.p-tag-value {
margin-top: -2px;
}

}

.p-tag-danger {
Expand All @@ -271,6 +277,21 @@ div:focus-visible {
}
}

.p-tag-info {
background-color: rgb(224, 242, 254);
outline-style: solid;
outline-color: $bcbox-primary;
outline-width: thin;

.p-tag-value {
color: $bcbox-primary;
}

.p-tag-icon {
color: $bcbox-primary
}
}

/* datatable */
.p-datatable,
.p-treetable {
Expand Down
154 changes: 154 additions & 0 deletions frontend/src/components/bucket/BucketIdpToggle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue';

import { InputSwitch, useConfirm, useToast } from '@/lib/primevue';
import { useBucketStore, usePermissionStore } from '@/store';
import { bucketService } from '@/services';
import { Permissions } from '@/utils/constants';

import { getParentKey } from '@/utils/utils';
import type { Ref } from 'vue';

// Props
type Props = {
bucketId: string;
bucketName: string;
bucketPublic: boolean;
userId: string;
};
const props = withDefaults(defineProps<Props>(), {});

// Store
const bucketStore = useBucketStore();
const permissionStore = usePermissionStore();

// State
const isInternal: Ref<boolean> = ref(false);
const isParentBucketInternal = ref(false);

const isInternalState = computed(() => {
return permissionStore.getBucketInternal(props.bucketId) || props.bucketPublic || isParentBucketInternal.value;
});

const isToggleEnabled = computed(() => {
return (
!isParentBucketInternal.value &&
!props.bucketPublic &&
usePermissionStore().isUserElevatedRights() &&
permissionStore.isBucketActionAllowed(props.bucketId, props.userId, Permissions.MANAGE)
);
});

// Actions
const toast = useToast();
const confirm = useConfirm();

const toggleIdp = async (value: boolean) => {
if (value) {
confirm.require({
message: "Setting this file to 'Internal only' will allow all IDIR users " + 'to view and download it.',
header: 'Set file to Internal only?',
acceptLabel: 'Set to Internal only',
rejectLabel: 'Cancel',
accept: () => {
permissionStore
.addBucketIdpPermission(props.bucketId, 'idir', 'READ')
.then(() => {
isInternal.value = true;
toast.success('File set to Internal only', `"${props.bucketName}" is now Internal only`);
})
.catch((e) => toast.error('Setting file to Internal only failed', e.response?.data.detail, { life: 0 }));
},
reject: () => (isInternal.value = false),
onHide: () => (isInternal.value = false)
});
} else
confirm.require({
message:
"Setting this file to private will remove 'Internal only' access. " +
'Only users with permissions will be able to view or download the file.',
header: 'Set file to private?',
acceptLabel: 'Set to private',
rejectLabel: 'Cancel',
accept: () => {
permissionStore
.deleteBucketIdpPermission(props.bucketId, 'idir', 'READ')
.then(() => {
isInternal.value = false;
toast.success('File set to private', `"${props.bucketName}" is no longer 'Internal only'`);
})
.catch((e) => toast.error('Setting file to private failed', e.response?.data.detail, { life: 0 }));
},
reject: () => (isInternal.value = true),
onHide: () => (isInternal.value = true)
});
};

onMounted(async () => {
// refresh current bucket IDP permissions
await permissionStore.fetchBucketIdpPermissions({
bucketId: props.bucketId,
permCode: 'READ',
idp: 'idir'
});

// get immediate parent bucket IDP permissions
// TODO: get perms for every parent in path (eg: x/y/z/currentBucket)
// let isParentBucketInternal = false;
const bucket = bucketStore.getBucket(props.bucketId);
if (bucket) {
// if current bucket is not at root
if (bucket.key !== '/') {
// search for parent bucket by endpoint and bucket and segment of key
const responseArray = (
await bucketService.searchBuckets({
key: getParentKey(bucket.key),
endpoint: bucket.endpoint,
bucket: bucket.bucket
})
).data;
const parent = responseArray.reduce((a: any, b: any) => (a.key.length <= b.key.length ? a : b));
// fetch bucketIdpPermissions for parent
await permissionStore.fetchBucketIdpPermissions({
bucketId: parent.bucketId,
permCode: 'READ',
idp: 'idir'
});
isParentBucketInternal.value = permissionStore.getBucketInternal(parent.bucketId);
}
}
isInternal.value = isInternalState.value;
});

watch(props, () => {
if (props.bucketPublic === true) isInternal.value = true;
else {
const perms = permissionStore.getBucketIdpPermissions.filter(
(p: any) => p.permCode === Permissions.READ && p.bucketId === props.bucketId && p.idp === 'idir'
);
if (perms.length > 0) isInternal.value = true;
else {
isInternal.value = false;
}
}
});
</script>

<template>
<span
v-tooltip="
isToggleEnabled
? ''
: props.bucketPublic
? 'Enabled by Public Sharing'
: 'Change the folder\'s \'Internal only\' setting to update this file'
"
>
<InputSwitch
:model-value="isInternal"
aria-label="Toggle to make file Internal only"
:disabled="!isToggleEnabled"
@update:model-value="toggleIdp($event)"
/>
</span>
</template>
18 changes: 15 additions & 3 deletions frontend/src/components/bucket/BucketList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,19 @@ const closeBucketConfig = () => {
};

onMounted(async () => {
await bucketStore.fetchBuckets({ userId: getUserId.value, objectPerms: true });
const buckets = await bucketStore.fetchBuckets({
userId: getUserId.value,
objectPerms: true
});
// get IDP permissions for labelling in table
if (buckets && buckets.length > 0 && usePermissionStore().isUserElevatedRights()) {
await usePermissionStore().fetchBucketIdpPermissions({
// limit to 1000 folders
bucketId: buckets.slice(0, 1000).map((b) => b.bucketId),
idp: 'idir',
objectPerms: true
});
}
});
</script>

Expand Down Expand Up @@ -103,7 +115,7 @@ onMounted(async () => {
{{ bucketConfigTitle }}
</h3>

<!-- <Message severity="warn">
<Message severity="warn">
If you intend to share files in your bucket with BCeID or BC Services Card users, please notify
<a href="mailto:IDIM.Consulting@gov.bc.ca">IDIM.Consulting@gov.bc.ca</a>
that you plan to use BCBox.
Expand All @@ -119,7 +131,7 @@ onMounted(async () => {
</a>
(Natural Resource ministries) or your ministry's service desk if you need help with &quot;bucket&quot; storage
location sources.
</Message> -->
</Message>

<BucketConfigForm
:bucket="bucketToUpdate"
Expand Down
28 changes: 21 additions & 7 deletions frontend/src/components/bucket/BucketPermission.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { storeToRefs } from 'pinia';
import { computed, onBeforeMount, ref } from 'vue';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';

import BucketPublicToggle from '@/components/bucket/BucketPublicToggle.vue';
import { BucketPublicToggle, BucketIdpToggle } from '@/components/bucket';
import BucketPermissionAddUser from '@/components/bucket/BucketPermissionAddUser.vue';
import { BulkPermission } from '@/components/common';
import { useAlert } from '@/composables/useAlert';
Expand Down Expand Up @@ -82,16 +82,15 @@ onBeforeMount(async () => {
<template>
<TabView>
<TabPanel header="Manage permissions">
<h3>Sharing Access</h3>
<!-- public toggle -->
<div class="flex pb-3">
<div class="flex-grow-1">
<div class="pb-1">
<h3>Set to public</h3>
<h4>Set to public</h4>
<p>
Making a folder
<strong>public</strong>
means that all files within it, including those in any subfolders, can be accessed by anyone without
requiring authentication.
Enabling this shares all files and subfolders. Anyone with the link can view the content without signing
in.
</p>
</div>
</div>
Expand All @@ -104,6 +103,21 @@ onBeforeMount(async () => {
:user-id="getUserId"
/>
</div>

<div class="flex flex-row pb-3">
<div class="flex-grow-1">
<h4 class="pb-1">Internal only</h4>
<p>Allow all IDIR users to view this folder and its content</p>
</div>
<BucketIdpToggle
v-if="bucket && getUserId"
:bucket-id="bucket.bucketId"
:bucket-name="bucket.bucketName"
:bucket-public="bucket.public"
:user-id="getUserId"
/>
</div>

<h3>User Permissions</h3>
<!-- user search -->
<div v-if="!showSearchUsers">
Expand Down Expand Up @@ -156,7 +170,7 @@ onBeforeMount(async () => {
aria-labelledby="upload_checkbox"
/>
<Column
field="idpName"
field="idp"
header="Provider"
/>
<Column
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/bucket/BucketPermissionAddUser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ const permissionStore = usePermissionStore();
// Actions
const onAdd = (selectedUser: User) => {
const configuredIdp = getConfig.value.idpList.find((idp: IdentityProvider) => idp.idp === selectedUser.idp);
const idpName = configuredIdp?.name || 'BCSC';
const idp = configuredIdp?.name || 'BCSC';
const idpElevated = configuredIdp?.elevatedRights || false;

permissionStore.addBucketUser({
userId: selectedUser.userId,
idpName: idpName,
idp: idp,
elevatedRights: idpElevated,
fullName: selectedUser.fullName,
create: false,
Expand Down
Loading
Loading