Commit c08b5ac8 authored by Nicolas Giard's avatar Nicolas Giard

feat: group permissions

parent 74aa3d90
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
v-spacer v-spacer
v-btn(color='indigo', large, outline, to='/groups') v-btn(color='indigo', large, outline, to='/groups')
v-icon arrow_back v-icon arrow_back
v-dialog(v-model='deleteGroupDialog', max-width='500') v-dialog(v-model='deleteGroupDialog', max-width='500', v-if='!group.isSystem')
v-btn(color='red', large, outline, slot='activator') v-btn(color='red', large, outline, slot='activator')
v-icon(color='red') delete v-icon(color='red') delete
v-card v-card
...@@ -45,28 +45,29 @@ ...@@ -45,28 +45,29 @@
.caption.mt-3.grey--text ID: {{group.id}} .caption.mt-3.grey--text ID: {{group.id}}
v-tab-item(key='permissions', :transition='false', :reverse-transition='false') v-tab-item(key='permissions', :transition='false', :reverse-transition='false')
v-card v-container.pa-3(fluid, grid-list-md)
v-layout(row, wrap)
v-flex(xs12, md6, lg4, v-for='pmGroup in permissions')
v-card.md2.grey.lighten-5(flat)
v-subheader {{pmGroup.category}}
v-card-text.pt-0
template(v-for='(pm, idx) in pmGroup.items')
v-checkbox.pt-0(
:key='pm.permission'
:label='pm.permission'
:hint='pm.hint'
persistent-hint
color='primary'
v-model='group.permissions'
:value='pm.permission'
:append-icon='pm.warning ? "warning" : null',
:disabled='(group.isSystem && pm.restrictedForSystem) || group.id === 1 || pm.disabled'
)
v-divider.mt-3(v-if='idx < pmGroup.items.length - 1')
v-tab-item(key='rules', :transition='false', :reverse-transition='false') v-tab-item(key='rules', :transition='false', :reverse-transition='false')
v-card v-card
v-card-title.pb-0 v-card-title.pb-0
v-subheader
v-icon.mr-2 border_color
.subheading Read and Write
v-spacer
v-btn(flat, outline)
v-icon(left) arrow_drop_down
| Load Preset
v-btn(flat, outline)
v-icon(left) vertical_align_bottom
| Import Rules
.pa-3.pl-4
criterias
v-divider.my-0
v-card-title.pb-0
v-subheader
v-icon.mr-2 pageview
.subheading Read Only
v-spacer v-spacer
v-btn(flat, outline) v-btn(flat, outline)
v-icon(left) arrow_drop_down v-icon(left) arrow_drop_down
...@@ -76,25 +77,12 @@ ...@@ -76,25 +77,12 @@
| Import Rules | Import Rules
.pa-3.pl-4 .pa-3.pl-4
criterias criterias
v-divider.my-0
v-card-title.pb-0
v-subheader Legend
.px-4.pb-4
.body-1.px-1.py-2 Any number of rules can be used at the same time. However, some rules requires more processing time than others. Rule types are color-coded as followed:
.caption
v-icon(color='blue') stop
span Fast rules. None or insignificant latency introduced to all page loads.
.caption
v-icon(color='orange') stop
span Medium rules. Some latency added to all page loads.
.caption
v-icon(color='red') stop
span Slow rules. May adds noticeable latency to all page loads. Avoid using in multiple rules.
v-tab-item(key='users', :transition='false', :reverse-transition='false') v-tab-item(key='users', :transition='false', :reverse-transition='false')
v-card v-card
v-card-title.pb-0 v-card-title.pb-0
v-btn(color='primary', @click='searchUserDialog = true') v-spacer
v-btn(color='primary', outline, flat, @click='searchUserDialog = true')
v-icon(left) assignment_ind v-icon(left) assignment_ind
| Assign User | Assign User
v-data-table( v-data-table(
...@@ -148,12 +136,138 @@ export default { ...@@ -148,12 +136,138 @@ export default {
group: { group: {
id: 0, id: 0,
name: '', name: '',
isSystem: false,
permissions: [],
pageRules: [],
users: [] users: []
}, },
name: '', name: '',
deleteGroupDialog: false, deleteGroupDialog: false,
searchUserDialog: false, searchUserDialog: false,
pagination: {}, pagination: {},
permissions: [
{
category: 'Content',
items: [
{
permission: 'read:pages',
hint: 'Can view pages, as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'write:pages',
hint: 'Can view and create new pages, as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'manage:pages',
hint: 'Can view, create, edit and move existing pages as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'delete:pages',
hint: 'Can delete existing pages, as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'write:assets',
hint: 'Can upload assets (such as images and files), as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'read:comments',
hint: 'Can view comments, as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'write:comments',
hint: 'Can post new comments, as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
}
]
},
{
category: 'Users',
items: [
{
permission: 'write:users',
hint: 'Can create or authorize new users, but not modify existing ones',
warning: false,
restrictedForSystem: true,
disabled: false
},
{
permission: 'manage:users',
hint: 'Can manage all users (but not users with administrative permissions)',
warning: false,
restrictedForSystem: true,
disabled: false
},
{
permission: 'write:groups',
hint: 'Can manage groups and assign CONTENT permissions / page rules',
warning: false,
restrictedForSystem: true,
disabled: false
},
{
permission: 'manage:groups',
hint: 'Can manage groups and assign ANY permissions (but not manage:system) / page rules',
warning: true,
restrictedForSystem: true,
disabled: false
}
]
},
{
category: 'Administration',
items: [
{
permission: 'manage:navigation',
hint: 'Can manage the site navigation',
warning: false,
restrictedForSystem: true,
disabled: false
},
{
permission: 'manage:theme',
hint: 'Can manage and modify themes',
warning: false,
restrictedForSystem: true,
disabled: false
},
{
permission: 'manage:api',
hint: 'Can generate and revoke API keys',
warning: true,
restrictedForSystem: true,
disabled: false
},
{
permission: 'manage:system',
hint: 'Can manage and access everything. Root administrator.',
warning: true,
restrictedForSystem: true,
disabled: true
}
]
}
],
users: [], users: [],
headers: [ headers: [
{ text: 'ID', value: 'id', width: 50, align: 'right' }, { text: 'ID', value: 'id', width: 50, align: 'right' },
......
...@@ -17,7 +17,17 @@ ...@@ -17,7 +17,17 @@
v-card v-card
.dialog-header.is-short New Group .dialog-header.is-short New Group
v-card-text v-card-text
v-text-field(v-model='newGroupName', label='Group Name', autofocus, counter='255', @keyup.enter='createGroup') v-text-field.md2(
solo,
flat,
background-color='grey lighten-4'
prepend-icon='people'
v-model='newGroupName'
label='Group Name'
counter='255'
@keyup.enter='createGroup'
ref='groupNameInput'
)
v-card-chin v-card-chin
v-spacer v-spacer
v-btn(flat, @click='newGroupDialog = false') Cancel v-btn(flat, @click='newGroupDialog = false') Cancel
...@@ -38,6 +48,10 @@ ...@@ -38,6 +48,10 @@
td {{ props.item.userCount }} td {{ props.item.userCount }}
td {{ props.item.createdAt | moment('calendar') }} td {{ props.item.createdAt | moment('calendar') }}
td {{ props.item.updatedAt | moment('calendar') }} td {{ props.item.updatedAt | moment('calendar') }}
td
v-tooltip(left, v-if='props.item.isSystem')
v-icon(slot='activator') lock_outline
span System Group
template(slot='no-data') template(slot='no-data')
v-alert.ma-3(icon='warning', :value='true', outline) No groups to display. v-alert.ma-3(icon='warning', :value='true', outline) No groups to display.
.text-xs-center.py-2(v-if='groups.length > 15') .text-xs-center.py-2(v-if='groups.length > 15')
...@@ -64,7 +78,8 @@ export default { ...@@ -64,7 +78,8 @@ export default {
{ text: 'Name', value: 'name' }, { text: 'Name', value: 'name' },
{ text: 'Users', value: 'userCount', width: 200 }, { text: 'Users', value: 'userCount', width: 200 },
{ text: 'Created', value: 'createdAt', width: 250 }, { text: 'Created', value: 'createdAt', width: 250 },
{ text: 'Last Updated', value: 'updatedAt', width: 250 } { text: 'Last Updated', value: 'updatedAt', width: 250 },
{ text: '', value: 'isSystem', width: 20, sortable: false }
], ],
search: '' search: ''
} }
......
...@@ -3,6 +3,7 @@ query { ...@@ -3,6 +3,7 @@ query {
list { list {
id id
name name
isSystem
userCount userCount
createdAt createdAt
updatedAt updatedAt
......
...@@ -3,7 +3,9 @@ query ($id: Int!) { ...@@ -3,7 +3,9 @@ query ($id: Int!) {
single(id: $id) { single(id: $id) {
id id
name name
rights { isSystem
permissions
pageRules {
id id
path path
role role
......
.datatable { .v-datatable {
.is-clickable { .is-clickable {
cursor: pointer; cursor: pointer;
} }
......
...@@ -76,6 +76,12 @@ jobs: ...@@ -76,6 +76,12 @@ jobs:
onInit: false onInit: false
cron: false cron: false
concurrency: 1 concurrency: 1
groups:
defaultPermissions:
- 'manage:pages'
- 'write:assets'
- 'read:comments'
- 'write:comments'
telemetry: telemetry:
BUGSNAG_ID: 'bb4b324d0675bcbba10025617fd2cec8' BUGSNAG_ID: 'bb4b324d0675bcbba10025617fd2cec8'
BUGSNAG_REMOTE: 'https://notify.bugsnag.com' BUGSNAG_REMOTE: 'https://notify.bugsnag.com'
......
...@@ -39,7 +39,9 @@ module.exports = { ...@@ -39,7 +39,9 @@ module.exports = {
}, },
async create(obj, args) { async create(obj, args) {
const group = await WIKI.models.groups.query().insertAndFetch({ const group = await WIKI.models.groups.query().insertAndFetch({
name: args.name name: args.name,
permissions: JSON.stringify(WIKI.data.groups.defaultPermissions),
isSystem: false
}) })
return { return {
responseResult: graphHelper.generateSuccess('Group created successfully.'), responseResult: graphHelper.generateSuccess('Group created successfully.'),
......
...@@ -38,7 +38,7 @@ type AuthenticationMutation { ...@@ -38,7 +38,7 @@ type AuthenticationMutation {
updateStrategies( updateStrategies(
strategies: [AuthenticationStrategyInput] strategies: [AuthenticationStrategyInput]
): DefaultResponse ): DefaultResponse @auth(requires: ["manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
......
...@@ -18,11 +18,11 @@ type GroupQuery { ...@@ -18,11 +18,11 @@ type GroupQuery {
list( list(
filter: String filter: String
orderBy: String orderBy: String
): [GroupMinimal] ): [GroupMinimal] @auth(requires: ["write:groups", "manage:groups", "manage:system"])
single( single(
id: Int! id: Int!
): Group ): Group @auth(requires: ["write:groups", "manage:groups", "manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
...@@ -32,26 +32,26 @@ type GroupQuery { ...@@ -32,26 +32,26 @@ type GroupQuery {
type GroupMutation { type GroupMutation {
create( create(
name: String! name: String!
): GroupResponse ): GroupResponse @auth(requires: ["write:groups", "manage:groups", "manage:system"])
update( update(
id: Int! id: Int!
name: String! name: String!
): DefaultResponse ): DefaultResponse @auth(requires: ["write:groups", "manage:groups", "manage:system"])
delete( delete(
id: Int! id: Int!
): DefaultResponse ): DefaultResponse @auth(requires: ["write:groups", "manage:groups", "manage:system"])
assignUser( assignUser(
groupId: Int! groupId: Int!
userId: Int! userId: Int!
): DefaultResponse ): DefaultResponse @auth(requires: ["write:groups", "manage:groups", "manage:system"])
unassignUser( unassignUser(
groupId: Int! groupId: Int!
userId: Int! userId: Int!
): DefaultResponse ): DefaultResponse @auth(requires: ["write:groups", "manage:groups", "manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
...@@ -66,6 +66,7 @@ type GroupResponse { ...@@ -66,6 +66,7 @@ type GroupResponse {
type GroupMinimal { type GroupMinimal {
id: Int! id: Int!
name: String! name: String!
isSystem: Boolean!
userCount: Int userCount: Int
createdAt: Date! createdAt: Date!
updatedAt: Date! updatedAt: Date!
...@@ -74,8 +75,10 @@ type GroupMinimal { ...@@ -74,8 +75,10 @@ type GroupMinimal {
type Group { type Group {
id: Int! id: Int!
name: String! name: String!
rights: [Right] isSystem: Boolean!
users: [User] permissions: [String]!
pageRules: [Right]
users: [UserMinimal]
createdAt: Date! createdAt: Date!
updatedAt: Date! updatedAt: Date!
} }
...@@ -26,14 +26,14 @@ type LocalizationQuery { ...@@ -26,14 +26,14 @@ type LocalizationQuery {
type LocalizationMutation { type LocalizationMutation {
downloadLocale( downloadLocale(
locale: String! locale: String!
): DefaultResponse ): DefaultResponse @auth(requires: ["manage:system"])
updateLocale( updateLocale(
locale: String! locale: String!
autoUpdate: Boolean! autoUpdate: Boolean!
namespacing: Boolean! namespacing: Boolean!
namespaces: [String]! namespaces: [String]!
): DefaultResponse ): DefaultResponse @auth(requires: ["manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
......
...@@ -22,7 +22,7 @@ type LoggingQuery { ...@@ -22,7 +22,7 @@ type LoggingQuery {
loggers( loggers(
filter: String filter: String
orderBy: String orderBy: String
): [Logger] ): [Logger] @auth(requires: ["manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
...@@ -32,7 +32,7 @@ type LoggingQuery { ...@@ -32,7 +32,7 @@ type LoggingQuery {
type LoggingMutation { type LoggingMutation {
updateLoggers( updateLoggers(
loggers: [LoggerInput] loggers: [LoggerInput]
): DefaultResponse ): DefaultResponse @auth(requires: ["manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
......
...@@ -45,7 +45,7 @@ type PageMutation { ...@@ -45,7 +45,7 @@ type PageMutation {
publishStartDate: Date publishStartDate: Date
tags: [String]! tags: [String]!
title: String! title: String!
): PageResponse ): PageResponse @auth(requires: ["write:pages", "manage:pages", "manage:system"])
update( update(
id: Int! id: Int!
...@@ -60,11 +60,11 @@ type PageMutation { ...@@ -60,11 +60,11 @@ type PageMutation {
publishStartDate: Date publishStartDate: Date
tags: [String] tags: [String]
title: String title: String
): PageResponse ): PageResponse @auth(requires: ["manage:pages", "manage:system"])
delete( delete(
id: Int! id: Int!
): DefaultResponse ): DefaultResponse @auth(requires: ["delete:pages", "manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
......
...@@ -18,7 +18,7 @@ type RenderingQuery { ...@@ -18,7 +18,7 @@ type RenderingQuery {
renderers( renderers(
filter: String filter: String
orderBy: String orderBy: String
): [Renderer] ): [Renderer] @auth(requires: ["manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
...@@ -28,7 +28,7 @@ type RenderingQuery { ...@@ -28,7 +28,7 @@ type RenderingQuery {
type RenderingMutation { type RenderingMutation {
updateRenderers( updateRenderers(
renderers: [RendererInput] renderers: [RendererInput]
): DefaultResponse ): DefaultResponse @auth(requires: ["manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
......
...@@ -18,7 +18,7 @@ type SearchQuery { ...@@ -18,7 +18,7 @@ type SearchQuery {
searchEngines( searchEngines(
filter: String filter: String
orderBy: String orderBy: String
): [SearchEngine] ): [SearchEngine] @auth(requires: ["manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
...@@ -28,7 +28,7 @@ type SearchQuery { ...@@ -28,7 +28,7 @@ type SearchQuery {
type SearchMutation { type SearchMutation {
updateSearchEngines( updateSearchEngines(
searchEngines: [SearchEngineInput] searchEngines: [SearchEngineInput]
): DefaultResponse ): DefaultResponse @auth(requires: ["manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
......
...@@ -18,7 +18,7 @@ type StorageQuery { ...@@ -18,7 +18,7 @@ type StorageQuery {
targets( targets(
filter: String filter: String
orderBy: String orderBy: String
): [StorageTarget] ): [StorageTarget] @auth(requires: ["manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
...@@ -28,7 +28,7 @@ type StorageQuery { ...@@ -28,7 +28,7 @@ type StorageQuery {
type StorageMutation { type StorageMutation {
updateTargets( updateTargets(
targets: [StorageTargetInput] targets: [StorageTargetInput]
): DefaultResponse ): DefaultResponse @auth(requires: ["manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
......
...@@ -15,7 +15,7 @@ extend type Mutation { ...@@ -15,7 +15,7 @@ extend type Mutation {
# ----------------------------------------------- # -----------------------------------------------
type SystemQuery { type SystemQuery {
info: SystemInfo info: SystemInfo @auth(requires: ["manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
......
...@@ -15,8 +15,8 @@ extend type Mutation { ...@@ -15,8 +15,8 @@ extend type Mutation {
# ----------------------------------------------- # -----------------------------------------------
type ThemingQuery { type ThemingQuery {
themes: [ThemingTheme] themes: [ThemingTheme] @auth(requires: ["manage:theme", "manage:system"])
config: ThemingConfig config: ThemingConfig @auth(requires: ["manage:theme", "manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
...@@ -27,7 +27,7 @@ type ThemingMutation { ...@@ -27,7 +27,7 @@ type ThemingMutation {
setConfig( setConfig(
theme: String! theme: String!
darkMode: Boolean! darkMode: Boolean!
): DefaultResponse ): DefaultResponse @auth(requires: ["manage:theme", "manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
......
...@@ -18,15 +18,15 @@ type UserQuery { ...@@ -18,15 +18,15 @@ type UserQuery {
list( list(
filter: String filter: String
orderBy: String orderBy: String
): [UserMinimal] ): [UserMinimal] @auth(requires: ["write:users", "manage:users", "manage:system"])
search( search(
query: String! query: String!
): [UserMinimal] ): [UserMinimal] @auth(requires: ["write:groups", "manage:groups", "write:users", "manage:users", "manage:system"])
single( single(
id: Int! id: Int!
): User ): User @auth(requires: ["manage:users", "manage:system"])
} }
# ----------------------------------------------- # -----------------------------------------------
...@@ -41,7 +41,7 @@ type UserMutation { ...@@ -41,7 +41,7 @@ type UserMutation {
provider: String! provider: String!
providerId: String providerId: String
role: UserRole! role: UserRole!
): UserResponse ): UserResponse @auth(requires: ["write:users", "manage:users", "manage:system"])
update( update(
id: Int! id: Int!
...@@ -50,11 +50,11 @@ type UserMutation { ...@@ -50,11 +50,11 @@ type UserMutation {
provider: String provider: String
providerId: String providerId: String
role: UserRole role: UserRole
): UserResponse ): UserResponse @auth(requires: ["manage:users", "manage:system"])
delete( delete(
id: Int! id: Int!
): DefaultResponse ): DefaultResponse @auth(requires: ["manage:users", "manage:system"])
resetPassword( resetPassword(
id: Int! id: Int!
......
...@@ -142,7 +142,7 @@ module.exports = () => { ...@@ -142,7 +142,7 @@ module.exports = () => {
}) })
const guestGroup = await WIKI.models.groups.query().insert({ const guestGroup = await WIKI.models.groups.query().insert({
name: 'Guests', name: 'Guests',
permissions: JSON.stringify(['read:page:/']), permissions: JSON.stringify(['read:pages']),
isSystem: true isSystem: true
}) })
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment