Skip to content

Commit

Permalink
Don't show user management in app switcher if user has not role 'admi…
Browse files Browse the repository at this point in the history
…n' (#7197)

Don't show user management in app switcher if user has not role 'admin'
  • Loading branch information
Jan committed Jun 30, 2022
1 parent af2fe70 commit be987b1
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Bugfix: Create space and access user management permission

We've fixed a bug, where users with insufficient permissions could access the user management and were able to see
the "New Space" button in the space overview.

https://github.com/owncloud/web/pull/7197
https://github.com/owncloud/web/issues/7181
https://github.com/owncloud/web/issues/7079
5 changes: 2 additions & 3 deletions packages/web-app-files/src/views/spaces/Projects.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
:show-actions-on-selection="true"
>
<template #actions>
<create-space />
<create-space v-if="hasCreatePermission" />
</template>
<template #content>
<p v-text="spacesHint" />
Expand Down Expand Up @@ -188,8 +188,7 @@ export default defineComponent({
return this.$gettext('Store your project related files in Spaces for seamless collaboration.')
},
hasCreatePermission() {
// @TODO
return true
return this.$permissionManager.hasSpaceManagement()
}
},
watch: {
Expand Down
8 changes: 8 additions & 0 deletions packages/web-app-user-management/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,21 @@ const navItems = [
icon: 'user',
route: {
path: `/${appInfo.id}/users?`
},
enabled: () => {
const permissionManager = window.Vue.$permissionManager
return permissionManager.hasUserManagement()
}
},
{
name: $gettext('Groups'),
icon: 'group-2',
route: {
path: `/${appInfo.id}/groups?`
},
enabled: () => {
const permissionManager = window.Vue.$permissionManager
return permissionManager.hasUserManagement()
}
}
]
Expand Down
22 changes: 22 additions & 0 deletions packages/web-app-user-management/tests/unit/index.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import index from '../../src/index'

describe('navItems', () => {
describe('function "enabled"', () => {
it('should be true if permissions are sufficient', () => {
window.Vue = {}
window.Vue.$permissionManager = {
hasUserManagement: () => true
}
expect(index.navItems[0].enabled()).toBeTruthy()
expect(index.navItems[1].enabled()).toBeTruthy()
})
it('should be false if permissions are insufficient', () => {
window.Vue = {}
window.Vue.$permissionManager = {
hasUserManagement: () => false
}
expect(index.navItems[0].enabled()).toBeFalsy()
expect(index.navItems[1].enabled()).toBeFalsy()
})
})
})
1 change: 1 addition & 0 deletions packages/web-pkg/src/services/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './client'
export * from './permissionManager'
27 changes: 27 additions & 0 deletions packages/web-pkg/src/services/permissionManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Store } from 'vuex'
interface Role {
name: 'admin' | 'spaceadmin' | 'user' | 'guest'
}
interface User {
role: Role
}

export class PermissionManager {
private readonly store: Store<any>

constructor(store: Store<any>) {
this.store = store
}

public hasUserManagement() {
return this.user.role.name === 'admin'
}

public hasSpaceManagement() {
return ['admin', 'spaceadmin'].includes(this.user.role.name)
}

get user(): User {
return this.store.getters.user
}
}
34 changes: 34 additions & 0 deletions packages/web-pkg/tests/unit/services/permissionManager.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { PermissionManager } from '../../../src/services'

beforeEach(jest.resetAllMocks)

describe('permissionManager', () => {
describe('method "hasUserManagement"', () => {
it('should be true if user has sufficient rights', () => {
const permissionManager = new PermissionManager({
getters: { user: { role: { name: 'admin' } } }
} as any)
expect(permissionManager.hasUserManagement()).toBeTruthy()
})
it('should be false if user has insufficient rights', () => {
const permissionManager = new PermissionManager({
getters: { user: { role: { name: 'user' } } }
} as any)
expect(permissionManager.hasUserManagement()).toBeFalsy()
})
})
describe('method "hasSpaceManagement"', () => {
it('should be true if user has sufficient rights', () => {
const permissionManager = new PermissionManager({
getters: { user: { role: { name: 'admin' } } }
} as any)
expect(permissionManager.hasSpaceManagement()).toBeTruthy()
})
it('should be false if user has insufficient rights', () => {
const permissionManager = new PermissionManager({
getters: { user: { role: { name: 'user' } } }
} as any)
expect(permissionManager.hasSpaceManagement()).toBeFalsy()
})
})
})
20 changes: 19 additions & 1 deletion packages/web-runtime/src/container/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { getBackendVersion, getWebVersion } from './versions'
import { useLocalStorage } from 'web-pkg/src/composables'
import { unref } from '@vue/composition-api'
import { useDefaultThemeName } from '../composables'
import { clientService } from 'web-pkg/src/services'
import { clientService, PermissionManager } from 'web-pkg/src/services'
import { UppyService } from '../services/uppyService'

