feat!: rename right to permission (#1277)

This commit is contained in:
kolaente
2025-08-13 11:05:05 +02:00
committed by GitHub
parent 70eef88557
commit a81a3ee0e5
130 changed files with 872 additions and 752 deletions

View File

@@ -139,7 +139,7 @@ linters:
text: 'G115: integer overflow conversion int -> uint64'
- linters:
- recvcheck
text: the methods of "Right" use pointer receiver and non-pointer receiver.
text: the methods of "Permission" use pointer receiver and non-pointer receiver.
- linters:
- recvcheck
text: the methods of "SubscriptionEntityType" use pointer receiver and non-pointer receiver.

View File

@@ -73,7 +73,7 @@ The Go backend follows a layered architecture with clear separation of concerns:
**Key Patterns:**
- **Generic CRUD**: Models implement `CRUDable` interface for standardized database operations
- **Rights System**: Three-tier permissions (Read/Write/Admin) enforced across all operations
- **Permissions System**: Three-tier permissions (Read/Write/Admin) enforced across all operations
- **Event-Driven**: Event system for notifications, webhooks, and cross-cutting concerns
- **Modular Design**: Pluggable authentication, avatar providers, migration tools
@@ -115,7 +115,7 @@ Modern Vue 3 composition API application with TypeScript:
### Adding New Features
**Backend Changes:**
1. Create/modify models in `pkg/models/` with proper CRUD and Rights interfaces as required
1. Create/modify models in `pkg/models/` with proper CRUD and Permissions interfaces as required
2. Add database migration if needed: `mage dev:make-migration <StructName>`
3. Create/update services in `pkg/services/` for complex business logic
4. Add API routes in `pkg/routes/api/v1/` following existing patterns
@@ -137,7 +137,7 @@ Modern Vue 3 composition API application with TypeScript:
### API Development
- All API endpoints follow RESTful conventions under `/api/v1/`
- Use generic web handlers in `pkg/web/handler/` for standard CRUD operations
- Implement proper rights checking using the Rights interface
- Implement proper permissions checking using the Permissions interface
- Add Swagger annotations for automatic documentation generation
### Testing
@@ -175,7 +175,7 @@ After adjusting the source string, you need to call the respective translation l
- Use `pkg/config/` for configuration management
**Code Style:**
- Go: golangci-lint per `.golangci.yml`; use goimports; wrap errors with `fmt.Errorf("...: %w", err)`; enforce rights checks in models; never log secrets; do not edit generated `pkg/swagger/*`
- Go: golangci-lint per `.golangci.yml`; use goimports; wrap errors with `fmt.Errorf("...: %w", err)`; enforce permissions checks in models; never log secrets; do not edit generated `pkg/swagger/*`
- Vue: ESLint + TS; single quotes, trailing commas, no semicolons, tab indent; script setup + lang ts; keep services/models in sync with backend
- Follow existing patterns for consistency
@@ -184,16 +184,16 @@ After adjusting the source string, you need to call the respective translation l
- Vue: PascalCase for components, camelCase for composables
- API endpoints: kebab-case in URLs, camelCase in JSON
**Rights and Permissions:**
- Always implement Rights interface for new models
**Permissions and Permissions:**
- Always implement Permissions interface for new models
- Use `CanRead`, `CanWrite`, `CanCreate`, `CanDelete` methods
- Rights are enforced at the model level, not just routes
- Permissions are enforced at the model level, not just routes
## Common Gotchas
- Database migrations are irreversible in production - test thoroughly
- Frontend services must match backend model structure exactly
- Rights checking is mandatory for all CRUD operations
- Permissions checking is mandatory for all CRUD operations
- Event listeners in `pkg/*/listeners.go` must be registered properly
- CORS settings in backend must allow frontend domain
- API tokens have different scopes - check permissions carefully

View File

@@ -59,7 +59,7 @@ describe('Project View List', () => {
UserProjectFactory.create(1, {
project_id: 2,
user_id: 1,
right: 0,
permission: 0,
})
const projects = ProjectFactory.create(2, {
owner_id: '{increment}',

View File

@@ -11,7 +11,7 @@ function prepareLinkShare() {
})
const linkShares = LinkShareFactory.create(1, {
project_id: projects[0].id,
right: 0,
permission: 0,
})
return {

View File

@@ -11,7 +11,7 @@ export class LinkShareFactory extends Factory {
id: '{increment}',
hash: faker.lorem.word(32),
project_id: 1,
right: 0,
permission: 0,
sharing_type: 0,
shared_by_id: 1,
created: now.toISOString(),

View File

@@ -10,7 +10,7 @@ export class UserProjectFactory extends Factory {
id: '{increment}',
project_id: 1,
user_id: 1,
right: 0,
permission: 0,
created: now.toISOString(),
updated: now.toISOString(),
}

View File

@@ -116,7 +116,7 @@
<script setup lang="ts">
import { computed } from 'vue'
import { RIGHTS as Rights } from '@/constants/rights'
import { PERMISSIONS as Permissions } from '@/constants/permissions'
import ProjectSettingsDropdown from '@/components/project/ProjectSettingsDropdown.vue'
import Dropdown from '@/components/misc/Dropdown.vue'
@@ -137,7 +137,7 @@ import { useAuthStore } from '@/stores/auth'
const baseStore = useBaseStore()
const currentProject = computed(() => baseStore.currentProject)
const background = computed(() => baseStore.background)
const canWriteCurrentProject = computed(() => baseStore.currentProject?.maxRight > Rights.READ)
const canWriteCurrentProject = computed(() => baseStore.currentProject?.maxPermission > Permissions.READ)
const menuActive = computed(() => baseStore.menuActive)
const authStore = useAuthStore()

View File

@@ -15,7 +15,7 @@
/>
</BaseButton>
<span
v-if="project.id > 0 && project.maxRight > RIGHTS.READ"
v-if="project.id > 0 && project.maxPermission > PERMISSIONS.READ"
class="icon menu-item-icon handle drag-handle-standalone"
@mousedown.stop
@click.stop.prevent
@@ -48,7 +48,7 @@
<span class="project-menu-title">{{ getProjectTitle(project) }}</span>
</BaseButton>
<BaseButton
v-if="project.id > 0 && project.maxRight > RIGHTS.READ"
v-if="project.id > 0 && project.maxPermission > PERMISSIONS.READ"
class="favorite"
:class="{'is-favorite': project.isFavorite}"
@click="projectStore.toggleProjectFavorite(project)"
@@ -57,7 +57,7 @@
<Icon :icon="project.isFavorite ? 'star' : ['far', 'star']" />
</BaseButton>
<ProjectSettingsDropdown
v-if="project.maxRight > RIGHTS.READ"
v-if="project.maxPermission > PERMISSIONS.READ"
class="menu-list-dropdown"
:project="project"
>
@@ -97,7 +97,7 @@ import ProjectSettingsDropdown from '@/components/project/ProjectSettingsDropdow
import {getProjectTitle} from '@/helpers/getProjectTitle'
import ColorBubble from '@/components/misc/ColorBubble.vue'
import ProjectsNavigation from '@/components/home/ProjectsNavigation.vue'
import {RIGHTS} from '@/constants/rights'
import {PERMISSIONS} from '@/constants/permissions'
const props = defineProps<{
project: IProject,

View File

@@ -110,7 +110,7 @@
{{ $t('menu.createProject') }}
</DropdownItem>
<DropdownItem
v-if="project.maxRight === RIGHTS.ADMIN"
v-if="project.maxPermission === PERMISSIONS.ADMIN"
v-tooltip="isDefaultProject ? $t('menu.cantDeleteIsDefault') : ''"
:to="{ name: 'project.settings.delete', params: { projectId: project.id } }"
icon="trash-alt"
@@ -137,7 +137,7 @@ import {isSavedFilter} from '@/services/savedFilter'
import {useConfigStore} from '@/stores/config'
import {useProjectStore} from '@/stores/projects'
import {useAuthStore} from '@/stores/auth'
import {RIGHTS} from '@/constants/rights'
import {PERMISSIONS} from '@/constants/permissions'
const props = defineProps<{
project: IProject

View File

@@ -77,7 +77,7 @@ const currentProject = computed<IProject>(() => {
id: 0,
title: '',
isArchived: false,
maxRight: null,
maxPermission: null,
} : baseStore.currentProject
})
useTitle(() => currentProject.value?.id ? getProjectTitle(currentProject.value) : '')

View File

@@ -87,7 +87,7 @@ import TaskForm from '@/components/tasks/TaskForm.vue'
import GanttChart from '@/components/gantt/GanttChart.vue'
import {useGanttFilters} from '../../../views/project/helpers/useGanttFilters'
import {RIGHTS} from '@/constants/rights'
import {PERMISSIONS} from '@/constants/permissions'
import type {DateISO} from '@/types/DateISO'
import type {ITask} from '@/modelTypes/ITask'
@@ -103,7 +103,7 @@ const props = defineProps<{
const baseStore = useBaseStore()
const canWrite = computed(() => baseStore.currentProject?.maxRight > RIGHTS.READ)
const canWrite = computed(() => baseStore.currentProject?.maxPermission > PERMISSIONS.READ)
const {route, viewId} = toRefs(props)
const {

View File

@@ -279,7 +279,7 @@ import {useI18n} from 'vue-i18n'
import draggable from 'zhyswan-vuedraggable'
import {klona} from 'klona/lite'
import {RIGHTS as Rights} from '@/constants/rights'
import {PERMISSIONS as Permissions} from '@/constants/permissions'
import BucketModel from '@/models/bucket'
import type {IBucket} from '@/modelTypes/IBucket'
@@ -398,7 +398,7 @@ const bucketDraggableComponentData = computed(() => ({
}))
const project = computed(() => props.projectId ? projectStore.projects[props.projectId] : null)
const view = computed(() => project.value?.views.find(v => v.id === props.viewId) as IProjectView || null)
const canWrite = computed(() => baseStore.currentProject?.maxRight > Rights.READ && view.value.bucketConfigurationMode === 'manual')
const canWrite = computed(() => baseStore.currentProject?.maxPermission > Permissions.READ && view.value.bucketConfigurationMode === 'manual')
const canCreateTasks = computed(() => canWrite.value && props.projectId > 0)
const buckets = computed(() => kanbanStore.buckets)
const loading = computed(() => kanbanStore.isLoading)

View File

@@ -109,7 +109,7 @@ import Pagination from '@/components/misc/Pagination.vue'
import {ALPHABETICAL_SORT} from '@/components/project/partials/Filters.vue'
import {useTaskList} from '@/composables/useTaskList'
import {RIGHTS as Rights} from '@/constants/rights'
import {PERMISSIONS as Permissions} from '@/constants/permissions'
import {calculateItemPosition} from '@/helpers/calculateItemPosition'
import type {ITask} from '@/modelTypes/ITask'
import {isSavedFilter} from '@/services/savedFilter'
@@ -182,7 +182,7 @@ const baseStore = useBaseStore()
const project = computed(() => baseStore.currentProject)
const canWrite = computed(() => {
return project.value.maxRight > Rights.READ && project.value.id > 0
return project.value.maxPermission > Permissions.READ && project.value.id > 0
})
const isPseudoProject = computed(() => (project.value && isSavedFilter(project.value)) || project.value?.id === -1)

View File

@@ -29,22 +29,22 @@
class="label"
for="linkShareRight"
>
{{ $t('project.share.right.title') }}
{{ $t('project.share.permission.title') }}
</label>
<div class="control">
<div class="select">
<select
id="linkShareRight"
v-model="selectedRight"
v-model="selectedPermission"
>
<option :value="RIGHTS.READ">
{{ $t('project.share.right.read') }}
<option :value="PERMISSIONS.READ">
{{ $t('project.share.permission.read') }}
</option>
<option :value="RIGHTS.READ_WRITE">
{{ $t('project.share.right.readWrite') }}
<option :value="PERMISSIONS.READ_WRITE">
{{ $t('project.share.permission.readWrite') }}
</option>
<option :value="RIGHTS.ADMIN">
{{ $t('project.share.right.admin') }}
<option :value="PERMISSIONS.ADMIN">
{{ $t('project.share.permission.admin') }}
</option>
</select>
</div>
@@ -129,23 +129,23 @@
</p>
<p class="mbe-2">
<template v-if="s.right === RIGHTS.ADMIN">
<template v-if="s.permission === PERMISSIONS.ADMIN">
<span class="icon is-small">
<Icon icon="lock" />
</span>&nbsp;
{{ $t('project.share.right.admin') }}
{{ $t('project.share.permission.admin') }}
</template>
<template v-else-if="s.right === RIGHTS.READ_WRITE">
<template v-else-if="s.permission === PERMISSIONS.READ_WRITE">
<span class="icon is-small">
<Icon icon="pen" />
</span>&nbsp;
{{ $t('project.share.right.readWrite') }}
{{ $t('project.share.permission.readWrite') }}
</template>
<template v-else>
<span class="icon is-small">
<Icon icon="users" />
</span>&nbsp;
{{ $t('project.share.right.read') }}
{{ $t('project.share.permission.read') }}
</template>
</p>
@@ -221,7 +221,7 @@
import {ref, watch, computed, shallowReactive} from 'vue'
import {useI18n} from 'vue-i18n'
import {RIGHTS} from '@/constants/rights'
import {PERMISSIONS} from '@/constants/permissions'
import LinkShareModel from '@/models/linkShare'
import type {ILinkShare} from '@/modelTypes/ILinkShare'
@@ -246,7 +246,7 @@ const {t} = useI18n({useScope: 'global'})
const linkShares = ref<ILinkShare[]>([])
const linkShareService = shallowReactive(new LinkShareService())
const selectedRight = ref(RIGHTS.READ)
const selectedPermission = ref(PERMISSIONS.READ)
const name = ref('')
const password = ref('')
const showDeleteModal = ref(false)
@@ -296,13 +296,13 @@ watch(() => ([linkShares.value, availableViews.value]), ([newLinkShares, newProj
async function add(projectId: IProject['id']) {
const newLinkShare = new LinkShareModel({
right: selectedRight.value,
permission: selectedPermission.value,
projectId,
name: name.value,
password: password.value,
})
await linkShareService.create(newLinkShare)
selectedRight.value = RIGHTS.READ
selectedPermission.value = PERMISSIONS.READ
name.value = ''
password.value = ''
showNewForm.value = false

View File

@@ -71,23 +71,23 @@
</td>
</template>
<td class="type">
<template v-if="s.right === RIGHTS.ADMIN">
<template v-if="s.permission === PERMISSIONS.ADMIN">
<span class="icon is-small">
<Icon icon="lock" />
</span>
{{ $t('project.share.right.admin') }}
{{ $t('project.share.permission.admin') }}
</template>
<template v-else-if="s.right === RIGHTS.READ_WRITE">
<template v-else-if="s.permission === PERMISSIONS.READ_WRITE">
<span class="icon is-small">
<Icon icon="pen" />
</span>
{{ $t('project.share.right.readWrite') }}
{{ $t('project.share.permission.readWrite') }}
</template>
<template v-else>
<span class="icon is-small">
<Icon icon="users" />
</span>
{{ $t('project.share.right.read') }}
{{ $t('project.share.permission.read') }}
</template>
</td>
<td
@@ -96,27 +96,27 @@
>
<div class="select">
<select
v-model="selectedRight[s.id]"
v-model="selectedPermission[s.id]"
class="mie-2"
@change="toggleType(s)"
>
<option
:selected="s.right === RIGHTS.READ"
:value="RIGHTS.READ"
:selected="s.permission === PERMISSIONS.READ"
:value="PERMISSIONS.READ"
>
{{ $t('project.share.right.read') }}
{{ $t('project.share.permission.read') }}
</option>
<option
:selected="s.right === RIGHTS.READ_WRITE"
:value="RIGHTS.READ_WRITE"
:selected="s.permission === PERMISSIONS.READ_WRITE"
:value="PERMISSIONS.READ_WRITE"
>
{{ $t('project.share.right.readWrite') }}
{{ $t('project.share.permission.readWrite') }}
</option>
<option
:selected="s.right === RIGHTS.ADMIN"
:value="RIGHTS.ADMIN"
:selected="s.permission === PERMISSIONS.ADMIN"
:value="PERMISSIONS.ADMIN"
>
{{ $t('project.share.right.admin') }}
{{ $t('project.share.permission.admin') }}
</option>
</select>
</div>
@@ -178,7 +178,7 @@ import TeamModel from '@/models/team'
import type {ITeam} from '@/modelTypes/ITeam'
import {RIGHTS} from '@/constants/rights'
import {PERMISSIONS} from '@/constants/permissions'
import Multiselect from '@/components/input/Multiselect.vue'
import Nothing from '@/components/misc/Nothing.vue'
import {success} from '@/message'
@@ -209,7 +209,7 @@ let searchService: UserService | TeamService
let sharable: Ref<IUser | ITeam>
const searchLabel = ref('')
const selectedRight = ref({})
const selectedPermission = ref({})
// This holds either teams or users who this namepace or project is shared with
@@ -275,8 +275,8 @@ load()
async function load() {
sharables.value = await stuffService.getAll(stuffModel)
sharables.value.forEach(({id, right}) =>
selectedRight.value[id] = right,
sharables.value.forEach(({id, permission}) =>
selectedPermission.value[id] = permission,
)
}
@@ -309,9 +309,9 @@ async function add(admin) {
if (admin === null) {
admin = false
}
stuffModel.right = RIGHTS.READ
stuffModel.permission = PERMISSIONS.READ
if (admin) {
stuffModel.right = RIGHTS.ADMIN
stuffModel.permission = PERMISSIONS.ADMIN
}
if (props.shareType === 'user') {
@@ -327,13 +327,13 @@ async function add(admin) {
async function toggleType(sharable) {
if (
selectedRight.value[sharable.id] !== RIGHTS.ADMIN &&
selectedRight.value[sharable.id] !== RIGHTS.READ &&
selectedRight.value[sharable.id] !== RIGHTS.READ_WRITE
selectedPermission.value[sharable.id] !== PERMISSIONS.ADMIN &&
selectedPermission.value[sharable.id] !== PERMISSIONS.READ &&
selectedPermission.value[sharable.id] !== PERMISSIONS.READ_WRITE
) {
selectedRight.value[sharable.id] = RIGHTS.READ
selectedPermission.value[sharable.id] = PERMISSIONS.READ
}
stuffModel.right = selectedRight.value[sharable.id]
stuffModel.permission = selectedPermission.value[sharable.id]
if (props.shareType === 'user') {
stuffModel.username = sharable.username
@@ -350,7 +350,7 @@ async function toggleType(sharable) {
(sharables.value[i].id === stuffModel.teamId &&
props.shareType === 'team')
) {
sharables.value[i].right = r.right
sharables.value[i].permission = r.permission
}
}
success({message: t('project.share.userTeam.updatedSuccess', {type: shareTypeName.value})})

View File

@@ -0,0 +1,7 @@
export const PERMISSIONS = {
'READ': 0,
'READ_WRITE': 1,
'ADMIN': 2,
} as const
export type Permission = typeof PERMISSIONS[keyof typeof PERMISSIONS]

View File

@@ -1,7 +0,0 @@
export const RIGHTS = {
'READ': 0,
'READ_WRITE': 1,
'ADMIN': 2,
} as const
export type Right = typeof RIGHTS[keyof typeof RIGHTS]

View File

@@ -1,5 +1,5 @@
import type {Right} from '@/constants/rights'
import type {Permission} from '@/constants/permissions'
export interface IAbstract {
maxRight: Right | null // FIXME: should this be readonly?
maxPermission: Permission | null // FIXME: should this be readonly?
}

View File

@@ -1,11 +1,11 @@
import type {IAbstract} from './IAbstract'
import type {IUser} from './IUser'
import type { Right } from '@/constants/rights'
import type { Permission } from '@/constants/permissions'
export interface ILinkShare extends IAbstract {
id: number
hash: string
right: Right
permission: Permission
sharedBy: IUser
sharingType: number // FIXME: use correct numbers
projectId: number

View File

@@ -1,14 +1,14 @@
import type {IAbstract} from './IAbstract'
import type {IUser} from './IUser'
import type {ITeamMember} from './ITeamMember'
import type {Right} from '@/constants/rights'
import type {Permission} from '@/constants/permissions'
export interface ITeam extends IAbstract {
id: number
name: string
description: string
members: ITeamMember[]
right: Right
permission: Permission
externalId: string
isPublic: boolean

View File

@@ -1,10 +1,10 @@
import type {IAbstract} from './IAbstract'
import type {ITeam} from './ITeam'
import type {Right} from '@/constants/rights'
import type {Permission} from '@/constants/permissions'
export interface ITeamShareBase extends IAbstract {
teamId: ITeam['id']
right: Right
permission: Permission
created: Date
updated: Date

View File

@@ -1,10 +1,10 @@
import type {IAbstract} from './IAbstract'
import type {IUser} from './IUser'
import type {Right} from '@/constants/rights'
import type {Permission} from '@/constants/permissions'
export interface IUserShareBase extends IAbstract {
username: IUser['username']
right: Right
permission: Permission
created: Date
updated: Date

View File

@@ -1,15 +1,15 @@
import {objectToCamelCase} from '@/helpers/case'
import {omitBy, isNil} from '@/helpers/utils'
import type {Right} from '@/constants/rights'
import type {Permission} from '@/constants/permissions'
import type {IAbstract} from '@/modelTypes/IAbstract'
export default abstract class AbstractModel<Model extends IAbstract = IAbstract> implements IAbstract {
/**
* The max right the user has on this object, as returned by the x-max-right header from the api.
* The max permission the user has on this object, as returned by the x-max-permission header from the api.
*/
maxRight: Right | null = null
maxPermission: Permission | null = null
/**
* Takes an object and merges its data with the default data of this model.

View File

@@ -1,14 +1,14 @@
import AbstractModel from './abstractModel'
import UserModel from './user'
import {RIGHTS, type Right} from '@/constants/rights'
import {PERMISSIONS, type Permission} from '@/constants/permissions'
import type {ILinkShare} from '@/modelTypes/ILinkShare'
import type {IUser} from '@/modelTypes/IUser'
export default class LinkShareModel extends AbstractModel<ILinkShare> implements ILinkShare {
id = 0
hash = ''
right: Right = RIGHTS.READ
permission: Permission = PERMISSIONS.READ
sharedBy: IUser = UserModel
sharingType = 0 // FIXME: use correct numbers
projectId = 0

View File

@@ -2,7 +2,7 @@ import AbstractModel from './abstractModel'
import UserModel from './user'
import TeamMemberModel from './teamMember'
import {RIGHTS, type Right} from '@/constants/rights'
import {PERMISSIONS, type Permission} from '@/constants/permissions'
import type {ITeam} from '@/modelTypes/ITeam'
import type {ITeamMember} from '@/modelTypes/ITeamMember'
import type {IUser} from '@/modelTypes/IUser'
@@ -12,7 +12,7 @@ export default class TeamModel extends AbstractModel<ITeam> implements ITeam {
name = ''
description = ''
members: ITeamMember[] = []
right: Right = RIGHTS.READ
permission: Permission = PERMISSIONS.READ
externalId = ''
isPublic: boolean = false

View File

@@ -1,6 +1,6 @@
import AbstractModel from './abstractModel'
import {RIGHTS, type Right} from '@/constants/rights'
import {PERMISSIONS, type Permission} from '@/constants/permissions'
import type {ITeamShareBase} from '@/modelTypes/ITeamShareBase'
import type {ITeam} from '@/modelTypes/ITeam'
@@ -10,7 +10,7 @@ import type {ITeam} from '@/modelTypes/ITeam'
*/
export default class TeamShareBaseModel extends AbstractModel<ITeamShareBase> implements ITeamShareBase {
teamId: ITeam['id'] = 0
right: Right = RIGHTS.READ
permission: Permission = PERMISSIONS.READ
created: Date = null
updated: Date = null

View File

@@ -3,7 +3,7 @@ import UserShareBaseModel from './userShareBase'
import type {IUserProject} from '@/modelTypes/IUserProject'
import type {IProject} from '@/modelTypes/IProject'
// This class extends the user share model with a 'rights' parameter which is used in sharing
// This class extends the user share model with a 'permissions' parameter which is used in sharing
export default class UserProjectModel extends UserShareBaseModel implements IUserProject {
projectId: IProject['id'] = 0

View File

@@ -1,12 +1,12 @@
import AbstractModel from './abstractModel'
import {RIGHTS, type Right} from '@/constants/rights'
import {PERMISSIONS, type Permission} from '@/constants/permissions'
import type {IUserShareBase} from '@/modelTypes/IUserShareBase'
import type {IUser} from '@/modelTypes/IUser'
export default class UserShareBaseModel extends AbstractModel<IUserShareBase> implements IUserShareBase {
username: IUser['username'] = ''
right: Right = RIGHTS.READ
permission: Permission = PERMISSIONS.READ
created: Date = null
updated: Date = null

View File

@@ -4,7 +4,7 @@ import type {Method} from 'axios'
import {objectToSnakeCase} from '@/helpers/case'
import AbstractModel from '@/models/abstractModel'
import type {IAbstract} from '@/modelTypes/IAbstract'
import type {Right} from '@/constants/rights'
import type {Permission} from '@/constants/permissions'
interface Paths {
create : string
@@ -310,7 +310,7 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
try {
const response = await this.http.get(finalUrl, {params: prepareParams(params)})
const result = this.modelGetFactory(response.data)
result.maxRight = Number(response.headers['x-max-right']) as Right
result.maxPermission = Number(response.headers['x-max-permission']) as Permission
return result
} finally {
cancel()
@@ -386,8 +386,8 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
try {
const response = await this.http.put(finalUrl, model)
const result = this.modelCreateFactory(response.data)
if (typeof model.maxRight !== 'undefined') {
result.maxRight = model.maxRight
if (typeof model.maxPermission !== 'undefined') {
result.maxPermission = model.maxPermission
}
return result
} finally {
@@ -405,8 +405,8 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
try {
const response = await this.http.post(url, model)
const result = this.modelUpdateFactory(response.data)
if (typeof model.maxRight !== 'undefined') {
result.maxRight = model.maxRight
if (typeof model.maxPermission !== 'undefined') {
result.maxPermission = model.maxPermission
}
return result
} finally {

View File

@@ -12,7 +12,7 @@ import {useMenuActive} from '@/composables/useMenuActive'
import {useAuthStore} from '@/stores/auth'
import type {IProject} from '@/modelTypes/IProject'
import type {Right} from '@/constants/rights'
import type {Permission} from '@/constants/permissions'
import type {IProjectView} from '@/modelTypes/IProjectView'
export const useBaseStore = defineStore('base', () => {
@@ -40,19 +40,19 @@ export const useBaseStore = defineStore('base', () => {
const updateAvailable = ref(false)
function setCurrentProject(newCurrentProject: IProject | null, currentViewId?: IProjectView['id']) {
// Server updates don't return the right. Therefore, the right is reset after updating the project which is
// confusing because all the buttons will disappear in that case. To prevent this, we're keeping the right
// Server updates don't return the permission. Therefore, the permission is reset after updating the project which is
// confusing because all the buttons will disappear in that case. To prevent this, we're keeping the permission
// when updating the project in global state.
let maxRight: Right | null = newCurrentProject?.maxRight || null
let maxPermission: Permission | null = newCurrentProject?.maxPermission || null
if (
typeof currentProject.value?.maxRight !== 'undefined' &&
typeof currentProject.value?.maxPermission !== 'undefined' &&
newCurrentProject !== null &&
(
typeof newCurrentProject.maxRight === 'undefined' ||
newCurrentProject.maxRight === null
typeof newCurrentProject.maxPermission === 'undefined' ||
newCurrentProject.maxPermission === null
)
) {
maxRight = currentProject.value.maxRight
maxPermission = currentProject.value.maxPermission
}
if (newCurrentProject === null) {
currentProject.value = null
@@ -60,7 +60,7 @@ export const useBaseStore = defineStore('base', () => {
}
currentProject.value = {
...newCurrentProject,
maxRight,
maxPermission,
}
setCurrentProjectViewId(currentViewId)
}

View File

@@ -17,7 +17,7 @@ import {success} from '@/message'
import {useBaseStore} from '@/stores/base'
import {getSavedFilterIdFromProjectId} from '@/services/savedFilter'
import type {IProjectView} from '@/modelTypes/IProjectView'
import {RIGHTS} from '@/constants/rights.ts'
import {PERMISSIONS} from '@/constants/permissions.ts'
const {add, remove, search, update} = createNewIndexer('projects', ['title', 'description'])
@@ -213,7 +213,7 @@ export const useProjectStore = defineStore('project', () => {
let page = 1
try {
do {
const newProjects = await projectService.getAll({}, {is_archived: true, expand: 'rights'}, page) as IProject[]
const newProjects = await projectService.getAll({}, {is_archived: true, expand: 'permissions'}, page) as IProject[]
loadedProjects.push(...newProjects)
page++
} while (page <= projectService.totalPages)
@@ -320,7 +320,7 @@ export function useProject(projectId: MaybeRefOrGetter<IProject['id']>) {
const duplicate = await projectDuplicateService.create(projectDuplicate)
if (duplicate.duplicatedProject) {
duplicate.duplicatedProject.maxRight = RIGHTS.ADMIN
duplicate.duplicatedProject.maxPermission = PERMISSIONS.ADMIN
}
projectStore.setProject(duplicate.duplicatedProject)

View File

@@ -3,7 +3,7 @@
:title="$t('project.edit.header')"
primary-icon=""
:primary-label="$t('misc.save')"
:tertiary="project.maxRight === RIGHTS.ADMIN ? $t('misc.delete') : undefined"
:tertiary="project.maxPermission === PERMISSIONS.ADMIN ? $t('misc.delete') : undefined"
@primary="save"
@tertiary="$router.push({ name: 'project.settings.delete', params: { id: projectId } })"
>
@@ -100,7 +100,7 @@ import {useProjectStore} from '@/stores/projects'
import {useProject} from '@/stores/projects'
import {useTitle} from '@/composables/useTitle'
import {RIGHTS} from '@/constants/rights'
import {PERMISSIONS} from '@/constants/permissions'
const props = defineProps<{
projectId: IProject['id'],

View File

@@ -34,7 +34,7 @@ import {useTitle} from '@vueuse/core'
import ProjectService from '@/services/project'
import ProjectModel from '@/models/project'
import type {IProject} from '@/modelTypes/IProject'
import {RIGHTS} from '@/constants/rights'
import {PERMISSIONS} from '@/constants/permissions'
import CreateEdit from '@/components/misc/CreateEdit.vue'
import LinkSharing from '@/components/sharing/LinkSharing.vue'
@@ -57,7 +57,7 @@ useTitle(title)
const configStore = useConfigStore()
const linkSharingEnabled = computed(() => configStore.linkSharingEnabled)
const userIsAdmin = computed(() => project?.value?.maxRight === RIGHTS.ADMIN)
const userIsAdmin = computed(() => project?.value?.maxPermission === PERMISSIONS.ADMIN)
async function loadProject(projectId: number) {
const projectService = new ProjectService()

View File

@@ -10,7 +10,7 @@ import XButton from '@/components/input/Button.vue'
import {error, success} from '@/message'
import {useI18n} from 'vue-i18n'
import ProjectService from '@/services/project'
import {RIGHTS} from '@/constants/rights'
import {PERMISSIONS} from '@/constants/permissions'
import ProjectModel from '@/models/project'
import Message from '@/components/misc/Message.vue'
import draggable from 'zhyswan-vuedraggable'
@@ -49,7 +49,7 @@ watch(
async () => {
const projectService = new ProjectService()
const project = await projectService.get(new ProjectModel({id: props.projectId}))
isAdmin.value = project.maxRight === RIGHTS.ADMIN
isAdmin.value = project.maxPermission === PERMISSIONS.ADMIN
},
{immediate: true},
)

View File

@@ -603,7 +603,7 @@ import type {ITask} from '@/modelTypes/ITask'
import type {IProject} from '@/modelTypes/IProject'
import {PRIORITIES, type Priority} from '@/constants/priorities'
import {RIGHTS} from '@/constants/rights'
import {PERMISSIONS} from '@/constants/permissions'
import BaseButton from '@/components/base/BaseButton.vue'
@@ -703,8 +703,8 @@ const visible = ref(false)
const project = computed(() => projectStore.projects[task.value.projectId])
const canWrite = computed(() => (
task.value.maxRight !== null &&
task.value.maxRight > RIGHTS.READ
task.value.maxPermission !== null &&
task.value.maxPermission > PERMISSIONS.READ
))
const color = computed(() => {

View File

@@ -272,7 +272,7 @@ import TeamService from '@/services/team'
import TeamMemberService from '@/services/teamMember'
import UserService from '@/services/user'
import {RIGHTS as Rights} from '@/constants/rights'
import {PERMISSIONS as Permissions} from '@/constants/permissions'
import {useTitle} from '@/composables/useTitle'
import {success} from '@/message'
@@ -292,8 +292,8 @@ const {t} = useI18n({useScope: 'global'})
const userIsAdmin = computed(() => {
return (
team.value &&
team.value.maxRight &&
team.value.maxRight > Rights.READ
team.value.maxPermission &&
team.value.maxPermission > Permissions.READ
)
})
const userInfo = computed(() => authStore.info)

View File

@@ -1403,7 +1403,7 @@ func (Plugins) Build(pathToSourceFiles string) error {
if pathToSourceFiles == "" {
return fmt.Errorf("please provide a plugin path")
}
// Convert relative path to absolute path
if !strings.HasPrefix(pathToSourceFiles, "/") {
absPath, err := filepath.Abs(pathToSourceFiles)
@@ -1412,7 +1412,7 @@ func (Plugins) Build(pathToSourceFiles string) error {
}
pathToSourceFiles = absPath
}
out := filepath.Join(RootPath, "plugins", filepath.Base(pathToSourceFiles)+".so")
runAndStreamOutput("go", "build", "-buildmode=plugin", "-o", out, pathToSourceFiles)
return nil

View File

@@ -1,7 +1,7 @@
- id: 1
hash: test
project_id: 1
right: 0
permission: 0
sharing_type: 1
shared_by_id: 1
created: 2018-12-01 15:13:12
@@ -9,7 +9,7 @@
- id: 2
hash: test2
project_id: 2
right: 1
permission: 1
sharing_type: 1
shared_by_id: 1
created: 2018-12-01 15:13:12
@@ -17,7 +17,7 @@
- id: 3
hash: test3
project_id: 3
right: 2
permission: 2
sharing_type: 1
shared_by_id: 1
created: 2018-12-01 15:13:12
@@ -26,7 +26,7 @@
hash: testWithPassword
name: testWithPassword
project_id: 1
right: 0
permission: 0
password: '$2a$04$X4aRMEt0ytgPwMIgv36cI..7X9.nhY/.tYwxpqSi0ykRHx2CwQ0S6' # 12345678
sharing_type: 2
shared_by_id: 1

View File

@@ -1,96 +1,96 @@
- id: 1
team_id: 1
project_id: 3
right: 0
permission: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
# This team has read only access on project 6
- id: 2
team_id: 2
project_id: 6
right: 0
permission: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
# This team has write access on project 7
- id: 3
team_id: 3
project_id: 7
right: 1
permission: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
# This team has admin access on project 8
- id: 4
team_id: 4
project_id: 8
right: 2
permission: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
# Readonly acces on project 19
- id: 5
team_id: 8
project_id: 19
right: 2
permission: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
# Write acces on project 19
- id: 6
team_id: 9
project_id: 19
right: 1
permission: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
# Admin acces on project 19
- id: 7
team_id: 10
project_id: 19
right: 2
permission: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 8
team_id: 1
project_id: 21
right: 0
permission: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 9
team_id: 1
project_id: 28
right: 0
permission: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 10
team_id: 11
project_id: 29
right: 0
permission: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 11
team_id: 12
project_id: 29
right: 1
permission: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 12
team_id: 13
project_id: 29
right: 2
permission: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 13
team_id: 1
project_id: 32
right: 0
permission: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 14
team_id: 1
project_id: 33
right: 1
permission: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 15
team_id: 1
project_id: 34
right: 2
permission: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12

View File

@@ -1,114 +1,114 @@
- id: 1
user_id: 1
project_id: 3
right: 0
permission: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 2
user_id: 2
project_id: 3
right: 0
permission: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 3
user_id: 1
project_id: 9
right: 0
permission: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 4
user_id: 1
project_id: 10
right: 1
permission: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 5
user_id: 1
project_id: 11
right: 2
permission: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 6
user_id: 4
project_id: 19
right: 0
permission: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 7
user_id: 5
project_id: 19
right: 1
permission: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 8
user_id: 6
project_id: 19
right: 2
permission: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 9
user_id: 1
project_id: 27
right: 0
permission: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 10
user_id: 11
project_id: 29
right: 0
permission: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 11
user_id: 12
project_id: 29
right: 1
permission: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 12
user_id: 13
project_id: 29
right: 2
permission: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 13
user_id: 1
project_id: 30
right: 1
permission: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 14
user_id: 1
project_id: 31
right: 2
permission: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 15
user_id: 1
project_id: 28
right: 1
permission: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 16
user_id: 1
project_id: 29
right: 2
permission: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 17
user_id: 15
project_id: 26
right: 0
permission: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 18
user_id: 15
project_id: 36
right: 0
permission: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 19
user_id: 15
project_id: 38
right: 0
permission: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12

View File

@@ -45,7 +45,7 @@ type Opts struct {
// ContentType represents mail content types
type ContentType int
// Enumerate all the team rights
// Enumerate all the team permissions
const (
ContentTypePlain ContentType = iota
ContentTypeHTML

View File

@@ -26,7 +26,7 @@ type linkSharing20190818210133 struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
Hash string `xorm:"varchar(40) not null unique" json:"hash" param:"hash"`
ListID int64 `xorm:"int(11) not null" json:"list_id"`
Right models.Right `xorm:"int(11) INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
Right models.Permission `xorm:"int(11) INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
SharingType models.SharingType `xorm:"int(11) INDEX not null default 0" json:"sharing_type" valid:"length(0|2)" maximum:"2" default:"0"`
SharedByID int64 `xorm:"int(11) INDEX not null"`
Created int64 `xorm:"created not null" json:"created"`

View File

@@ -0,0 +1,44 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-present Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package migration
import (
"src.techknowlogick.com/xormigrate"
"xorm.io/xorm"
)
func init() {
migrations = append(migrations, &xormigrate.Migration{
ID: "20250813093602",
Description: "rename 'right' column to 'permission' in link_shares, users_projects, and team_projects tables",
Migrate: func(tx *xorm.Engine) error {
tables := []string{"link_shares", "users_projects", "team_projects"}
for _, table := range tables {
err := renameColumn(tx, table, "right", "permission")
if err != nil {
return err
}
}
return nil
},
Rollback: func(tx *xorm.Engine) error {
return nil
},
})
}

View File

@@ -186,6 +186,82 @@ func renameTable(x *xorm.Engine, oldName, newName string) error {
return nil
}
// Checks if a column exists in a table
func columnExists(x *xorm.Engine, tableName, columnName string) (bool, error) {
switch config.DatabaseType.GetString() {
case "sqlite":
results, err := x.Query("PRAGMA table_info(" + tableName + ")")
if err != nil {
return false, err
}
for _, row := range results {
if name, ok := row["name"]; ok && string(name) == columnName {
return true, nil
}
}
return false, nil
case "mysql":
results, err := x.Query("SHOW COLUMNS FROM `" + tableName + "` LIKE '" + columnName + "'")
if err != nil {
return false, err
}
return len(results) > 0, nil
case "postgres":
results, err := x.Query("SELECT column_name FROM information_schema.columns WHERE table_name = '" + tableName + "' AND column_name = '" + columnName + "'")
if err != nil {
return false, err
}
return len(results) > 0, nil
default:
log.Fatal("Unknown db.")
return false, nil
}
}
func renameColumn(x *xorm.Engine, tableName, oldColumn, newColumn string) error {
// Check if old column exists
exists, err := columnExists(x, tableName, oldColumn)
if err != nil {
return err
}
if !exists {
log.Debugf("Column %s in table %s does not exist, skipping rename", oldColumn, tableName)
return nil
}
// Check if new column already exists
newExists, err := columnExists(x, tableName, newColumn)
if err != nil {
return err
}
if newExists {
log.Debugf("Column %s in table %s already exists, skipping rename", newColumn, tableName)
return nil
}
switch config.DatabaseType.GetString() {
case "sqlite":
_, err := x.Exec("ALTER TABLE \"" + tableName + "\" RENAME COLUMN \"" + oldColumn + "\" TO \"" + newColumn + "\"")
if err != nil {
return err
}
case "mysql":
_, err := x.Exec("ALTER TABLE `" + tableName + "` CHANGE `" + oldColumn + "` `" + newColumn + "` BIGINT NOT NULL DEFAULT 0")
if err != nil {
return err
}
case "postgres":
_, err := x.Exec("ALTER TABLE \"" + tableName + "\" RENAME COLUMN \"" + oldColumn + "\" TO \"" + newColumn + "\"")
if err != nil {
return err
}
default:
log.Fatal("Unknown db.")
}
return nil
}
func initSchema(tx *xorm.Engine) error {
schemeBeans := []interface{}{}
schemeBeans = append(schemeBeans, models.GetTables()...)

View File

@@ -244,9 +244,9 @@ func CanDoAPIRoute(c echo.Context, token *APIToken) (can bool) {
routeGroupName = "other"
}
group, hasGroup := token.Permissions[routeGroupName]
group, hasGroup := token.APIPermissions[routeGroupName]
if !hasGroup {
group, hasGroup = token.Permissions[routeParts[0]]
group, hasGroup = token.APIPermissions[routeParts[0]]
if !hasGroup {
return false
}
@@ -276,7 +276,7 @@ func CanDoAPIRoute(c echo.Context, token *APIToken) (can bool) {
}
}
log.Debugf("[auth] Token %d tried to use route %s which requires permission %s but has only %v", token.ID, path, route, token.Permissions)
log.Debugf("[auth] Token %d tried to use route %s which requires permission %s but has only %v", token.ID, path, route, token.APIPermissions)
return false
}

View File

@@ -22,13 +22,12 @@ import (
"encoding/hex"
"time"
"xorm.io/builder"
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/utils"
"code.vikunja.io/api/pkg/web"
"golang.org/x/crypto/pbkdf2"
"xorm.io/builder"
"xorm.io/xorm"
)
@@ -46,7 +45,7 @@ type APIToken struct {
TokenHash string `xorm:"not null unique" json:"-"`
TokenLastEight string `xorm:"not null index varchar(8)" json:"-"`
// The permissions this token has. Possible values are available via the /routes endpoint and consist of the keys of the list from that endpoint. For example, if the token should be able to read all tasks as well as update existing tasks, you should add `{"tasks":["read_all","update"]}`.
Permissions APIPermissions `xorm:"json not null" json:"permissions" valid:"required"`
APIPermissions APIPermissions `xorm:"json not null permissions" json:"permissions" valid:"required"`
// The date when this key expires.
ExpiresAt time.Time `xorm:"not null" json:"expires_at" valid:"required"`
@@ -55,8 +54,8 @@ type APIToken struct {
OwnerID int64 `xorm:"bigint not null" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
}
const APITokenPrefix = `tk_`
@@ -102,7 +101,7 @@ func (t *APIToken) Create(s *xorm.Session, a web.Auth) (err error) {
t.OwnerID = a.GetID()
if err := PermissionsAreValid(t.Permissions); err != nil {
if err := PermissionsAreValid(t.APIPermissions); err != nil {
return err
}

View File

@@ -564,31 +564,31 @@ func (err ErrBulkTasksNeedAtLeastOne) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeBulkTasksNeedAtLeastOne, Message: "Need at least one tasks to do bulk editing."}
}
// ErrNoRightToSeeTask represents an error where a user does not have the right to see a task
type ErrNoRightToSeeTask struct {
// ErrNoPermissionToSeeTask represents an error where a user does not have the permission to see a task
type ErrNoPermissionToSeeTask struct {
TaskID int64
UserID int64
}
// IsErrNoRightToSeeTask checks if an error is ErrNoRightToSeeTask.
func IsErrNoRightToSeeTask(err error) bool {
_, ok := err.(ErrNoRightToSeeTask)
// IsErrNoPermissionToSeeTask checks if an error is ErrNoPermissionToSeeTask.
func IsErrNoPermissionToSeeTask(err error) bool {
_, ok := err.(ErrNoPermissionToSeeTask)
return ok
}
func (err ErrNoRightToSeeTask) Error() string {
return fmt.Sprintf("User does not have the right to see the task [TaskID: %v, ID: %v]", err.TaskID, err.UserID)
func (err ErrNoPermissionToSeeTask) Error() string {
return fmt.Sprintf("User does not have the permission to see the task [TaskID: %v, ID: %v]", err.TaskID, err.UserID)
}
// ErrCodeNoRightToSeeTask holds the unique world-error code of this error
const ErrCodeNoRightToSeeTask = 4005
// HTTPError holds the http error description
func (err ErrNoRightToSeeTask) HTTPError() web.HTTPError {
func (err ErrNoPermissionToSeeTask) HTTPError() web.HTTPError {
return web.HTTPError{
HTTPCode: http.StatusForbidden,
Code: ErrCodeNoRightToSeeTask,
Message: "You don't have the right to see this task.",
Message: "You don't have the permission to see this task.",
}
}
@@ -1497,7 +1497,7 @@ func (err ErrLabelDoesNotExist) HTTPError() web.HTTPError {
}
}
// ErrUserHasNoAccessToLabel represents an error where a user does not have the right to see a label
// ErrUserHasNoAccessToLabel represents an error where a user does not have the permission to see a label
type ErrUserHasNoAccessToLabel struct {
LabelID int64
UserID int64
@@ -1526,33 +1526,33 @@ func (err ErrUserHasNoAccessToLabel) HTTPError() web.HTTPError {
}
// ========
// Rights
// Permissions
// ========
// ErrInvalidRight represents an error where a right is invalid
type ErrInvalidRight struct {
Right Right
// ErrInvalidPermission represents an error where a permission is invalid
type ErrInvalidPermission struct {
Permission Permission
}
// IsErrInvalidRight checks if an error is ErrInvalidRight.
func IsErrInvalidRight(err error) bool {
_, ok := err.(ErrInvalidRight)
// IsErrInvalidPermission checks if an error is ErrInvalidPermission.
func IsErrInvalidPermission(err error) bool {
_, ok := err.(ErrInvalidPermission)
return ok
}
func (err ErrInvalidRight) Error() string {
return fmt.Sprintf("Right invalid [Right: %d]", err.Right)
func (err ErrInvalidPermission) Error() string {
return fmt.Sprintf("Permission invalid [Permission: %d]", err.Permission)
}
// ErrCodeInvalidRight holds the unique world-error code of this error
const ErrCodeInvalidRight = 9001
// HTTPError holds the http error description
func (err ErrInvalidRight) HTTPError() web.HTTPError {
func (err ErrInvalidPermission) HTTPError() web.HTTPError {
return web.HTTPError{
HTTPCode: http.StatusBadRequest,
Code: ErrCodeInvalidRight,
Message: "The right is invalid.",
Message: "The permission is invalid.",
}
}

View File

@@ -61,8 +61,8 @@ type Bucket struct {
// Including the task collection type so we can use task filters on kanban
TaskCollection `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
}
// TableName returns the table name for this bucket.

View File

@@ -42,7 +42,7 @@ func (b *Bucket) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
return b.canDoBucket(s, a)
}
// canDoBucket checks if the bucket exists and if the user has the right to act on it
// canDoBucket checks if the bucket exists and if the user has the permission to act on it
func (b *Bucket) canDoBucket(s *xorm.Session, a web.Auth) (bool, error) {
bb, err := getBucketByID(s, b.ID)
if err != nil {

View File

@@ -41,8 +41,8 @@ type TaskBucket struct {
ProjectID int64 `xorm:"-" json:"-" param:"project"`
Task *Task `xorm:"-" json:"task"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
}
func (b *TaskBucket) TableName() string {

View File

@@ -120,9 +120,9 @@ func TestBucket_ReadAll(t *testing.T) {
defer s.Close()
linkShare := &LinkSharing{
ID: 1,
ProjectID: 1,
Right: RightRead,
ID: 1,
ProjectID: 1,
Permission: PermissionRead,
}
b := &TaskCollection{
ProjectID: 1,

View File

@@ -46,8 +46,8 @@ type Label struct {
// A timestamp when this label was last updated. You cannot change this value.
Updated time.Time `xorm:"updated not null" json:"updated"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
// TableName makes a pretty table name

View File

@@ -61,7 +61,7 @@ func (l *Label) isLabelOwner(s *xorm.Session, a web.Auth) (bool, error) {
}
// Helper method to check if a user can see a specific label
func (l *Label) hasAccessToLabel(s *xorm.Session, a web.Auth) (has bool, maxRight int, err error) {
func (l *Label) hasAccessToLabel(s *xorm.Session, a web.Auth) (has bool, maxPermission int, err error) {
linkShare, isLinkShare := a.(*LinkSharing)
@@ -93,10 +93,10 @@ func (l *Label) hasAccessToLabel(s *xorm.Session, a web.Auth) (has bool, maxRigh
return
}
// Since the right depends on the task the label is associated with, we need to check that too.
// Since the permission depends on the task the label is associated with, we need to check that too.
if ll.TaskID > 0 {
t := &Task{ID: ll.TaskID}
_, maxRight, err = t.CanRead(s, a)
_, maxPermission, err = t.CanRead(s, a)
if err != nil {
return
}

View File

@@ -40,8 +40,8 @@ type LabelTask struct {
// A timestamp when this task was created. You cannot change this value.
Created time.Time `xorm:"created not null" json:"created"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
// TableName makes a pretty table name
@@ -127,14 +127,14 @@ func (lt *LabelTask) Create(s *xorm.Session, auth web.Auth) (err error) {
// @Failure 500 {object} models.Message "Internal error"
// @Router /tasks/{task}/labels [get]
func (lt *LabelTask) ReadAll(s *xorm.Session, a web.Auth, search string, page int, _ int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
// Check if the user has the right to see the task
// Check if the user has the permission to see the task
task := Task{ID: lt.TaskID}
canRead, _, err := task.CanRead(s, a)
if err != nil {
return nil, 0, 0, err
}
if !canRead {
return nil, 0, 0, ErrNoRightToSeeTask{lt.TaskID, a.GetID()}
return nil, 0, 0, ErrNoPermissionToSeeTask{lt.TaskID, a.GetID()}
}
return GetLabelsByTaskIDs(s, &LabelByTaskIDsOptions{
@@ -377,7 +377,7 @@ func (t *Task) UpdateTaskLabels(s *xorm.Session, creator web.Auth, labels []*Lab
return err
}
// Check if the user has the rights to see the label he is about to add
// Check if the user has the permissions to see the label he is about to add
hasAccessToLabel, _, err := label.hasAccessToLabel(s, creator)
if err != nil {
return err
@@ -413,8 +413,8 @@ type LabelTaskBulk struct {
Labels []*Label `json:"labels"`
TaskID int64 `json:"-" param:"projecttask"`
web.CRUDable `json:"-"`
web.Rights `json:"-"`
web.CRUDable `json:"-"`
web.Permissions `json:"-"`
}
// Create updates a bunch of labels on a task at once

View File

@@ -51,12 +51,12 @@ func TestLabelTask_ReadAll(t *testing.T) {
}
type fields struct {
ID int64
TaskID int64
LabelID int64
Created time.Time
CRUDable web.CRUDable
Rights web.Rights
ID int64
TaskID int64
LabelID int64
Created time.Time
CRUDable web.CRUDable
Permissions web.Permissions
}
type args struct {
search string
@@ -87,7 +87,7 @@ func TestLabelTask_ReadAll(t *testing.T) {
},
},
{
name: "no right to see the task",
name: "no permission to see the task",
fields: fields{
TaskID: 14,
},
@@ -95,7 +95,7 @@ func TestLabelTask_ReadAll(t *testing.T) {
a: &user.User{ID: 1},
},
wantErr: true,
errType: IsErrNoRightToSeeTask,
errType: IsErrNoPermissionToSeeTask,
},
{
name: "nonexistant task",
@@ -131,12 +131,12 @@ func TestLabelTask_ReadAll(t *testing.T) {
s := db.NewSession()
l := &LabelTask{
ID: tt.fields.ID,
TaskID: tt.fields.TaskID,
LabelID: tt.fields.LabelID,
Created: tt.fields.Created,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
ID: tt.fields.ID,
TaskID: tt.fields.TaskID,
LabelID: tt.fields.LabelID,
Created: tt.fields.Created,
CRUDable: tt.fields.CRUDable,
Permissions: tt.fields.Permissions,
}
gotLabels, _, _, err := l.ReadAll(s, tt.args.a, tt.args.search, tt.args.page, 0)
if (err != nil) != tt.wantErr {
@@ -157,12 +157,12 @@ func TestLabelTask_ReadAll(t *testing.T) {
func TestLabelTask_Create(t *testing.T) {
type fields struct {
ID int64
TaskID int64
LabelID int64
Created time.Time
CRUDable web.CRUDable
Rights web.Rights
ID int64
TaskID int64
LabelID int64
Created time.Time
CRUDable web.CRUDable
Permissions web.Permissions
}
type args struct {
a web.Auth
@@ -229,12 +229,12 @@ func TestLabelTask_Create(t *testing.T) {
s := db.NewSession()
l := &LabelTask{
ID: tt.fields.ID,
TaskID: tt.fields.TaskID,
LabelID: tt.fields.LabelID,
Created: tt.fields.Created,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
ID: tt.fields.ID,
TaskID: tt.fields.TaskID,
LabelID: tt.fields.LabelID,
Created: tt.fields.Created,
CRUDable: tt.fields.CRUDable,
Permissions: tt.fields.Permissions,
}
allowed, err := l.CanCreate(s, tt.args.a)
if !allowed && !tt.wantForbidden {
@@ -261,12 +261,12 @@ func TestLabelTask_Create(t *testing.T) {
func TestLabelTask_Delete(t *testing.T) {
type fields struct {
ID int64
TaskID int64
LabelID int64
Created time.Time
CRUDable web.CRUDable
Rights web.Rights
ID int64
TaskID int64
LabelID int64
Created time.Time
CRUDable web.CRUDable
Permissions web.Permissions
}
tests := []struct {
name string
@@ -329,12 +329,12 @@ func TestLabelTask_Delete(t *testing.T) {
defer s.Close()
l := &LabelTask{
ID: tt.fields.ID,
TaskID: tt.fields.TaskID,
LabelID: tt.fields.LabelID,
Created: tt.fields.Created,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
ID: tt.fields.ID,
TaskID: tt.fields.TaskID,
LabelID: tt.fields.LabelID,
Created: tt.fields.Created,
CRUDable: tt.fields.CRUDable,
Permissions: tt.fields.Permissions,
}
allowed, _ := l.CanDelete(s, tt.auth)
if !allowed && !tt.wantForbidden {

View File

@@ -40,7 +40,7 @@ func TestLabel_ReadAll(t *testing.T) {
Created time.Time
Updated time.Time
CRUDable web.CRUDable
Rights web.Rights
Permissions web.Permissions
}
type args struct {
search string
@@ -146,7 +146,7 @@ func TestLabel_ReadAll(t *testing.T) {
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
Permissions: tt.fields.Permissions,
}
db.LoadAndAssertFixtures(t)
s := db.NewSession()
@@ -177,7 +177,7 @@ func TestLabel_ReadOne(t *testing.T) {
Created time.Time
Updated time.Time
CRUDable web.CRUDable
Rights web.Rights
Permissions web.Permissions
}
user1 := &user.User{
ID: 1,
@@ -226,7 +226,7 @@ func TestLabel_ReadOne(t *testing.T) {
auth: &user.User{ID: 1},
},
{
name: "no rights",
name: "no permissions",
fields: fields{
ID: 3,
},
@@ -272,7 +272,7 @@ func TestLabel_ReadOne(t *testing.T) {
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
Permissions: tt.fields.Permissions,
}
s := db.NewSession()
@@ -308,7 +308,7 @@ func TestLabel_Create(t *testing.T) {
Created time.Time
Updated time.Time
CRUDable web.CRUDable
Rights web.Rights
Permissions web.Permissions
}
type args struct {
a web.Auth
@@ -344,7 +344,7 @@ func TestLabel_Create(t *testing.T) {
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
Permissions: tt.fields.Permissions,
}
s := db.NewSession()
allowed, _ := l.CanCreate(s, tt.args.a)
@@ -378,7 +378,7 @@ func TestLabel_Update(t *testing.T) {
Created time.Time
Updated time.Time
CRUDable web.CRUDable
Rights web.Rights
Permissions web.Permissions
}
tests := []struct {
name string
@@ -406,7 +406,7 @@ func TestLabel_Update(t *testing.T) {
wantErr: true,
},
{
name: "no rights",
name: "no permissions",
fields: fields{
ID: 3,
Title: "new and better",
@@ -415,7 +415,7 @@ func TestLabel_Update(t *testing.T) {
wantForbidden: true,
},
{
name: "no rights other creator but access",
name: "no permissions other creator but access",
fields: fields{
ID: 4,
Title: "new and better",
@@ -436,7 +436,7 @@ func TestLabel_Update(t *testing.T) {
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
Permissions: tt.fields.Permissions,
}
s := db.NewSession()
allowed, _ := l.CanUpdate(s, tt.auth)
@@ -468,7 +468,7 @@ func TestLabel_Delete(t *testing.T) {
Created time.Time
Updated time.Time
CRUDable web.CRUDable
Rights web.Rights
Permissions web.Permissions
}
tests := []struct {
name string
@@ -494,7 +494,7 @@ func TestLabel_Delete(t *testing.T) {
wantForbidden: true, // When the label does not exist, it is forbidden. We should fix this, but for everything.
},
{
name: "no rights",
name: "no permissions",
fields: fields{
ID: 3,
},
@@ -502,7 +502,7 @@ func TestLabel_Delete(t *testing.T) {
wantForbidden: true,
},
{
name: "no rights but visible",
name: "no permissions but visible",
fields: fields{
ID: 4,
},
@@ -522,7 +522,7 @@ func TestLabel_Delete(t *testing.T) {
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
Permissions: tt.fields.Permissions,
}
s := db.NewSession()
allowed, _ := l.CanDelete(s, tt.auth)

View File

@@ -52,8 +52,8 @@ type LinkSharing struct {
Name string `xorm:"text null" json:"name"`
// The ID of the shared project
ProjectID int64 `xorm:"bigint not null" json:"-" param:"project"`
// The right this project is shared with. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
Right Right `xorm:"bigint INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
// The permission this project is shared with. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
Permission Permission `xorm:"bigint INDEX not null default 0" json:"permission" valid:"length(0|2)" maximum:"2" default:"0"`
// The kind of this link. 0 = undefined, 1 = without password, 2 = with password.
SharingType SharingType `xorm:"bigint INDEX not null default 0" json:"sharing_type" valid:"length(0|2)" maximum:"2" default:"0"`
@@ -70,8 +70,8 @@ type LinkSharing struct {
// A timestamp when this share was last updated. You cannot change this value.
Updated time.Time `xorm:"updated not null" json:"updated"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
// TableName holds the table name
@@ -95,7 +95,7 @@ func GetLinkShareFromClaims(claims jwt.MapClaims) (share *LinkSharing, err error
share.ID = int64(claims["id"].(float64))
share.Hash = claims["hash"].(string)
share.ProjectID = int64(projectID)
share.Right = Right(claims["right"].(float64))
share.Permission = Permission(claims["permission"].(float64))
share.SharedByID = int64(claims["sharedByID"].(float64))
return
}
@@ -138,7 +138,7 @@ func (share *LinkSharing) toUser() *user.User {
// @Router /projects/{project}/shares [put]
func (share *LinkSharing) Create(s *xorm.Session, a web.Auth) (err error) {
err = share.Right.isValid()
err = share.Permission.isValid()
if err != nil {
return
}

View File

@@ -21,7 +21,7 @@ import (
"xorm.io/xorm"
)
// CanRead implements the read right check for a link share
// CanRead implements the read permission check for a link share
func (share *LinkSharing) CanRead(s *xorm.Session, a web.Auth) (bool, int, error) {
// Don't allow creating link shares if the user itself authenticated with a link share
if _, is := a.(*LinkSharing); is {
@@ -35,17 +35,17 @@ func (share *LinkSharing) CanRead(s *xorm.Session, a web.Auth) (bool, int, error
return l.CanRead(s, a)
}
// CanDelete implements the delete right check for a link share
// CanDelete implements the delete permission check for a link share
func (share *LinkSharing) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
return share.canDoLinkShare(s, a)
}
// CanUpdate implements the update right check for a link share
// CanUpdate implements the update permission check for a link share
func (share *LinkSharing) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
return share.canDoLinkShare(s, a)
}
// CanCreate implements the create right check for a link share
// CanCreate implements the create permission check for a link share
func (share *LinkSharing) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
return share.canDoLinkShare(s, a)
}
@@ -61,8 +61,8 @@ func (share *LinkSharing) canDoLinkShare(s *xorm.Session, a web.Auth) (bool, err
return false, err
}
// Check if the user is admin when the link right is admin
if share.Right == RightAdmin {
// Check if the user is admin when the link permission is admin
if share.Permission == PermissionAdmin {
return l.IsAdmin(s, a)
}

View File

@@ -36,8 +36,8 @@ func TestLinkSharing_Create(t *testing.T) {
defer s.Close()
share := &LinkSharing{
ProjectID: 1,
Right: RightRead,
ProjectID: 1,
Permission: PermissionRead,
}
err := share.Create(s, doer)
@@ -49,19 +49,19 @@ func TestLinkSharing_Create(t *testing.T) {
"id": share.ID,
}, false)
})
t.Run("invalid right", func(t *testing.T) {
t.Run("invalid permission", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := db.NewSession()
defer s.Close()
share := &LinkSharing{
ProjectID: 1,
Right: Right(123),
ProjectID: 1,
Permission: Permission(123),
}
err := share.Create(s, doer)
require.Error(t, err)
assert.True(t, IsErrInvalidRight(err))
assert.True(t, IsErrInvalidPermission(err))
})
t.Run("password should be hashed", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
@@ -69,9 +69,9 @@ func TestLinkSharing_Create(t *testing.T) {
defer s.Close()
share := &LinkSharing{
ProjectID: 1,
Right: RightRead,
Password: "somePassword",
ProjectID: 1,
Permission: PermissionRead,
Password: "somePassword",
}
err := share.Create(s, doer)

View File

@@ -30,8 +30,8 @@ type DatabaseNotifications struct {
// True is read, false is unread.
Read bool `xorm:"-" json:"read"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
// ReadAll returns all database notifications for a user

View File

@@ -22,38 +22,38 @@ import (
"strconv"
)
// Right defines the rights users/teams can have for projects
type Right int
// Permission defines the permissions users/teams can have for projects
type Permission int
// define unknown right
// define unknown permission
const (
RightUnknown = -1
PermissionUnknown = -1
// Can read projects in a
RightRead Right = iota - 1
PermissionRead Permission = iota - 1
// Can write in a like projects and tasks. Cannot create new projects.
RightWrite
PermissionWrite
// Can manage a project, can do everything
RightAdmin
PermissionAdmin
)
func (r Right) isValid() error {
if r != RightAdmin && r != RightRead && r != RightWrite {
return ErrInvalidRight{r}
func (r Permission) isValid() error {
if r != PermissionAdmin && r != PermissionRead && r != PermissionWrite {
return ErrInvalidPermission{r}
}
return nil
}
// MarshalJSON marshals the enum as a quoted json string
func (r Right) MarshalJSON() ([]byte, error) {
if r == RightUnknown {
func (r Permission) MarshalJSON() ([]byte, error) {
if r == PermissionUnknown {
return []byte(`null`), nil
}
return []byte(strconv.Itoa(int(r))), nil
}
// UnmarshalJSON unmarshals a quoted json string to the enum value
func (r *Right) UnmarshalJSON(data []byte) error {
func (r *Permission) UnmarshalJSON(data []byte) error {
var s int
if err := json.Unmarshal(data, &s); err != nil {
return err
@@ -61,15 +61,15 @@ func (r *Right) UnmarshalJSON(data []byte) error {
switch s {
case -1:
*r = RightUnknown
*r = PermissionUnknown
case 0:
*r = RightRead
*r = PermissionRead
case 1:
*r = RightWrite
*r = PermissionWrite
case 2:
*r = RightAdmin
*r = PermissionAdmin
default:
return fmt.Errorf("invalid Right %q", s)
return fmt.Errorf("invalid Permission %q", s)
}
return nil
}

View File

@@ -77,21 +77,21 @@ type Project struct {
Views []*ProjectView `xorm:"-" json:"views"`
Expand ProjectExpandable `xorm:"-" json:"-" query:"expand"`
MaxRight Right `xorm:"-" json:"max_right"`
Expand ProjectExpandable `xorm:"-" json:"-" query:"expand"`
MaxPermission Permission `xorm:"-" json:"max_permission"`
// A timestamp when this project was created. You cannot change this value.
Created time.Time `xorm:"created not null" json:"created"`
// A timestamp when this project was last updated. You cannot change this value.
Updated time.Time `xorm:"updated not null" json:"updated"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
type ProjectExpandable string
const ProjectExpandableRights = `rights`
const ProjectExpandableRights = `permissions`
type ProjectWithTasksAndBuckets struct {
Project
@@ -168,7 +168,7 @@ var FavoritesPseudoProject = Project{
// @Param per_page query int false "The maximum number of items per page. Note this parameter is limited by the configured maximum of items per page."
// @Param s query string false "Search projects by title."
// @Param is_archived query bool false "If true, also returns all archived projects."
// @Param expand query string false "If set to `rights`, Vikunja will return the max right the current user has on this project. You can currently only set this to `rights`."
// @Param expand query string false "If set to `permissions`, Vikunja will return the max permission the current user has on this project. You can currently only set this to `permissions`."
// @Security JWTKeyAuth
// @Success 200 {array} models.Project "The projects"
// @Failure 403 {object} web.HTTPError "The user does not have access to the project"
@@ -200,13 +200,13 @@ func (p *Project) ReadAll(s *xorm.Session, a web.Auth, search string, page int,
if err != nil {
return
}
err = addMaxRightToProjects(s, prs, doer)
err = addMaxPermissionToProjects(s, prs, doer)
if err != nil {
return
}
} else {
for _, pr := range prs {
pr.MaxRight = RightUnknown
pr.MaxPermission = PermissionUnknown
}
}
@@ -765,25 +765,25 @@ func addProjectDetails(s *xorm.Session, projects []*Project, a web.Auth) (err er
return
}
func addMaxRightToProjects(s *xorm.Session, projects []*Project, u *user.User) (err error) {
func addMaxPermissionToProjects(s *xorm.Session, projects []*Project, u *user.User) (err error) {
projectIDs := make([]int64, 0, len(projects))
for _, project := range projects {
if GetSavedFilterIDFromProjectID(project.ID) > 0 {
project.MaxRight = RightAdmin
project.MaxPermission = PermissionAdmin
continue
}
projectIDs = append(projectIDs, project.ID)
}
rights, err := checkRightsForProjects(s, u, projectIDs)
permissions, err := checkPermissionsForProjects(s, u, projectIDs)
if err != nil {
return err
}
for _, project := range projects {
right, has := rights[project.ID]
permission, has := permissions[project.ID]
if has {
project.MaxRight = right.MaxRight
project.MaxPermission = permission.MaxPermission
}
}

View File

@@ -35,11 +35,11 @@ type ProjectDuplicate struct {
// The copied project
Project *Project `json:"duplicated_project,omitempty"`
web.Rights `json:"-"`
web.CRUDable `json:"-"`
web.Permissions `json:"-"`
web.CRUDable `json:"-"`
}
// CanCreate checks if a user has the right to duplicate a project
// CanCreate checks if a user has the permission to duplicate a project
func (pd *ProjectDuplicate) CanCreate(s *xorm.Session, a web.Auth) (canCreate bool, err error) {
// Project Exists + user has read access to project
pd.Project = &Project{ID: pd.ProjectID}
@@ -59,7 +59,7 @@ func (pd *ProjectDuplicate) CanCreate(s *xorm.Session, a web.Auth) (canCreate bo
// Create duplicates a project
// @Summary Duplicate an existing project
// @Description Copies the project, tasks, files, kanban data, assignees, comments, attachments, labels, relations, backgrounds, user/team rights and link shares from one project to a new one. The user needs read access in the project and write access in the parent of the new project.
// @Description Copies the project, tasks, files, kanban data, assignees, comments, attachments, labels, relations, backgrounds, user/team permissions and link shares from one project to a new one. The user needs read access in the project and write access in the parent of the new project.
// @tags project
// @Accept json
// @Produce json
@@ -114,8 +114,8 @@ func (pd *ProjectDuplicate) Create(s *xorm.Session, doer web.Auth) (err error) {
return
}
// Rights / Shares
// To keep it simple(r) we will only copy rights which are directly used with the project, not the parent
// Permissions / Shares
// To keep it simple(r) we will only copy permissions which are directly used with the project, not the parent
users := []*ProjectUser{}
err = s.Where("project_id = ?", pd.ProjectID).Find(&users)
if err != nil {

View File

@@ -22,6 +22,7 @@ import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/api/pkg/utils"
"code.vikunja.io/api/pkg/web"
"xorm.io/xorm"
)
@@ -33,7 +34,7 @@ func (p *Project) CanWrite(s *xorm.Session, a web.Auth) (bool, error) {
return false, nil
}
// Get the project and check the right
// Get the project and check the permission
originalProject, err := GetProjectSimpleByID(s, p.ID)
if err != nil {
return false, err
@@ -49,7 +50,7 @@ func (p *Project) CanWrite(s *xorm.Session, a web.Auth) (bool, error) {
shareAuth, ok := a.(*LinkSharing)
if ok {
return originalProject.ID == shareAuth.ProjectID &&
(shareAuth.Right == RightWrite || shareAuth.Right == RightAdmin), errIsArchived
(shareAuth.Permission == PermissionWrite || shareAuth.Permission == PermissionAdmin), errIsArchived
}
u := &user.User{ID: a.GetID()}
@@ -63,7 +64,7 @@ func (p *Project) CanWrite(s *xorm.Session, a web.Auth) (bool, error) {
return canWrite, errIsArchived
}
canWrite, _, err = originalProject.checkRight(s, u, RightWrite, RightAdmin)
canWrite, _, err = originalProject.checkPermission(s, u, PermissionWrite, PermissionAdmin)
if err != nil {
return false, err
}
@@ -82,7 +83,7 @@ func (p *Project) CanRead(s *xorm.Session, a web.Auth) (bool, int, error) {
*p = FavoritesPseudoProject
p.Owner = owner
return true, int(RightRead), nil
return true, int(PermissionRead), nil
}
// Saved Filter Projects need a special case
@@ -104,10 +105,10 @@ func (p *Project) CanRead(s *xorm.Session, a web.Auth) (bool, int, error) {
shareAuth, ok := a.(*LinkSharing)
if ok {
return p.ID == shareAuth.ProjectID &&
(shareAuth.Right == RightRead || shareAuth.Right == RightWrite || shareAuth.Right == RightAdmin), int(shareAuth.Right), nil
(shareAuth.Permission == PermissionRead || shareAuth.Permission == PermissionWrite || shareAuth.Permission == PermissionAdmin), int(shareAuth.Permission), nil
}
return p.checkRight(s, &user.User{ID: a.GetID()}, RightRead, RightWrite, RightAdmin)
return p.checkPermission(s, &user.User{ID: a.GetID()}, PermissionRead, PermissionWrite, PermissionAdmin)
}
// CanUpdate checks if the user can update a project
@@ -175,7 +176,7 @@ func (p *Project) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
return true, nil
}
// IsAdmin returns whether the user has admin rights on the project or not
// IsAdmin returns whether the user has admin permissions on the project or not
func (p *Project) IsAdmin(s *xorm.Session, a web.Auth) (bool, error) {
// The favorite project can't be edited
if p.ID == FavoritesPseudoProject.ID {
@@ -190,7 +191,7 @@ func (p *Project) IsAdmin(s *xorm.Session, a web.Auth) (bool, error) {
// Check if we're dealing with a share auth
shareAuth, ok := a.(*LinkSharing)
if ok {
return originalProject.ID == shareAuth.ProjectID && shareAuth.Right == RightAdmin, nil
return originalProject.ID == shareAuth.ProjectID && shareAuth.Permission == PermissionAdmin, nil
}
u := &user.User{ID: a.GetID()}
@@ -201,7 +202,7 @@ func (p *Project) IsAdmin(s *xorm.Session, a web.Auth) (bool, error) {
if originalProject.isOwner(u) {
return true, nil
}
is, _, err := originalProject.checkRight(s, u, RightAdmin)
is, _, err := originalProject.checkPermission(s, u, PermissionAdmin)
return is, err
}
@@ -210,33 +211,33 @@ func (p *Project) isOwner(u *user.User) bool {
return p.OwnerID == u.ID
}
// Checks n different rights for any given user
func (p *Project) checkRight(s *xorm.Session, u *user.User, rights ...Right) (bool, int, error) {
projectRights, err := checkRightsForProjects(s, u, []int64{p.ID})
// Checks n different permissions for any given user
func (p *Project) checkPermission(s *xorm.Session, u *user.User, permissions ...Permission) (bool, int, error) {
projectPermissions, err := checkPermissionsForProjects(s, u, []int64{p.ID})
if err != nil {
return false, 0, err
}
right, has := projectRights[p.ID]
permission, has := projectPermissions[p.ID]
if !has {
return false, 0, nil
}
for _, r := range rights {
if r == right.MaxRight {
return true, int(right.MaxRight), nil
for _, r := range permissions {
if r == permission.MaxPermission {
return true, int(permission.MaxPermission), nil
}
}
return false, 0, nil
}
type projectRight struct {
ID int64 `xorm:"pk autoincr"`
MaxRight Right
type projectPermission struct {
ID int64 `xorm:"pk autoincr"`
MaxPermission Permission
}
func checkRightsForProjects(s *xorm.Session, u *user.User, projectIDs []int64) (projectRightMap map[int64]*projectRight, err error) {
projectRightMap = make(map[int64]*projectRight)
func checkPermissionsForProjects(s *xorm.Session, u *user.User, projectIDs []int64) (projectPermissionMap map[int64]*projectPermission, err error) {
projectPermissionMap = make(map[int64]*projectPermission)
if len(projectIDs) < 1 {
return
@@ -277,9 +278,9 @@ WITH RECURSIVE
ph.original_project_id,
CASE
WHEN p.owner_id = ? THEN 2
WHEN COALESCE(ul.right, 0) > COALESCE(tl.right, 0) THEN ul.right
ELSE COALESCE(tl.right, 0)
END AS project_right,
WHEN COALESCE(ul.permission, 0) > COALESCE(tl.permission, 0) THEN ul.permission
ELSE COALESCE(tl.permission, 0)
END AS project_permission,
CASE
WHEN p.owner_id = ? THEN 1 -- Direct project ownership
ELSE ph.level + 1 -- Derived from parent project
@@ -293,12 +294,12 @@ WITH RECURSIVE
WHERE p.owner_id = ? OR ul.user_id = ? OR tm.user_id = ?)
SELECT ph.original_project_id AS id,
COALESCE(MAX(pp.project_right), -1) AS max_right
COALESCE(MAX(pp.project_permission), -1) AS max_permission
FROM project_hierarchy ph
LEFT JOIN (SELECT *,
ROW_NUMBER() OVER (PARTITION BY original_project_id ORDER BY priority) AS rn
FROM project_permissions) pp ON ph.id = pp.id AND pp.rn = 1
GROUP BY ph.original_project_id`, args...).
Find(&projectRightMap)
Find(&projectPermissionMap)
return
}

View File

@@ -34,16 +34,16 @@ type TeamProject struct {
TeamID int64 `xorm:"bigint not null INDEX" json:"team_id" param:"team"`
// The project id.
ProjectID int64 `xorm:"bigint not null INDEX" json:"-" param:"project"`
// The right this team has. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
Right Right `xorm:"bigint INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
// The permission this team has. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
Permission Permission `xorm:"bigint INDEX not null default 0" json:"permission" valid:"length(0|2)" maximum:"2" default:"0"`
// A timestamp when this relation was created. You cannot change this value.
Created time.Time `xorm:"created not null" json:"created"`
// A timestamp when this relation was last updated. You cannot change this value.
Updated time.Time `xorm:"updated not null" json:"updated"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
// TableName makes beautiful table names
@@ -51,10 +51,10 @@ func (*TeamProject) TableName() string {
return "team_projects"
}
// TeamWithRight represents a team, combined with rights.
type TeamWithRight struct {
Team `xorm:"extends"`
Right Right `json:"right"`
// TeamWithPermission represents a team, combined with permissions.
type TeamWithPermission struct {
Team `xorm:"extends"`
Permission Permission `json:"permission"`
}
// Create creates a new team <-> project relation
@@ -74,8 +74,8 @@ type TeamWithRight struct {
// @Router /projects/{id}/teams [put]
func (tl *TeamProject) Create(s *xorm.Session, a web.Auth) (err error) {
// Check if the rights are valid
if err = tl.Right.isValid(); err != nil {
// Check if the permissions are valid
if err = tl.Permission.isValid(); err != nil {
return
}
@@ -177,8 +177,8 @@ func (tl *TeamProject) Delete(s *xorm.Session, _ web.Auth) (err error) {
// @Param per_page query int false "The maximum number of items per page. Note this parameter is limited by the configured maximum of items per page."
// @Param s query string false "Search teams by its name."
// @Security JWTKeyAuth
// @Success 200 {array} models.TeamWithRight "The teams with their right."
// @Failure 403 {object} web.HTTPError "No right to see the project."
// @Success 200 {array} models.TeamWithPermission "The teams with their permission."
// @Failure 403 {object} web.HTTPError "No permission to see the project."
// @Failure 500 {object} models.Message "Internal error"
// @Router /projects/{id}/teams [get]
func (tl *TeamProject) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) {
@@ -195,7 +195,7 @@ func (tl *TeamProject) ReadAll(s *xorm.Session, a web.Auth, search string, page
limit, start := getLimitFromPageIndex(page, perPage)
// Get the teams
all := []*TeamWithRight{}
all := []*TeamWithPermission{}
query := s.
Table("teams").
Join("INNER", "team_projects", "team_id = teams.id").
@@ -224,7 +224,7 @@ func (tl *TeamProject) ReadAll(s *xorm.Session, a web.Auth, search string, page
Join("INNER", "team_projects", "team_id = teams.id").
Where("team_projects.project_id = ?", tl.ProjectID).
Where("teams.name LIKE ?", "%"+search+"%").
Count(&TeamWithRight{})
Count(&TeamWithPermission{})
if err != nil {
return nil, 0, 0, err
}
@@ -234,7 +234,7 @@ func (tl *TeamProject) ReadAll(s *xorm.Session, a web.Auth, search string, page
// Update updates a team <-> project relation
// @Summary Update a team <-> project relation
// @Description Update a team <-> project relation. Mostly used to update the right that team has.
// @Description Update a team <-> project relation. Mostly used to update the permission that team has.
// @tags sharing
// @Accept json
// @Produce json
@@ -249,14 +249,14 @@ func (tl *TeamProject) ReadAll(s *xorm.Session, a web.Auth, search string, page
// @Router /projects/{projectID}/teams/{teamID} [post]
func (tl *TeamProject) Update(s *xorm.Session, _ web.Auth) (err error) {
// Check if the right is valid
if err := tl.Right.isValid(); err != nil {
// Check if the permission is valid
if err := tl.Permission.isValid(); err != nil {
return err
}
_, err = s.
Where("project_id = ? AND team_id = ?", tl.ProjectID, tl.TeamID).
Cols("right").
Cols("permission").
Update(tl)
if err != nil {
return err

View File

@@ -60,9 +60,9 @@ func TestTeamProject_ReadAll(t *testing.T) {
})
t.Run("no access", func(t *testing.T) {
tl := TeamProject{
TeamID: 1,
ProjectID: 5,
Right: RightAdmin,
TeamID: 1,
ProjectID: 5,
Permission: PermissionAdmin,
}
db.LoadAndAssertFixtures(t)
s := db.NewSession()
@@ -80,7 +80,7 @@ func TestTeamProject_ReadAll(t *testing.T) {
teams, _, _, err := tl.ReadAll(s, u, "TEAM9", 1, 50)
require.NoError(t, err)
assert.Equal(t, reflect.Slice, reflect.TypeOf(teams).Kind())
ts := teams.([]*TeamWithRight)
ts := teams.([]*TeamWithPermission)
assert.Len(t, ts, 1)
assert.Equal(t, int64(9), ts[0].ID)
_ = s.Close()
@@ -93,9 +93,9 @@ func TestTeamProject_Create(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := db.NewSession()
tl := TeamProject{
TeamID: 1,
ProjectID: 1,
Right: RightAdmin,
TeamID: 1,
ProjectID: 1,
Permission: PermissionAdmin,
}
allowed, _ := tl.CanCreate(s, u)
assert.True(t, allowed)
@@ -106,33 +106,33 @@ func TestTeamProject_Create(t *testing.T) {
db.AssertExists(t, "team_projects", map[string]interface{}{
"team_id": 1,
"project_id": 1,
"right": RightAdmin,
"permission": PermissionAdmin,
}, false)
})
t.Run("team already has access", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := db.NewSession()
tl := TeamProject{
TeamID: 1,
ProjectID: 3,
Right: RightAdmin,
TeamID: 1,
ProjectID: 3,
Permission: PermissionAdmin,
}
err := tl.Create(s, u)
require.Error(t, err)
assert.True(t, IsErrTeamAlreadyHasAccess(err))
_ = s.Close()
})
t.Run("wrong rights", func(t *testing.T) {
t.Run("wrong permissions", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := db.NewSession()
tl := TeamProject{
TeamID: 1,
ProjectID: 1,
Right: RightUnknown,
TeamID: 1,
ProjectID: 1,
Permission: PermissionUnknown,
}
err := tl.Create(s, u)
require.Error(t, err)
assert.True(t, IsErrInvalidRight(err))
assert.True(t, IsErrInvalidPermission(err))
_ = s.Close()
})
t.Run("nonexistant team", func(t *testing.T) {
@@ -208,14 +208,14 @@ func TestTeamProject_Delete(t *testing.T) {
func TestTeamProject_Update(t *testing.T) {
type fields struct {
ID int64
TeamID int64
ProjectID int64
Right Right
Created time.Time
Updated time.Time
CRUDable web.CRUDable
Rights web.Rights
ID int64
TeamID int64
ProjectID int64
Permission Permission
Created time.Time
Updated time.Time
CRUDable web.CRUDable
Permissions web.Permissions
}
tests := []struct {
name string
@@ -226,36 +226,36 @@ func TestTeamProject_Update(t *testing.T) {
{
name: "Test Update Normally",
fields: fields{
ProjectID: 3,
TeamID: 1,
Right: RightAdmin,
ProjectID: 3,
TeamID: 1,
Permission: PermissionAdmin,
},
},
{
name: "Test Update to write",
fields: fields{
ProjectID: 3,
TeamID: 1,
Right: RightWrite,
ProjectID: 3,
TeamID: 1,
Permission: PermissionWrite,
},
},
{
name: "Test Update to Read",
fields: fields{
ProjectID: 3,
TeamID: 1,
Right: RightRead,
ProjectID: 3,
TeamID: 1,
Permission: PermissionRead,
},
},
{
name: "Test Update with invalid right",
name: "Test Update with invalid permission",
fields: fields{
ProjectID: 3,
TeamID: 1,
Right: 500,
ProjectID: 3,
TeamID: 1,
Permission: 500,
},
wantErr: true,
errType: IsErrInvalidRight,
errType: IsErrInvalidPermission,
},
}
for _, tt := range tests {
@@ -264,14 +264,14 @@ func TestTeamProject_Update(t *testing.T) {
s := db.NewSession()
tl := &TeamProject{
ID: tt.fields.ID,
TeamID: tt.fields.TeamID,
ProjectID: tt.fields.ProjectID,
Right: tt.fields.Right,
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
ID: tt.fields.ID,
TeamID: tt.fields.TeamID,
ProjectID: tt.fields.ProjectID,
Permission: tt.fields.Permission,
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CRUDable: tt.fields.CRUDable,
Permissions: tt.fields.Permissions,
}
err := tl.Update(s, &user.User{ID: 1})
if (err != nil) != tt.wantErr {
@@ -286,7 +286,7 @@ func TestTeamProject_Update(t *testing.T) {
db.AssertExists(t, "team_projects", map[string]interface{}{
"project_id": tt.fields.ProjectID,
"team_id": tt.fields.TeamID,
"right": tt.fields.Right,
"permission": tt.fields.Permission,
}, false)
}
})

View File

@@ -37,16 +37,16 @@ type ProjectUser struct {
UserID int64 `xorm:"bigint not null INDEX" json:"-"`
// The project id.
ProjectID int64 `xorm:"bigint not null INDEX" json:"-" param:"project"`
// The right this user has. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
Right Right `xorm:"bigint INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
// The permission this user has. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
Permission Permission `xorm:"bigint INDEX not null default 0" json:"permission" valid:"length(0|2)" maximum:"2" default:"0"`
// A timestamp when this relation was created. You cannot change this value.
Created time.Time `xorm:"created not null" json:"created"`
// A timestamp when this relation was last updated. You cannot change this value.
Updated time.Time `xorm:"updated not null" json:"updated"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
// TableName is the table name for ProjectUser
@@ -54,10 +54,10 @@ func (*ProjectUser) TableName() string {
return "users_projects"
}
// UserWithRight represents a user in combination with the right it can have on a project
type UserWithRight struct {
user.User `xorm:"extends"`
Right Right `json:"right"`
// UserWithPermission represents a user in combination with the permission it can have on a project
type UserWithPermission struct {
user.User `xorm:"extends"`
Permission Permission `json:"permission"`
}
// Create creates a new project <-> user relation
@@ -77,8 +77,8 @@ type UserWithRight struct {
// @Router /projects/{id}/users [put]
func (lu *ProjectUser) Create(s *xorm.Session, a web.Auth) (err error) {
// Check if the right is valid
if err := lu.Right.isValid(); err != nil {
// Check if the permission is valid
if err := lu.Permission.isValid(); err != nil {
return err
}
@@ -183,8 +183,8 @@ func (lu *ProjectUser) Delete(s *xorm.Session, _ web.Auth) (err error) {
// @Param per_page query int false "The maximum number of items per page. Note this parameter is limited by the configured maximum of items per page."
// @Param s query string false "Search users by its name."
// @Security JWTKeyAuth
// @Success 200 {array} models.UserWithRight "The users with the right they have."
// @Failure 403 {object} web.HTTPError "No right to see the project."
// @Success 200 {array} models.UserWithPermission "The users with the permission they have."
// @Failure 403 {object} web.HTTPError "No permission to see the project."
// @Failure 500 {object} models.Message "Internal error"
// @Router /projects/{id}/users [get]
func (lu *ProjectUser) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
@@ -201,7 +201,7 @@ func (lu *ProjectUser) ReadAll(s *xorm.Session, a web.Auth, search string, page
limit, start := getLimitFromPageIndex(page, perPage)
// Get all users
all := []*UserWithRight{}
all := []*UserWithPermission{}
query := s.
Join("INNER", "users_projects", "user_id = users.id").
Where("users_projects.project_id = ?", lu.ProjectID).
@@ -223,14 +223,14 @@ func (lu *ProjectUser) ReadAll(s *xorm.Session, a web.Auth, search string, page
Join("INNER", "users_projects", "user_id = users.id").
Where("users_projects.project_id = ?", lu.ProjectID).
Where("users.username LIKE ?", "%"+search+"%").
Count(&UserWithRight{})
Count(&UserWithPermission{})
return all, len(all), numberOfTotalItems, err
}
// Update updates a user <-> project relation
// @Summary Update a user <-> project relation
// @Description Update a user <-> project relation. Mostly used to update the right that user has.
// @Description Update a user <-> project relation. Mostly used to update the permission that user has.
// @tags sharing
// @Accept json
// @Produce json
@@ -245,8 +245,8 @@ func (lu *ProjectUser) ReadAll(s *xorm.Session, a web.Auth, search string, page
// @Router /projects/{projectID}/users/{userID} [post]
func (lu *ProjectUser) Update(s *xorm.Session, _ web.Auth) (err error) {
// Check if the right is valid
if err := lu.Right.isValid(); err != nil {
// Check if the permission is valid
if err := lu.Permission.isValid(); err != nil {
return err
}
@@ -259,7 +259,7 @@ func (lu *ProjectUser) Update(s *xorm.Session, _ web.Auth) (err error) {
_, err = s.
Where("project_id = ? AND user_id = ?", lu.ProjectID, lu.UserID).
Cols("right").
Cols("permission").
Update(lu)
if err != nil {
return err

View File

@@ -28,14 +28,14 @@ import (
func TestProjectUser_CanDoSomething(t *testing.T) {
type fields struct {
ID int64
UserID int64
ProjectID int64
Right Right
Created time.Time
Updated time.Time
CRUDable web.CRUDable
Rights web.Rights
ID int64
UserID int64
ProjectID int64
Permission Permission
Created time.Time
Updated time.Time
CRUDable web.CRUDable
Permissions web.Permissions
}
type args struct {
a web.Auth
@@ -67,7 +67,7 @@ func TestProjectUser_CanDoSomething(t *testing.T) {
want: map[string]bool{"CanCreate": false, "CanDelete": false, "CanUpdate": false},
},
{
name: "CanDoSomething where the user does not have the rights",
name: "CanDoSomething where the user does not have the permissions",
fields: fields{
ProjectID: 3,
},
@@ -83,14 +83,14 @@ func TestProjectUser_CanDoSomething(t *testing.T) {
s := db.NewSession()
lu := &ProjectUser{
ID: tt.fields.ID,
UserID: tt.fields.UserID,
ProjectID: tt.fields.ProjectID,
Right: tt.fields.Right,
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
ID: tt.fields.ID,
UserID: tt.fields.UserID,
ProjectID: tt.fields.ProjectID,
Permission: tt.fields.Permission,
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CRUDable: tt.fields.CRUDable,
Permissions: tt.fields.Permissions,
}
if got, _ := lu.CanCreate(s, tt.args.a); got != tt.want["CanCreate"] {
t.Errorf("ProjectUser.CanCreate() = %v, want %v", got, tt.want["CanCreate"])

View File

@@ -32,15 +32,15 @@ import (
func TestProjectUser_Create(t *testing.T) {
type fields struct {
ID int64
UserID int64
Username string
ProjectID int64
Right Right
Created time.Time
Updated time.Time
CRUDable web.CRUDable
Rights web.Rights
ID int64
UserID int64
Username string
ProjectID int64
Permission Permission
Created time.Time
Updated time.Time
CRUDable web.CRUDable
Permissions web.Permissions
}
type args struct {
a web.Auth
@@ -69,14 +69,14 @@ func TestProjectUser_Create(t *testing.T) {
errType: IsErrUserAlreadyHasAccess,
},
{
name: "ListUsers Create with invalid right",
name: "ListUsers Create with invalid permission",
fields: fields{
Username: "user1",
ProjectID: 2,
Right: 500,
Username: "user1",
ProjectID: 2,
Permission: 500,
},
wantErr: true,
errType: IsErrInvalidRight,
errType: IsErrInvalidPermission,
},
{
name: "ListUsers Create with inexisting project",
@@ -112,15 +112,15 @@ func TestProjectUser_Create(t *testing.T) {
s := db.NewSession()
ul := &ProjectUser{
ID: tt.fields.ID,
UserID: tt.fields.UserID,
Username: tt.fields.Username,
ProjectID: tt.fields.ProjectID,
Right: tt.fields.Right,
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
ID: tt.fields.ID,
UserID: tt.fields.UserID,
Username: tt.fields.Username,
ProjectID: tt.fields.ProjectID,
Permission: tt.fields.Permission,
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CRUDable: tt.fields.CRUDable,
Permissions: tt.fields.Permissions,
}
err := ul.Create(s, tt.args.a)
if (err != nil) != tt.wantErr {
@@ -144,7 +144,7 @@ func TestProjectUser_Create(t *testing.T) {
}
func TestProjectUser_ReadAll(t *testing.T) {
user1Read := &UserWithRight{
user1Read := &UserWithPermission{
User: user.User{
ID: 1,
Username: "user1",
@@ -157,9 +157,9 @@ func TestProjectUser_ReadAll(t *testing.T) {
Updated: testUpdatedTime,
ExportFileID: 1,
},
Right: RightRead,
Permission: PermissionRead,
}
user2Read := &UserWithRight{
user2Read := &UserWithPermission{
User: user.User{
ID: 2,
Username: "user2",
@@ -172,18 +172,18 @@ func TestProjectUser_ReadAll(t *testing.T) {
Created: testCreatedTime,
Updated: testUpdatedTime,
},
Right: RightRead,
Permission: PermissionRead,
}
type fields struct {
ID int64
UserID int64
ProjectID int64
Right Right
Created time.Time
Updated time.Time
CRUDable web.CRUDable
Rights web.Rights
ID int64
UserID int64
ProjectID int64
Permission Permission
Created time.Time
Updated time.Time
CRUDable web.CRUDable
Permissions web.Permissions
}
type args struct {
search string
@@ -206,7 +206,7 @@ func TestProjectUser_ReadAll(t *testing.T) {
args: args{
a: &user.User{ID: 3},
},
want: []*UserWithRight{
want: []*UserWithPermission{
user1Read,
user2Read,
},
@@ -231,7 +231,7 @@ func TestProjectUser_ReadAll(t *testing.T) {
a: &user.User{ID: 3},
search: "USER2",
},
want: []*UserWithRight{
want: []*UserWithPermission{
user2Read,
},
},
@@ -242,14 +242,14 @@ func TestProjectUser_ReadAll(t *testing.T) {
s := db.NewSession()
ul := &ProjectUser{
ID: tt.fields.ID,
UserID: tt.fields.UserID,
ProjectID: tt.fields.ProjectID,
Right: tt.fields.Right,
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
ID: tt.fields.ID,
UserID: tt.fields.UserID,
ProjectID: tt.fields.ProjectID,
Permission: tt.fields.Permission,
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CRUDable: tt.fields.CRUDable,
Permissions: tt.fields.Permissions,
}
got, _, _, err := ul.ReadAll(s, tt.args.a, tt.args.search, tt.args.page, 50)
if (err != nil) != tt.wantErr {
@@ -268,14 +268,14 @@ func TestProjectUser_ReadAll(t *testing.T) {
func TestProjectUser_Update(t *testing.T) {
type fields struct {
ID int64
Username string
ProjectID int64
Right Right
Created time.Time
Updated time.Time
CRUDable web.CRUDable
Rights web.Rights
ID int64
Username string
ProjectID int64
Permission Permission
Created time.Time
Updated time.Time
CRUDable web.CRUDable
Permissions web.Permissions
}
tests := []struct {
name string
@@ -286,36 +286,36 @@ func TestProjectUser_Update(t *testing.T) {
{
name: "Test Update Normally",
fields: fields{
ProjectID: 3,
Username: "user1",
Right: RightAdmin,
ProjectID: 3,
Username: "user1",
Permission: PermissionAdmin,
},
},
{
name: "Test Update to write",
fields: fields{
ProjectID: 3,
Username: "user1",
Right: RightWrite,
ProjectID: 3,
Username: "user1",
Permission: PermissionWrite,
},
},
{
name: "Test Update to Read",
fields: fields{
ProjectID: 3,
Username: "user1",
Right: RightRead,
ProjectID: 3,
Username: "user1",
Permission: PermissionRead,
},
},
{
name: "Test Update with invalid right",
name: "Test Update with invalid permission",
fields: fields{
ProjectID: 3,
Username: "user1",
Right: 500,
ProjectID: 3,
Username: "user1",
Permission: 500,
},
wantErr: true,
errType: IsErrInvalidRight,
errType: IsErrInvalidPermission,
},
}
for _, tt := range tests {
@@ -324,14 +324,14 @@ func TestProjectUser_Update(t *testing.T) {
s := db.NewSession()
lu := &ProjectUser{
ID: tt.fields.ID,
Username: tt.fields.Username,
ProjectID: tt.fields.ProjectID,
Right: tt.fields.Right,
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
ID: tt.fields.ID,
Username: tt.fields.Username,
ProjectID: tt.fields.ProjectID,
Permission: tt.fields.Permission,
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CRUDable: tt.fields.CRUDable,
Permissions: tt.fields.Permissions,
}
err := lu.Update(s, &user.User{ID: 1})
if (err != nil) != tt.wantErr {
@@ -348,7 +348,7 @@ func TestProjectUser_Update(t *testing.T) {
db.AssertExists(t, "users_projects", map[string]interface{}{
"project_id": tt.fields.ProjectID,
"user_id": lu.UserID,
"right": tt.fields.Right,
"permission": tt.fields.Permission,
}, false)
}
})
@@ -357,15 +357,15 @@ func TestProjectUser_Update(t *testing.T) {
func TestProjectUser_Delete(t *testing.T) {
type fields struct {
ID int64
Username string
UserID int64
ProjectID int64
Right Right
Created time.Time
Updated time.Time
CRUDable web.CRUDable
Rights web.Rights
ID int64
Username string
UserID int64
ProjectID int64
Permission Permission
Created time.Time
Updated time.Time
CRUDable web.CRUDable
Permissions web.Permissions
}
tests := []struct {
name string
@@ -406,14 +406,14 @@ func TestProjectUser_Delete(t *testing.T) {
s := db.NewSession()
lu := &ProjectUser{
ID: tt.fields.ID,
Username: tt.fields.Username,
ProjectID: tt.fields.ProjectID,
Right: tt.fields.Right,
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
ID: tt.fields.ID,
Username: tt.fields.Username,
ProjectID: tt.fields.ProjectID,
Permission: tt.fields.Permission,
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CRUDable: tt.fields.CRUDable,
Permissions: tt.fields.Permissions,
}
err := lu.Delete(s, &user.User{ID: 1})
if (err != nil) != tt.wantErr {

View File

@@ -157,8 +157,8 @@ type ProjectView struct {
// A timestamp when this reaction was created. You cannot change this value.
Created time.Time `xorm:"created not null" json:"created"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
func (pv *ProjectView) TableName() string {

View File

@@ -53,8 +53,8 @@ type Reaction struct {
// A timestamp when this reaction was created. You cannot change this value.
Created time.Time `xorm:"created not null" json:"created"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
func (*Reaction) TableName() string {

View File

@@ -53,8 +53,8 @@ type SavedFilter struct {
// A timestamp when this filter was last updated. You cannot change this value.
Updated time.Time `xorm:"updated not null" json:"updated"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
// TableName returns a better table name for saved filters
@@ -170,7 +170,7 @@ func GetSavedFilterSimpleByID(s *xorm.Session, id int64) (sf *SavedFilter, err e
// @Failure 500 {object} models.Message "Internal error"
// @Router /filters/{id} [get]
func (sf *SavedFilter) ReadOne(s *xorm.Session, _ web.Auth) error {
// s already contains almost the full saved filter from the rights check, we only need to add the user
// s already contains almost the full saved filter from the permissions check, we only need to add the user
u, err := user.GetUserByID(s, sf.OwnerID)
sf.Owner = u
return err

View File

@@ -21,25 +21,25 @@ import (
"xorm.io/xorm"
)
// CanRead checks if a user has the right to read a saved filter
// CanRead checks if a user has the permission to read a saved filter
func (sf *SavedFilter) CanRead(s *xorm.Session, auth web.Auth) (bool, int, error) {
can, err := sf.canDoFilter(s, auth)
return can, int(RightAdmin), err
return can, int(PermissionAdmin), err
}
// CanDelete checks if a user has the right to delete a saved filter
// CanDelete checks if a user has the permission to delete a saved filter
func (sf *SavedFilter) CanDelete(s *xorm.Session, auth web.Auth) (bool, error) {
return sf.canDoFilter(s, auth)
}
// CanUpdate checks if a user has the right to update a saved filter
// CanUpdate checks if a user has the permission to update a saved filter
func (sf *SavedFilter) CanUpdate(s *xorm.Session, auth web.Auth) (bool, error) {
// A normal check would replace the passed struct which in our case would override the values we want to update.
sff := &SavedFilter{ID: sf.ID}
return sff.canDoFilter(s, auth)
}
// CanCreate checks if a user has the right to update a saved filter
// CanCreate checks if a user has the permission to update a saved filter
func (sf *SavedFilter) CanCreate(_ *xorm.Session, auth web.Auth) (bool, error) {
if _, is := auth.(*LinkSharing); is {
return false, nil
@@ -48,7 +48,7 @@ func (sf *SavedFilter) CanCreate(_ *xorm.Session, auth web.Auth) (bool, error) {
return true, nil
}
// Helper function to check saved filter rights sind they all have the same logic
// Helper function to check saved filter permissions sind they all have the same logic
func (sf *SavedFilter) canDoFilter(s *xorm.Session, auth web.Auth) (can bool, err error) {
// Link shares can't view or modify saved filters, therefore we can error out right away
if _, is := auth.(*LinkSharing); is {

View File

@@ -191,7 +191,7 @@ func TestSavedFilter_Delete(t *testing.T) {
})
}
func TestSavedFilter_Rights(t *testing.T) {
func TestSavedFilter_Permissions(t *testing.T) {
user1 := &user.User{ID: 1}
user2 := &user.User{ID: 2}
ls := &LinkSharing{ID: 1}
@@ -216,9 +216,9 @@ func TestSavedFilter_Rights(t *testing.T) {
ID: 1,
Title: "Lorem",
}
can, maxRight, err := sf.CanRead(s, user1)
can, maxPermission, err := sf.CanRead(s, user1)
require.NoError(t, err)
assert.Equal(t, int(RightAdmin), maxRight)
assert.Equal(t, int(PermissionAdmin), maxPermission)
assert.True(t, can)
})
t.Run("not owner", func(t *testing.T) {

View File

@@ -109,8 +109,8 @@ type Subscription struct {
// A timestamp when this subscription was created. You cannot change this value.
Created time.Time `xorm:"created not null" json:"created"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
type SubscriptionWithUser struct {
@@ -145,7 +145,7 @@ func (sb *Subscription) TableName() string {
// @Failure 500 {object} models.Message "Internal error"
// @Router /subscriptions/{entity}/{entityID} [put]
func (sb *Subscription) Create(s *xorm.Session, auth web.Auth) (err error) {
// Rights method already does the validation of the entity type, so we don't need to do that here
// Permissions method already does the validation of the entity type, so we don't need to do that here
sb.ID = 0
sb.UserID = auth.GetID()

View File

@@ -136,7 +136,7 @@ func TestSubscription_Create(t *testing.T) {
assert.True(t, IsErrTaskDoesNotExist(err))
assert.False(t, can)
})
t.Run("no rights to see project", func(t *testing.T) {
t.Run("no permissions to see project", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := db.NewSession()
defer s.Close()
@@ -151,7 +151,7 @@ func TestSubscription_Create(t *testing.T) {
require.NoError(t, err)
assert.False(t, can)
})
t.Run("no rights to see task", func(t *testing.T) {
t.Run("no permissions to see task", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := db.NewSession()
defer s.Close()

View File

@@ -35,8 +35,8 @@ type TaskAssginee struct {
UserID int64 `xorm:"bigint INDEX not null" json:"user_id" param:"user"`
Created time.Time `xorm:"created not null"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
// TableName makes a pretty table name
@@ -355,8 +355,8 @@ type BulkAssignees struct {
Assignees []*user.User `json:"assignees"`
TaskID int64 `json:"-" param:"projecttask"`
web.CRUDable `json:"-"`
web.Rights `json:"-"`
web.CRUDable `json:"-"`
web.Permissions `json:"-"`
}
// Create adds new assignees to a task

View File

@@ -48,8 +48,8 @@ type TaskAttachment struct {
Created time.Time `xorm:"created" json:"created"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
// TableName returns the table name for task attachments

View File

@@ -197,7 +197,7 @@ func TestTaskAttachment_Delete(t *testing.T) {
})
}
func TestTaskAttachment_Rights(t *testing.T) {
func TestTaskAttachment_Permissions(t *testing.T) {
u := &user.User{ID: 1}
t.Run("Can Read", func(t *testing.T) {
t.Run("Allowed", func(t *testing.T) {

View File

@@ -60,8 +60,8 @@ type TaskCollection struct {
isSavedFilter bool
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
type TaskCollectionExpandable string

View File

@@ -661,8 +661,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
Expand []TaskCollectionExpandable
CRUDable web.CRUDable
Rights web.Rights
CRUDable web.CRUDable
Permissions web.Permissions
}
type args struct {
search string
@@ -1644,8 +1644,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
Expand: tt.fields.Expand,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
CRUDable: tt.fields.CRUDable,
Permissions: tt.fields.Permissions,
}
got, _, _, err := lt.ReadAll(s, tt.args.a, tt.args.search, tt.args.page, 50)
if (err != nil) != tt.wantErr {

View File

@@ -41,8 +41,8 @@ type TaskComment struct {
Created time.Time `xorm:"created" json:"created"`
Updated time.Time `xorm:"updated" json:"updated"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
// TableName holds the table name for the task comments table

View File

@@ -41,8 +41,8 @@ type TaskPosition struct {
// endpoint, otherwise they will always be 0. To update them, take a look at the Task Position endpoint.
Position float64 `xorm:"double not null" json:"position"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
func (tp *TaskPosition) TableName() string {

View File

@@ -95,8 +95,8 @@ type TaskRelation struct {
// A timestamp when this label was created. You cannot change this value.
Created time.Time `xorm:"created not null" json:"created"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
// TableName holds the table name for the task relation table
@@ -188,7 +188,7 @@ func checkTaskRelationCycle(s *xorm.Session, relation *TaskRelation, otherTaskID
// Create creates a new task relation
// @Summary Create a new relation between two tasks
// @Description Creates a new relation between two tasks. The user needs to have update rights on the base task and at least read rights on the other task. Both tasks do not need to be on the same project. Take a look at the docs for available task relation kinds.
// @Description Creates a new relation between two tasks. The user needs to have update permissions on the base task and at least read permissions on the other task. Both tasks do not need to be on the same project. Take a look at the docs for available task relation kinds.
// @tags task
// @Accept json
// @Produce json

View File

@@ -347,7 +347,7 @@ func TestTaskRelation_CanCreate(t *testing.T) {
require.NoError(t, err)
assert.True(t, can)
})
t.Run("No update rights on base task", func(t *testing.T) {
t.Run("No update permissions on base task", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := db.NewSession()
defer s.Close()
@@ -361,7 +361,7 @@ func TestTaskRelation_CanCreate(t *testing.T) {
require.NoError(t, err)
assert.False(t, can)
})
t.Run("No update rights on base task, but read rights", func(t *testing.T) {
t.Run("No update permissions on base task, but read permissions", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := db.NewSession()
defer s.Close()
@@ -375,7 +375,7 @@ func TestTaskRelation_CanCreate(t *testing.T) {
require.NoError(t, err)
assert.False(t, can)
})
t.Run("No read rights on other task", func(t *testing.T) {
t.Run("No read permissions on other task", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := db.NewSession()
defer s.Close()

View File

@@ -141,8 +141,8 @@ type Task struct {
CreatedBy *user.User `xorm:"-" json:"created_by" valid:"-"`
CreatedByID int64 `xorm:"bigint not null" json:"-"` // ID of the user who put that task on the project
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Permissions `xorm:"-" json:"-"`
}
type TaskWithComments struct {
@@ -1184,7 +1184,7 @@ func (t *Task) Update(s *xorm.Session, a web.Auth) (err error) {
//
// Maybe FIXME:
// I've disabled this for now, because it requires significant changes in the way we do updates (using the
// Update() function. We need a user object in updateTaskLabels to check if the user has the right to see
// Update() function. We need a user object in updateTaskLabels to check if the user has the permission to see
// the label it is currently adding. To do this, we'll need to update the webhandler to let it pass the current
// user object (like it's already the case with the create method). However when we change it, that'll break
// a lot of existing code which we'll then need to refactor.

View File

@@ -26,12 +26,12 @@ func (t *Task) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
return t.canDoTask(s, a)
}
// CanUpdate determines if a user has the right to update a project task
// CanUpdate determines if a user has the permission to update a project task
func (t *Task) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
return t.canDoTask(s, a)
}
// CanCreate determines if a user has the right to create a project task
// CanCreate determines if a user has the permission to create a project task
func (t *Task) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
// A user can do a task if he has write acces to its project
l := &Project{ID: t.ProjectID}
@@ -39,7 +39,7 @@ func (t *Task) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
}
// CanRead determines if a user can read a task
func (t *Task) CanRead(s *xorm.Session, a web.Auth) (canRead bool, maxRight int, err error) {
func (t *Task) CanRead(s *xorm.Session, a web.Auth) (canRead bool, maxPermission int, err error) {
expand := t.Expand
// Get the task, error out if it doesn't exist
*t, err = GetTaskByIDSimple(s, t.ID)
@@ -67,7 +67,7 @@ func (t *Task) canDoTask(s *xorm.Session, a web.Auth) (bool, error) {
return false, err
}
// Check if we're moving the task into a different project to check if the user has sufficient rights for that on the new project
// Check if we're moving the task into a different project to check if the user has sufficient permissions for that on the new project
if t.ProjectID != 0 && t.ProjectID != ot.ProjectID {
newProject := &Project{ID: t.ProjectID}
can, err := newProject.CanWrite(s, a)

View File

@@ -36,7 +36,7 @@ func TestTask_Create(t *testing.T) {
Email: "user1@example.com",
}
// We only test creating a task here, the rights are all well tested in the web tests.
// We only test creating a task here, the permissions are all well tested in the web tests.
t.Run("normal", func(t *testing.T) {
db.LoadAndAssertFixtures(t)

View File

@@ -132,7 +132,7 @@ func (tm *TeamMember) MembershipExists(s *xorm.Session) (exists bool, err error)
// @Security JWTKeyAuth
// @Param id path int true "Team ID"
// @Param userID path int true "User ID"
// @Success 200 {object} models.Message "The member right was successfully changed."
// @Success 200 {object} models.Message "The member permission was successfully changed."
// @Failure 500 {object} models.Message "Internal error"
// @Router /teams/{id}/members/{userID}/admin [post]
func (tm *TeamMember) Update(s *xorm.Session, _ web.Auth) (err error) {
@@ -143,7 +143,7 @@ func (tm *TeamMember) Update(s *xorm.Session, _ web.Auth) (err error) {
}
tm.UserID = user.ID
// Get the full member object and change the admin right
// Get the full member object and change the admin permission
ttm := &TeamMember{}
_, err = s.
Where("team_id = ? AND user_id = ?", tm.TeamID, tm.UserID).
@@ -158,6 +158,6 @@ func (tm *TeamMember) Update(s *xorm.Session, _ web.Auth) (err error) {
Where("team_id = ? AND user_id = ?", tm.TeamID, tm.UserID).
Cols("admin").
Update(ttm)
tm.Admin = ttm.Admin // Since we're returning the updated rights object
tm.Admin = ttm.Admin // Since we're returning the updated permissions object
return
}

Some files were not shown because too many files have changed in this diff Show More