Skip to content

Commit

Permalink
introduce the ability to share a space via a user group
Browse files Browse the repository at this point in the history
introduce isMember check to detect if a user is a direct or indirect member of a space
  • Loading branch information
fschade committed Dec 30, 2022
1 parent 052fabb commit 37e77eb
Show file tree
Hide file tree
Showing 11 changed files with 121 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,7 @@ export default defineComponent({
}
})
let groups = recipients.exact.groups.concat(recipients.groups)
const groups = recipients.exact.groups.concat(recipients.groups)
const remotes = recipients.exact.remotes.concat(recipients.remotes)
this.autocompleteResults = users.concat(groups, remotes).filter((collaborator) => {
Expand Down
33 changes: 14 additions & 19 deletions packages/web-app-files/src/components/SideBar/Shares/FileShares.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ import {
shareInviteCollaboratorHelp,
shareInviteCollaboratorHelpCern
} from '../../../helpers/contextualHelpers'
import { computed, defineComponent, PropType } from 'vue'
import { defineComponent, PropType } from 'vue'
import { isProjectSpaceResource, SpaceResource } from 'web-client/src/helpers'
import { createFileRouteOptions } from 'web-pkg/src/helpers/router'
Expand All @@ -103,19 +103,9 @@ export default defineComponent({
setup() {
const store = useStore()
const sharesListCollapsed = !store.getters.configuration.options.sidebar.shares.showAllOnLoad
const currentUserIsMemberOfSpace = computed(() => {
const userId = store.getters.user?.id
if (!userId) {
return false
}
return store.getters['runtime/spaces/spaceMembers'].some(
(member) => member.collaborator?.name === userId
)
})
return {
sharesListCollapsed,
currentUserIsMemberOfSpace,
hasProjectSpaces: useCapabilityProjectSpacesEnabled(),
hasShareJail: useCapabilityShareJailEnabled(),
hasResharing: useCapabilityFilesSharingResharing()
Expand Down Expand Up @@ -234,7 +224,7 @@ export default defineComponent({
return (
this.space?.driveType === 'project' &&
this.highlightedFile.type !== 'space' &&
this.currentUserIsMemberOfSpace
this.space?.isMember(this.user)
)
}
},
Expand Down Expand Up @@ -347,16 +337,21 @@ export default defineComponent({
},
isShareModifiable(collaborator) {
if (
this.space &&
isProjectSpaceResource(this.space) &&
this.currentUserIsMemberOfSpace &&
!this.space?.spaceRoles.manager.includes(this.user.uuid)
) {
const isSharableResource = this.space && isProjectSpaceResource(this.space)
if (!isSharableResource) {
return false
}
return !collaborator.indirect
if (this.space?.isManager(this.user)) {
return true
}
if (collaborator.indirect) {
return true
}
return false
}
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,7 @@ export default defineComponent({
return this.currentUserIsManager
},
currentUserIsManager() {
const currentUserCollaborator = this.spaceMembers.find(
(collaborator) => collaborator.collaborator.name === this.user.id
)
return currentUserCollaborator?.role?.name === spaceRoleManager.name
return this.space.isManager(this.user)
}
},
watch: {
Expand Down
4 changes: 2 additions & 2 deletions packages/web-app-files/src/mixins/actions/delete.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ export default {

if (
isProjectSpaceResource(this.space) &&
!this.space.isEditor(this.user.uuid) &&
!this.space.isManager(this.user.uuid)
!this.space.isEditor(this.user) &&
!this.space.isManager(this.user)
) {
return false
}
Expand Down
4 changes: 2 additions & 2 deletions packages/web-app-files/src/mixins/actions/emptyTrashBin.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ export default {

if (
isProjectSpaceResource(this.space) &&
!this.space.isEditor(this.user.uuid) &&
!this.space.isManager(this.user.uuid)
!this.space.isEditor(this.user) &&
!this.space.isManager(this.user)
) {
return false
}
Expand Down
4 changes: 2 additions & 2 deletions packages/web-app-files/src/mixins/actions/restore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ export default {

if (
isProjectSpaceResource(this.space) &&
!this.space.isEditor(this.user.uuid) &&
!this.space.isManager(this.user.uuid)
!this.space.isEditor(this.user) &&
!this.space.isManager(this.user)
) {
return false
}
Expand Down
4 changes: 2 additions & 2 deletions packages/web-app-files/src/views/spaces/GenericTrash.vue
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,8 @@ export default defineComponent({
showActions() {
return (
!isProjectSpaceResource(this.space) ||
this.space.isEditor(this.user.uuid) ||
this.space.isManager(this.user.uuid)
this.space.isEditor(this.user) ||
this.space.isManager(this.user)
)
}
},
Expand Down
2 changes: 2 additions & 0 deletions packages/web-client/src/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export interface Graph {
groups: {
listGroups: (orderBy?: string) => AxiosPromise<CollectionOfGroup>
createGroup: (group: Group) => AxiosPromise<Group>
getGroup: (groupId: string) => AxiosPromise<Group>
editGroup: (groupId: string, group: Group) => AxiosPromise<void>
deleteGroup: (groupId: string) => AxiosPromise<void>
addMember: (groupId: string, userId: string, server: string) => AxiosPromise<void>
Expand Down Expand Up @@ -116,6 +117,7 @@ export const graph = (baseURI: string, axiosClient: AxiosInstance): Graph => {
groups: {
createGroup: (group: Group) => groupsApiFactory.createGroup(group),
editGroup: (groupId: string, group: Group) => groupApiFactory.updateGroup(groupId, group),
getGroup: (groupId: string) => groupApiFactory.getGroup(groupId),
deleteGroup: (groupId: string) => groupApiFactory.deleteGroup(groupId),
listGroups: (orderBy?: any) =>
groupsApiFactory.listGroups(
Expand Down
8 changes: 7 additions & 1 deletion packages/web-client/src/helpers/resource/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { User } from '../user'

export interface SpaceRole {
id: string
kind: 'user' | 'group'
isMember(u: User): boolean
}

// TODO: add more fields to the resource interface. Extend into different resource types: FileResource, FolderResource, ShareResource, IncomingShareResource, OutgoingShareResource, ...
export interface Resource {
id: number | string
Expand All @@ -16,7 +22,7 @@ export interface Resource {
status?: number
processing?: boolean
spaceRoles?: {
[k: string]: any[]
[k: string]: SpaceRole[]
}
spaceQuota?: any
spaceImageData?: any
Expand Down
71 changes: 51 additions & 20 deletions packages/web-client/src/helpers/space/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
buildWebDavSpacesTrashPath,
extractDomSelector,
extractNodeId,
Resource
Resource,
SpaceRole
} from '../resource'
import { SpacePeopleShareRoles, spaceRoleEditor, spaceRoleManager, spaceRoleViewer } from '../share'
import { PublicSpaceResource, ShareSpaceResource, SpaceResource, SHARE_JAIL_ID } from './types'
Expand Down Expand Up @@ -86,9 +87,36 @@ export function buildSpace(data): SpaceResource {
for (const permission of data.root.permissions) {
for (const role of SpacePeopleShareRoles.list()) {
if (permission.roles.includes(role.name)) {
spaceRoles[role.name].push(
...permission.grantedToIdentities.filter((el) => !!el.user).map((el) => el.user.id)
)
spaceRoles[role.name] = permission.grantedToIdentities.reduce((acc, info) => {
const spaceRole: SpaceRole = {
kind: 'user',
id: '',
isMember(u?: any): boolean {
if (!u) {
return false
}

switch (this.kind) {
case 'user':
return this.id == u.uuid
break
case 'group':
return u.groups.map((g) => g.id).includes(this.id)
break
default:
return false
}
}
}

if (info.hasOwnProperty('group')) {
spaceRole.kind = 'group'
}

spaceRole.id = info[spaceRole.kind].id

return [...acc, spaceRole]
}, [])
}
}
}
Expand Down Expand Up @@ -143,53 +171,53 @@ export function buildSpace(data): SpaceResource {
spaceRoles,
spaceImageData,
spaceReadmeData,
canUpload: function ({ user }: { user?: User } = {}) {
canUpload: function ({ user }: { user?: User } = {}): boolean {
const allowedRoles = [
...this.spaceRoles[spaceRoleManager.name],
...this.spaceRoles[spaceRoleEditor.name]
]
return user && allowedRoles.includes(user.uuid)
return user && allowedRoles.map((r) => r.isAllowed(user)).some(Boolean)
},
canDownload: function () {
return true
},
canBeDeleted: function ({ user }: { user?: User } = {}) {
const allowedRoles = [...this.spaceRoles[spaceRoleManager.name]]
return this.disabled && user && allowedRoles.includes(user.uuid)
return this.disabled && user && allowedRoles.map((r) => r.isAllowed(user)).some(Boolean)
},
canRename: function ({ user }: { user?: User } = {}) {
const allowedRoles = [...this.spaceRoles[spaceRoleManager.name]]
return user && allowedRoles.includes(user.uuid)
return user && allowedRoles.map((r) => r.isAllowed(user)).some(Boolean)
},
canEditDescription: function ({ user }: { user?: User } = {}) {
const allowedRoles = [...this.spaceRoles[spaceRoleManager.name]]
return user && allowedRoles.includes(user.uuid)
return user && allowedRoles.map((r) => r.isAllowed(user)).some(Boolean)
},
canRestore: function ({ user }: { user?: User } = {}) {
const allowedRoles = [...this.spaceRoles[spaceRoleManager.name]]
return this.disabled && user && allowedRoles.includes(user.uuid)
return this.disabled && user && allowedRoles.map((r) => r.isAllowed(user)).some(Boolean)
},
canDisable: function ({ user }: { user?: User } = {}) {
const allowedRoles = [...this.spaceRoles[spaceRoleManager.name]]
return !this.disabled && user && allowedRoles.includes(user.uuid)
return !this.disabled && user && allowedRoles.map((r) => r.isAllowed(user)).some(Boolean)
},
canShare: function ({ user }: { user?: User } = {}) {
const allowedRoles = [...this.spaceRoles[spaceRoleManager.name]]
return user && allowedRoles.includes(user.uuid)
return user && allowedRoles.map((r) => r.isAllowed(user)).some(Boolean)
},
canEditImage: function ({ user }: { user?: User } = {}) {
const allowedRoles = [
...this.spaceRoles[spaceRoleManager.name],
...this.spaceRoles[spaceRoleEditor.name]
]
return user && allowedRoles.includes(user.uuid)
return user && allowedRoles.map((r) => r.isAllowed(user)).some(Boolean)
},
canEditReadme: function ({ user }: { user?: User } = {}) {
const allowedRoles = [
...this.spaceRoles[spaceRoleManager.name],
...this.spaceRoles[spaceRoleEditor.name]
]
return user && allowedRoles.includes(user.uuid)
return user && allowedRoles.map((r) => r.isAllowed(user)).some(Boolean)
},
canCreate: function () {
return true
Expand All @@ -213,14 +241,17 @@ export function buildSpace(data): SpaceResource {
getWebDavTrashUrl({ path }: { path: string }): string {
return urlJoin(webDavTrashUrl, path)
},
isViewer(uuid: string): boolean {
return this.spaceRoles[spaceRoleViewer.name].includes(uuid)
isViewer(user: User): boolean {
return this.spaceRoles[spaceRoleViewer.name].map((r) => r.isAllowed(user)).some(Boolean)
},
isEditor(user: User): boolean {
return this.spaceRoles[spaceRoleEditor.name].map((r) => r.isAllowed(user)).some(Boolean)
},
isEditor(uuid: string): boolean {
return this.spaceRoles[spaceRoleEditor.name].includes(uuid)
isManager(user: User): boolean {
return this.spaceRoles[spaceRoleManager.name].map((r) => r.isAllowed(user)).some(Boolean)
},
isManager(uuid: string): boolean {
return this.spaceRoles[spaceRoleManager.name].includes(uuid)
isMember(user: User): boolean {
return this.isViewer(user) || this.isEditor(user) || this.isManager(user)
}
}
Object.defineProperty(s, 'nodeId', {
Expand Down
52 changes: 37 additions & 15 deletions packages/web-runtime/src/store/spaces.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { buildSpace, isProjectSpaceResource } from 'web-client/src/helpers'
import Vue from 'vue'
import Vue, { Ref } from 'vue'
import { set, has } from 'lodash-es'
import { unref } from 'vue'
import { buildSpaceShare } from 'web-client/src/helpers/share'
import { sortSpaceMembers } from '../helpers/space/sortMembers'

import { configurationManager } from 'web-pkg/src/configuration'
import { Graph } from 'web-client'
import { AxiosResponse } from 'axios'

const state = {
spaces: [],
Expand Down Expand Up @@ -132,20 +134,32 @@ const actions = {
context.commit('CLEAR_PROJECT_SPACES')
context.commit('ADD_SPACES', spaces)
},
loadSpaceMembers(context, { graphClient, space }) {
loadSpaceMembers(context, { graphClient, space }: { graphClient: Ref<Graph>; space: any }) {
context.commit('CLEAR_SPACE_MEMBERS')
const promises = []
const spaceShares = []

for (const role of Object.keys(space.spaceRoles)) {
for (const userId of space.spaceRoles[role]) {
promises.push(
unref(graphClient)
.users.getUser(userId)
.then((resolved) => {
spaceShares.push(buildSpaceShare({ ...resolved.data, role }, space.id))
})
)
for (const { kind, id } of space.spaceRoles[role]) {
const client = unref(graphClient)

let prom: Promise<AxiosResponse>
switch (kind) {
case 'user':
prom = client.users.getUser(id)
break
case 'group':
prom = client.groups.getGroup(id)
break
default:
continue
}

prom.then((resolved) => {
spaceShares.push(buildSpaceShare({ ...resolved.data, role }, space.id))
})

promises.push(prom)
}
}

Expand All @@ -167,10 +181,15 @@ const actions = {
context.commit('UPSERT_SPACE_MEMBERS', buildSpaceShare(shareObj, storageId))
},
async changeSpaceMember(context, { client, graphClient, share, permissions, role }) {
await client.shares.shareSpaceWithUser('', share.collaborator.name, share.id, {
permissions,
role: role.name
})
await client.shares.shareSpaceWithUser(
'',
share.collaborator.name || share.collaborator.displayName,
share.id,
{
permissions,
role: role.name
}
)

const graphResponse = await graphClient.drives.getDrive(share.id)
context.commit('UPSERT_SPACE', buildSpace(graphResponse.data))
Expand All @@ -186,7 +205,10 @@ const actions = {
context.commit('UPSERT_SPACE_MEMBERS', spaceShare)
},
async deleteSpaceMember(context, { client, graphClient, share, reloadSpace = true }) {
const additionalParams = { shareWith: share.collaborator.name } as any
const additionalParams = {
shareWith: share.collaborator.name || share.collaborator.displayName
} as any
console.log(additionalParams)
await client.shares.deleteShare(share.id, additionalParams)

if (reloadSpace) {
Expand Down

0 comments on commit 37e77eb

Please sign in to comment.