import { init as SentryInit } from '@sentry/browser'
Expand Down Expand Up @@ -253,6 +253,24 @@ export const announceClientService = ({
vue.prototype.$clientService.owncloudSdk = sdk
}

/**
* announce uppyService and inject it into vue
*
* @param vue
* @param store
*/
export const announcePermissionManager = ({
vue,
store
}: {
vue: VueConstructor
store: Store<any>
}): void => {
const permissionManager = new PermissionManager(store)
vue.prototype.$permissionManager = permissionManager
set(vue, '$permissionManager', permissionManager)
}

/**
* announce uppyService and inject it into vue
*
Expand Down
2 changes: 2 additions & 0 deletions packages/web-runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
announceVersions,
applicationStore,
announceUppyService,
announcePermissionManager,
startSentry
} from './container'

Expand All @@ -29,6 +30,7 @@ export const bootstrap = async (configurationPath: string): Promise<void> => {
startSentry(runtimeConfiguration, Vue)
announceClientService({ vue: Vue, runtimeConfiguration })
announceUppyService({ vue: Vue })
announcePermissionManager({ vue: Vue, store })
await announceClient(runtimeConfiguration)
await announceStore({ vue: Vue, store, runtimeConfiguration })
await announceApplications({
Expand Down
47 changes: 44 additions & 3 deletions packages/web-runtime/src/store/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { router } from '../router'
import { clientService } from 'web-pkg/src/services'

import { setUser as sentrySetUser } from '@sentry/browser'
import axios from 'axios'

let vueAuthInstance

Expand All @@ -20,7 +21,9 @@ const state = {
groups: [],
userReady: false,
quota: null,
language: null
language: null,
role: {},
roles: []
}

const actions = {
Expand Down Expand Up @@ -101,9 +104,42 @@ const actions = {

// FIXME: Can be removed as soon as the uuid is integrated in the OCS api
let graphUser
let role = []
if (context.state.capabilities.spaces?.enabled) {
const graphClient = clientService.graphAuthenticated(instance, token)
graphUser = await graphClient.users.getMe()

const {
data: { bundles: roles }
} = await axios.post(
'/api/v0/settings/roles-list',
{},
{
headers: {
authorization: `Bearer ${token}`
}
}
)

context.commit('SET_ROLES', roles)

const userAssignmentResponse = await axios.post(
'/api/v0/settings/assignments-list',
{
account_uuid: graphUser.data.id
},
{
headers: {
authorization: `Bearer ${token}`
}
}
)
const assignments = userAssignmentResponse.data?.assignments
const roleAssignment = assignments.find((assignment) => 'roleId' in assignment)

if (roleAssignment) {
role = roles.find((role) => role.id === roleAssignment.roleId)
}
}

let userEmail = ''
Expand All @@ -124,6 +160,7 @@ const actions = {
token,
isAuthenticated: true,
groups: userGroups,
role,
language
})

Expand Down Expand Up @@ -261,6 +298,7 @@ const mutations = {
state.token = user.token
state.groups = user.groups
state.language = user.language
state.role = user.role
sentrySetUser({ username: user.id })
},
SET_CAPABILITIES(state, data) {
Expand All @@ -281,6 +319,9 @@ const mutations = {
quota.total = parseInt(quota.total)

state.quota = quota
},
SET_ROLES(state, roles) {
state.roles = roles
}
}

Expand All @@ -300,8 +341,8 @@ const getters = {
capabilities: (state) => {
return state.capabilities
},

quota: (state) => state.quota
quota: (state) => state.quota,
roles: (state) => state.roles
}

export default {
Expand Down
12 changes: 11 additions & 1 deletion packages/web-runtime/tests/unit/container/bootstrap.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { announceApplications } from '../../../src/container'
import { announceApplications, announcePermissionManager } from '../../../src/container'
import { buildApplication } from '../../../src/container/application'
import { Vue } from './../../../src/defaults'

jest.mock('../../../src/container/application')

Expand Down Expand Up @@ -40,3 +41,12 @@ describe('announce applications', () => {
expect(errorSpy.mock.calls[1][0]).toMatchObject(fishyError)
})
})

describe('announcePermissionManager', () => {
it('should inject vue object contains permissionManager instance', () => {
const vue = Vue
announcePermissionManager({ vue: Vue, store: {} as any })
expect(vue.prototype.$permissionManager).toBeDefined()
expect(vue.$permissionManager).toBeDefined()
})
})

0 comments on commit be987b1

Please sign in to comment.