Skip to content

Commit 834692e

Browse files
committed
Controls for Internal Only sharing
Toggle for folder/file 'internal only' setting 'internal' annotation added
1 parent eb3751e commit 834692e

15 files changed

Lines changed: 477 additions & 136 deletions

frontend/src/assets/main.scss

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,21 @@ div:focus-visible {
271271
}
272272
}
273273

274+
.p-tag-info {
275+
background-color: rgb(224, 242, 254);
276+
outline-style: solid;
277+
outline-color: $bcbox-primary;
278+
outline-width: thin;
279+
280+
.p-tag-value {
281+
color: $bcbox-primary;
282+
}
283+
284+
.p-tag-icon {
285+
color: $bcbox-primary
286+
}
287+
}
288+
274289
/* datatable */
275290
.p-datatable,
276291
.p-treetable {
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<script setup lang="ts">
2+
import { computed, onMounted, ref, watch } from 'vue';
3+
4+
import { InputSwitch, useConfirm, useToast } from '@/lib/primevue';
5+
import { usePermissionStore } from '@/store';
6+
import { Permissions } from '@/utils/constants';
7+
8+
import type { Ref } from 'vue';
9+
10+
// Props
11+
type Props = {
12+
bucketId: string;
13+
bucketName: string;
14+
bucketPublic: boolean;
15+
userId: string;
16+
};
17+
18+
const props = withDefaults(defineProps<Props>(), {});
19+
20+
// Store
21+
const permissionStore = usePermissionStore();
22+
23+
// State
24+
const isInternal: Ref<boolean> = ref(false);
25+
const fetchBucketInternal = async (): Promise<boolean> => {
26+
const bucketIdpPermissions = await permissionStore.fetchBucketIdpPermissions({
27+
bucketId: props.bucketId,
28+
permCode: 'READ',
29+
idp: 'idir'
30+
});
31+
const hasPerms = (bucketIdpPermissions?.length ?? 0) > 0;
32+
33+
return hasPerms || props.bucketPublic;
34+
};
35+
36+
// check bucket for the IDP permission
37+
const isBucketInternal: Ref<boolean> = ref(false);
38+
const isParentBucketInternal: Ref<boolean> = ref(false);
39+
const fetchParentBucketInternal = async (): Promise<boolean> => {
40+
const bucketIdpPermissions = await permissionStore.fetchBucketIdpPermissions({
41+
bucketId: props.bucketId,
42+
permCode: 'READ',
43+
idp: 'idir'
44+
});
45+
return (bucketIdpPermissions?.length ?? 0) > 0;
46+
};
47+
48+
const isToggleEnabled = computed(() => {
49+
return (
50+
!isBucketInternal.value &&
51+
!props.bucketPublic &&
52+
usePermissionStore().isUserElevatedRights() &&
53+
permissionStore.isBucketActionAllowed(props.bucketId, props.userId, Permissions.MANAGE)
54+
);
55+
});
56+
57+
// Actions
58+
const toast = useToast();
59+
const confirm = useConfirm();
60+
61+
const toggleIdp = async (value: boolean) => {
62+
if (value) {
63+
confirm.require({
64+
message: "Setting this file to 'Internal only' will allow all IDIR users " + 'to view and download it.',
65+
header: 'Set file to Internal only?',
66+
acceptLabel: 'Set to Internal only',
67+
rejectLabel: 'Cancel',
68+
accept: () => {
69+
permissionStore
70+
.addBucketIdpPermission(props.bucketId, 'idir', 'READ')
71+
.then(() => {
72+
isInternal.value = true;
73+
toast.success('File set to Internal only', `"${props.bucketName}" is now Internal only`);
74+
})
75+
.catch((e) => toast.error('Setting file to Internal only failed', e.response?.data.detail, { life: 0 }));
76+
},
77+
reject: () => (isInternal.value = false),
78+
onHide: () => (isInternal.value = false)
79+
});
80+
} else
81+
confirm.require({
82+
message:
83+
"Setting this file to private will remove 'Internal only' access. " +
84+
'Only users with permissions will be able to view or download the file.',
85+
header: 'Set file to private?',
86+
acceptLabel: 'Set to private',
87+
rejectLabel: 'Cancel',
88+
accept: () => {
89+
permissionStore
90+
.deleteBucketIdpPermission(props.bucketId, 'idir', 'READ')
91+
.then(() => {
92+
isInternal.value = false;
93+
toast.success('File set to private', `"${props.bucketName}" is no longer 'Internal only'`);
94+
})
95+
.catch((e) => toast.error('Setting file to private failed', e.response?.data.detail, { life: 0 }));
96+
},
97+
reject: () => (isInternal.value = true),
98+
onHide: () => (isInternal.value = true)
99+
});
100+
};
101+
102+
onMounted(async () => {
103+
isInternal.value = await fetchBucketInternal();
104+
isParentBucketInternal.value = await fetchParentBucketInternal();
105+
});
106+
107+
watch(props, () => {
108+
if (props.bucketPublic === true) isInternal.value = true;
109+
else {
110+
const perms = permissionStore.getBucketIdpPermissions.filter(
111+
(p: any) => p.permCode === Permissions.READ && p.bucketId === props.bucketId && p.idp === 'idir'
112+
);
113+
if (perms.length > 0) isInternal.value = true;
114+
else {
115+
isInternal.value = false;
116+
}
117+
}
118+
});
119+
</script>
120+
121+
<template>
122+
<span
123+
v-tooltip="
124+
isToggleEnabled
125+
? ''
126+
: props.bucketPublic
127+
? 'Enabled by Public Sharing'
128+
: 'Change the folder\'s \'Internal only\' setting to update this file'
129+
"
130+
>
131+
<InputSwitch
132+
:model-value="isInternal"
133+
aria-label="Toggle to make file Internl only"
134+
:disabled="!isToggleEnabled"
135+
@update:model-value="toggleIdp($event)"
136+
/>
137+
</span>
138+
</template>

frontend/src/components/bucket/BucketList.vue

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ const closeBucketConfig = () => {
4343
};
4444
4545
onMounted(async () => {
46-
await bucketStore.fetchBuckets({ userId: getUserId.value, objectPerms: true });
46+
await bucketStore.fetchBuckets({
47+
userId: getUserId.value,
48+
idp: 'idir',
49+
objectPerms: true
50+
});
4751
});
4852
</script>
4953

@@ -103,7 +107,7 @@ onMounted(async () => {
103107
{{ bucketConfigTitle }}
104108
</h3>
105109

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

124128
<BucketConfigForm
125129
:bucket="bucketToUpdate"

frontend/src/components/bucket/BucketPermission.vue

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { storeToRefs } from 'pinia';
33
import { computed, onBeforeMount, ref } from 'vue';
44
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
55
6-
import BucketPublicToggle from '@/components/bucket/BucketPublicToggle.vue';
6+
import { BucketPublicToggle, BucketIdpToggle } from '@/components/bucket';
77
import BucketPermissionAddUser from '@/components/bucket/BucketPermissionAddUser.vue';
88
import { BulkPermission } from '@/components/common';
99
import { useAlert } from '@/composables/useAlert';
@@ -82,16 +82,15 @@ onBeforeMount(async () => {
8282
<template>
8383
<TabView>
8484
<TabPanel header="Manage permissions">
85+
<h3>Sharing Access</h3>
8586
<!-- public toggle -->
8687
<div class="flex pb-3">
8788
<div class="flex-grow-1">
8889
<div class="pb-1">
89-
<h3>Set to public</h3>
90+
<h4>Set to public</h4>
9091
<p>
91-
Making a folder
92-
<strong>public</strong>
93-
means that all files within it, including those in any subfolders, can be accessed by anyone without
94-
requiring authentication.
92+
Enabling this shares all files and subfolders. Anyone with the link can view the content without signing
93+
in.
9594
</p>
9695
</div>
9796
</div>
@@ -104,6 +103,21 @@ onBeforeMount(async () => {
104103
:user-id="getUserId"
105104
/>
106105
</div>
106+
107+
<div class="flex flex-row pb-3">
108+
<div class="flex-grow-1">
109+
<h4 class="pb-1">Internl only</h4>
110+
<p>Allow all IDIR users to view this flder and its content</p>
111+
</div>
112+
<BucketIdpToggle
113+
v-if="bucket && getUserId"
114+
:bucket-id="bucket.bucketId"
115+
:bucket-name="bucket.bucketName"
116+
:bucket-public="bucket.public"
117+
:user-id="getUserId"
118+
/>
119+
</div>
120+
107121
<h3>User Permissions</h3>
108122
<!-- user search -->
109123
<div v-if="!showSearchUsers">

frontend/src/components/bucket/BucketTable.vue

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ const getBucketPublicStatus = computed(() => {
5858
};
5959
});
6060
61+
const getInternalStatus = computed(() => {
62+
return (node: BucketTreeNode): boolean => {
63+
if (node.data.bucketId) {
64+
return permissionStore.getBucketInternal(node.data.bucketId);
65+
}
66+
return false;
67+
};
68+
});
69+
6170
const emit = defineEmits(['show-bucket-config', 'show-sidebar-info']);
6271
6372
// Actions
@@ -337,6 +346,17 @@ watch(getBuckets, () => {
337346
icon="pi pi-info-circle"
338347
class="public-folder"
339348
/>
349+
350+
<Tag
351+
v-if="!getBucketPublicStatus(node) && getInternalStatus(node)"
352+
v-tooltip="'Contents of this Folder can be read by anyone internal to government.'"
353+
value="Internal"
354+
severity="info"
355+
rounded
356+
icon="pi pi-info-circle"
357+
class="ml-2 mb-1 min-w-100"
358+
/>
359+
340360
<BucketChildConfig
341361
v-if="permissionStore.isBucketActionAllowed(node.data.bucketId, getUserId, Permissions.CREATE)"
342362
:parent-bucket="node.data"

frontend/src/components/bucket/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ export { default as BucketConfigForm } from './BucketConfigForm.vue';
33
export { default as BucketList } from './BucketList.vue';
44
export { default as BucketPermission } from './BucketPermission.vue';
55
export { default as BucketPermissionAddUser } from './BucketPermissionAddUser.vue';
6+
export { default as BucketPublicToggle } from './BucketPublicToggle.vue';
7+
export { default as BucketIdpToggle } from './BucketIdpToggle.vue';
68
export { default as BucketSidebar } from './BucketSidebar.vue';
79
export { default as BucketTable } from './BucketTable.vue';
810
export { default as BucketTableBucketName } from './BucketTableBucketName.vue';

frontend/src/components/object/ObjectFileDetails.vue

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ const object: Ref<COMSObject | undefined> = ref(undefined);
6565
const bucketId: Ref<string> = ref('');
6666
const permissionsVisible: Ref<boolean> = ref(false);
6767
68+
const isInternal = computed(() => permissionStore.getInternal(object.value));
69+
6870
// version stuff
6971
const bucketVersioningEnabled = computed(() => getIsVersioningEnabled.value(props.objectId));
7072
const currentVersionId: Ref<string | undefined> = ref(props.versionId);
@@ -170,6 +172,17 @@ onMounted(async () => {
170172
rounded
171173
icon="pi pi-info-circle"
172174
/>
175+
<Tag
176+
v-else-if="isInternal"
177+
v-tooltip="
178+
'This folder and its contents can be read by anyone internal to government. ' +
179+
'Change the settings in &quot;Folder permissions.&quot;'
180+
"
181+
value="Internal"
182+
severity="info"
183+
rounded
184+
icon="pi pi-info-circle"
185+
/>
173186
</span>
174187
</div>
175188

0 commit comments

Comments
 (0)