mirror of
https://github.com/go-vikunja/vikunja.git
synced 2025-12-05 19:16:51 -06:00
feat!: rename right to permission (#1277)
This commit is contained in:
@@ -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.
|
||||
|
||||
16
AGENTS.md
16
AGENTS.md
@@ -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
|
||||
|
||||
@@ -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}',
|
||||
|
||||
@@ -11,7 +11,7 @@ function prepareLinkShare() {
|
||||
})
|
||||
const linkShares = LinkShareFactory.create(1, {
|
||||
project_id: projects[0].id,
|
||||
right: 0,
|
||||
permission: 0,
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) : '')
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
{{ $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>
|
||||
</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
|
||||
|
||||
@@ -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})})
|
||||
|
||||
7
frontend/src/constants/permissions.ts
Normal file
7
frontend/src/constants/permissions.ts
Normal 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]
|
||||
@@ -1,7 +0,0 @@
|
||||
export const RIGHTS = {
|
||||
'READ': 0,
|
||||
'READ_WRITE': 1,
|
||||
'ADMIN': 2,
|
||||
} as const
|
||||
|
||||
export type Right = typeof RIGHTS[keyof typeof RIGHTS]
|
||||
@@ -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?
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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},
|
||||
)
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"`
|
||||
|
||||
44
pkg/migration/20250813093602.go
Normal file
44
pkg/migration/20250813093602.go
Normal 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
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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()...)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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.",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"])
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user