feat!: rename right to permission (#1277)
This commit is contained in:
parent
70eef88557
commit
a81a3ee0e5
|
|
@ -139,7 +139,7 @@ linters:
|
||||||
text: 'G115: integer overflow conversion int -> uint64'
|
text: 'G115: integer overflow conversion int -> uint64'
|
||||||
- linters:
|
- linters:
|
||||||
- recvcheck
|
- 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:
|
- linters:
|
||||||
- recvcheck
|
- recvcheck
|
||||||
text: the methods of "SubscriptionEntityType" use pointer receiver and non-pointer receiver.
|
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:**
|
**Key Patterns:**
|
||||||
- **Generic CRUD**: Models implement `CRUDable` interface for standardized database operations
|
- **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
|
- **Event-Driven**: Event system for notifications, webhooks, and cross-cutting concerns
|
||||||
- **Modular Design**: Pluggable authentication, avatar providers, migration tools
|
- **Modular Design**: Pluggable authentication, avatar providers, migration tools
|
||||||
|
|
||||||
|
|
@ -115,7 +115,7 @@ Modern Vue 3 composition API application with TypeScript:
|
||||||
### Adding New Features
|
### Adding New Features
|
||||||
|
|
||||||
**Backend Changes:**
|
**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>`
|
2. Add database migration if needed: `mage dev:make-migration <StructName>`
|
||||||
3. Create/update services in `pkg/services/` for complex business logic
|
3. Create/update services in `pkg/services/` for complex business logic
|
||||||
4. Add API routes in `pkg/routes/api/v1/` following existing patterns
|
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
|
### API Development
|
||||||
- All API endpoints follow RESTful conventions under `/api/v1/`
|
- All API endpoints follow RESTful conventions under `/api/v1/`
|
||||||
- Use generic web handlers in `pkg/web/handler/` for standard CRUD operations
|
- 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
|
- Add Swagger annotations for automatic documentation generation
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
@ -175,7 +175,7 @@ After adjusting the source string, you need to call the respective translation l
|
||||||
- Use `pkg/config/` for configuration management
|
- Use `pkg/config/` for configuration management
|
||||||
|
|
||||||
**Code Style:**
|
**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
|
- 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
|
- 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
|
- Vue: PascalCase for components, camelCase for composables
|
||||||
- API endpoints: kebab-case in URLs, camelCase in JSON
|
- API endpoints: kebab-case in URLs, camelCase in JSON
|
||||||
|
|
||||||
**Rights and Permissions:**
|
**Permissions and Permissions:**
|
||||||
- Always implement Rights interface for new models
|
- Always implement Permissions interface for new models
|
||||||
- Use `CanRead`, `CanWrite`, `CanCreate`, `CanDelete` methods
|
- 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
|
## Common Gotchas
|
||||||
|
|
||||||
- Database migrations are irreversible in production - test thoroughly
|
- Database migrations are irreversible in production - test thoroughly
|
||||||
- Frontend services must match backend model structure exactly
|
- 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
|
- Event listeners in `pkg/*/listeners.go` must be registered properly
|
||||||
- CORS settings in backend must allow frontend domain
|
- CORS settings in backend must allow frontend domain
|
||||||
- API tokens have different scopes - check permissions carefully
|
- API tokens have different scopes - check permissions carefully
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ describe('Project View List', () => {
|
||||||
UserProjectFactory.create(1, {
|
UserProjectFactory.create(1, {
|
||||||
project_id: 2,
|
project_id: 2,
|
||||||
user_id: 1,
|
user_id: 1,
|
||||||
right: 0,
|
permission: 0,
|
||||||
})
|
})
|
||||||
const projects = ProjectFactory.create(2, {
|
const projects = ProjectFactory.create(2, {
|
||||||
owner_id: '{increment}',
|
owner_id: '{increment}',
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ function prepareLinkShare() {
|
||||||
})
|
})
|
||||||
const linkShares = LinkShareFactory.create(1, {
|
const linkShares = LinkShareFactory.create(1, {
|
||||||
project_id: projects[0].id,
|
project_id: projects[0].id,
|
||||||
right: 0,
|
permission: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ export class LinkShareFactory extends Factory {
|
||||||
id: '{increment}',
|
id: '{increment}',
|
||||||
hash: faker.lorem.word(32),
|
hash: faker.lorem.word(32),
|
||||||
project_id: 1,
|
project_id: 1,
|
||||||
right: 0,
|
permission: 0,
|
||||||
sharing_type: 0,
|
sharing_type: 0,
|
||||||
shared_by_id: 1,
|
shared_by_id: 1,
|
||||||
created: now.toISOString(),
|
created: now.toISOString(),
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ export class UserProjectFactory extends Factory {
|
||||||
id: '{increment}',
|
id: '{increment}',
|
||||||
project_id: 1,
|
project_id: 1,
|
||||||
user_id: 1,
|
user_id: 1,
|
||||||
right: 0,
|
permission: 0,
|
||||||
created: now.toISOString(),
|
created: now.toISOString(),
|
||||||
updated: now.toISOString(),
|
updated: now.toISOString(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
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 ProjectSettingsDropdown from '@/components/project/ProjectSettingsDropdown.vue'
|
||||||
import Dropdown from '@/components/misc/Dropdown.vue'
|
import Dropdown from '@/components/misc/Dropdown.vue'
|
||||||
|
|
@ -137,7 +137,7 @@ import { useAuthStore } from '@/stores/auth'
|
||||||
const baseStore = useBaseStore()
|
const baseStore = useBaseStore()
|
||||||
const currentProject = computed(() => baseStore.currentProject)
|
const currentProject = computed(() => baseStore.currentProject)
|
||||||
const background = computed(() => baseStore.background)
|
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 menuActive = computed(() => baseStore.menuActive)
|
||||||
|
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
/>
|
/>
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
<span
|
<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"
|
class="icon menu-item-icon handle drag-handle-standalone"
|
||||||
@mousedown.stop
|
@mousedown.stop
|
||||||
@click.stop.prevent
|
@click.stop.prevent
|
||||||
|
|
@ -48,7 +48,7 @@
|
||||||
<span class="project-menu-title">{{ getProjectTitle(project) }}</span>
|
<span class="project-menu-title">{{ getProjectTitle(project) }}</span>
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
v-if="project.id > 0 && project.maxRight > RIGHTS.READ"
|
v-if="project.id > 0 && project.maxPermission > PERMISSIONS.READ"
|
||||||
class="favorite"
|
class="favorite"
|
||||||
:class="{'is-favorite': project.isFavorite}"
|
:class="{'is-favorite': project.isFavorite}"
|
||||||
@click="projectStore.toggleProjectFavorite(project)"
|
@click="projectStore.toggleProjectFavorite(project)"
|
||||||
|
|
@ -57,7 +57,7 @@
|
||||||
<Icon :icon="project.isFavorite ? 'star' : ['far', 'star']" />
|
<Icon :icon="project.isFavorite ? 'star' : ['far', 'star']" />
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
<ProjectSettingsDropdown
|
<ProjectSettingsDropdown
|
||||||
v-if="project.maxRight > RIGHTS.READ"
|
v-if="project.maxPermission > PERMISSIONS.READ"
|
||||||
class="menu-list-dropdown"
|
class="menu-list-dropdown"
|
||||||
:project="project"
|
:project="project"
|
||||||
>
|
>
|
||||||
|
|
@ -97,7 +97,7 @@ import ProjectSettingsDropdown from '@/components/project/ProjectSettingsDropdow
|
||||||
import {getProjectTitle} from '@/helpers/getProjectTitle'
|
import {getProjectTitle} from '@/helpers/getProjectTitle'
|
||||||
import ColorBubble from '@/components/misc/ColorBubble.vue'
|
import ColorBubble from '@/components/misc/ColorBubble.vue'
|
||||||
import ProjectsNavigation from '@/components/home/ProjectsNavigation.vue'
|
import ProjectsNavigation from '@/components/home/ProjectsNavigation.vue'
|
||||||
import {RIGHTS} from '@/constants/rights'
|
import {PERMISSIONS} from '@/constants/permissions'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
project: IProject,
|
project: IProject,
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@
|
||||||
{{ $t('menu.createProject') }}
|
{{ $t('menu.createProject') }}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
v-if="project.maxRight === RIGHTS.ADMIN"
|
v-if="project.maxPermission === PERMISSIONS.ADMIN"
|
||||||
v-tooltip="isDefaultProject ? $t('menu.cantDeleteIsDefault') : ''"
|
v-tooltip="isDefaultProject ? $t('menu.cantDeleteIsDefault') : ''"
|
||||||
:to="{ name: 'project.settings.delete', params: { projectId: project.id } }"
|
:to="{ name: 'project.settings.delete', params: { projectId: project.id } }"
|
||||||
icon="trash-alt"
|
icon="trash-alt"
|
||||||
|
|
@ -137,7 +137,7 @@ import {isSavedFilter} from '@/services/savedFilter'
|
||||||
import {useConfigStore} from '@/stores/config'
|
import {useConfigStore} from '@/stores/config'
|
||||||
import {useProjectStore} from '@/stores/projects'
|
import {useProjectStore} from '@/stores/projects'
|
||||||
import {useAuthStore} from '@/stores/auth'
|
import {useAuthStore} from '@/stores/auth'
|
||||||
import {RIGHTS} from '@/constants/rights'
|
import {PERMISSIONS} from '@/constants/permissions'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
project: IProject
|
project: IProject
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ const currentProject = computed<IProject>(() => {
|
||||||
id: 0,
|
id: 0,
|
||||||
title: '',
|
title: '',
|
||||||
isArchived: false,
|
isArchived: false,
|
||||||
maxRight: null,
|
maxPermission: null,
|
||||||
} : baseStore.currentProject
|
} : baseStore.currentProject
|
||||||
})
|
})
|
||||||
useTitle(() => currentProject.value?.id ? getProjectTitle(currentProject.value) : '')
|
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 GanttChart from '@/components/gantt/GanttChart.vue'
|
||||||
import {useGanttFilters} from '../../../views/project/helpers/useGanttFilters'
|
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 {DateISO} from '@/types/DateISO'
|
||||||
import type {ITask} from '@/modelTypes/ITask'
|
import type {ITask} from '@/modelTypes/ITask'
|
||||||
|
|
@ -103,7 +103,7 @@ const props = defineProps<{
|
||||||
|
|
||||||
|
|
||||||
const baseStore = useBaseStore()
|
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 {route, viewId} = toRefs(props)
|
||||||
const {
|
const {
|
||||||
|
|
|
||||||
|
|
@ -279,7 +279,7 @@ import {useI18n} from 'vue-i18n'
|
||||||
import draggable from 'zhyswan-vuedraggable'
|
import draggable from 'zhyswan-vuedraggable'
|
||||||
import {klona} from 'klona/lite'
|
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 BucketModel from '@/models/bucket'
|
||||||
|
|
||||||
import type {IBucket} from '@/modelTypes/IBucket'
|
import type {IBucket} from '@/modelTypes/IBucket'
|
||||||
|
|
@ -398,7 +398,7 @@ const bucketDraggableComponentData = computed(() => ({
|
||||||
}))
|
}))
|
||||||
const project = computed(() => props.projectId ? projectStore.projects[props.projectId] : null)
|
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 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 canCreateTasks = computed(() => canWrite.value && props.projectId > 0)
|
||||||
const buckets = computed(() => kanbanStore.buckets)
|
const buckets = computed(() => kanbanStore.buckets)
|
||||||
const loading = computed(() => kanbanStore.isLoading)
|
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 {ALPHABETICAL_SORT} from '@/components/project/partials/Filters.vue'
|
||||||
|
|
||||||
import {useTaskList} from '@/composables/useTaskList'
|
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 {calculateItemPosition} from '@/helpers/calculateItemPosition'
|
||||||
import type {ITask} from '@/modelTypes/ITask'
|
import type {ITask} from '@/modelTypes/ITask'
|
||||||
import {isSavedFilter} from '@/services/savedFilter'
|
import {isSavedFilter} from '@/services/savedFilter'
|
||||||
|
|
@ -182,7 +182,7 @@ const baseStore = useBaseStore()
|
||||||
const project = computed(() => baseStore.currentProject)
|
const project = computed(() => baseStore.currentProject)
|
||||||
|
|
||||||
const canWrite = computed(() => {
|
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)
|
const isPseudoProject = computed(() => (project.value && isSavedFilter(project.value)) || project.value?.id === -1)
|
||||||
|
|
|
||||||
|
|
@ -29,22 +29,22 @@
|
||||||
class="label"
|
class="label"
|
||||||
for="linkShareRight"
|
for="linkShareRight"
|
||||||
>
|
>
|
||||||
{{ $t('project.share.right.title') }}
|
{{ $t('project.share.permission.title') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<div class="select">
|
<div class="select">
|
||||||
<select
|
<select
|
||||||
id="linkShareRight"
|
id="linkShareRight"
|
||||||
v-model="selectedRight"
|
v-model="selectedPermission"
|
||||||
>
|
>
|
||||||
<option :value="RIGHTS.READ">
|
<option :value="PERMISSIONS.READ">
|
||||||
{{ $t('project.share.right.read') }}
|
{{ $t('project.share.permission.read') }}
|
||||||
</option>
|
</option>
|
||||||
<option :value="RIGHTS.READ_WRITE">
|
<option :value="PERMISSIONS.READ_WRITE">
|
||||||
{{ $t('project.share.right.readWrite') }}
|
{{ $t('project.share.permission.readWrite') }}
|
||||||
</option>
|
</option>
|
||||||
<option :value="RIGHTS.ADMIN">
|
<option :value="PERMISSIONS.ADMIN">
|
||||||
{{ $t('project.share.right.admin') }}
|
{{ $t('project.share.permission.admin') }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -129,23 +129,23 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="mbe-2">
|
<p class="mbe-2">
|
||||||
<template v-if="s.right === RIGHTS.ADMIN">
|
<template v-if="s.permission === PERMISSIONS.ADMIN">
|
||||||
<span class="icon is-small">
|
<span class="icon is-small">
|
||||||
<Icon icon="lock" />
|
<Icon icon="lock" />
|
||||||
</span>
|
</span>
|
||||||
{{ $t('project.share.right.admin') }}
|
{{ $t('project.share.permission.admin') }}
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="s.right === RIGHTS.READ_WRITE">
|
<template v-else-if="s.permission === PERMISSIONS.READ_WRITE">
|
||||||
<span class="icon is-small">
|
<span class="icon is-small">
|
||||||
<Icon icon="pen" />
|
<Icon icon="pen" />
|
||||||
</span>
|
</span>
|
||||||
{{ $t('project.share.right.readWrite') }}
|
{{ $t('project.share.permission.readWrite') }}
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<span class="icon is-small">
|
<span class="icon is-small">
|
||||||
<Icon icon="users" />
|
<Icon icon="users" />
|
||||||
</span>
|
</span>
|
||||||
{{ $t('project.share.right.read') }}
|
{{ $t('project.share.permission.read') }}
|
||||||
</template>
|
</template>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
@ -221,7 +221,7 @@
|
||||||
import {ref, watch, computed, shallowReactive} from 'vue'
|
import {ref, watch, computed, shallowReactive} from 'vue'
|
||||||
import {useI18n} from 'vue-i18n'
|
import {useI18n} from 'vue-i18n'
|
||||||
|
|
||||||
import {RIGHTS} from '@/constants/rights'
|
import {PERMISSIONS} from '@/constants/permissions'
|
||||||
import LinkShareModel from '@/models/linkShare'
|
import LinkShareModel from '@/models/linkShare'
|
||||||
|
|
||||||
import type {ILinkShare} from '@/modelTypes/ILinkShare'
|
import type {ILinkShare} from '@/modelTypes/ILinkShare'
|
||||||
|
|
@ -246,7 +246,7 @@ const {t} = useI18n({useScope: 'global'})
|
||||||
|
|
||||||
const linkShares = ref<ILinkShare[]>([])
|
const linkShares = ref<ILinkShare[]>([])
|
||||||
const linkShareService = shallowReactive(new LinkShareService())
|
const linkShareService = shallowReactive(new LinkShareService())
|
||||||
const selectedRight = ref(RIGHTS.READ)
|
const selectedPermission = ref(PERMISSIONS.READ)
|
||||||
const name = ref('')
|
const name = ref('')
|
||||||
const password = ref('')
|
const password = ref('')
|
||||||
const showDeleteModal = ref(false)
|
const showDeleteModal = ref(false)
|
||||||
|
|
@ -296,13 +296,13 @@ watch(() => ([linkShares.value, availableViews.value]), ([newLinkShares, newProj
|
||||||
|
|
||||||
async function add(projectId: IProject['id']) {
|
async function add(projectId: IProject['id']) {
|
||||||
const newLinkShare = new LinkShareModel({
|
const newLinkShare = new LinkShareModel({
|
||||||
right: selectedRight.value,
|
permission: selectedPermission.value,
|
||||||
projectId,
|
projectId,
|
||||||
name: name.value,
|
name: name.value,
|
||||||
password: password.value,
|
password: password.value,
|
||||||
})
|
})
|
||||||
await linkShareService.create(newLinkShare)
|
await linkShareService.create(newLinkShare)
|
||||||
selectedRight.value = RIGHTS.READ
|
selectedPermission.value = PERMISSIONS.READ
|
||||||
name.value = ''
|
name.value = ''
|
||||||
password.value = ''
|
password.value = ''
|
||||||
showNewForm.value = false
|
showNewForm.value = false
|
||||||
|
|
|
||||||
|
|
@ -71,23 +71,23 @@
|
||||||
</td>
|
</td>
|
||||||
</template>
|
</template>
|
||||||
<td class="type">
|
<td class="type">
|
||||||
<template v-if="s.right === RIGHTS.ADMIN">
|
<template v-if="s.permission === PERMISSIONS.ADMIN">
|
||||||
<span class="icon is-small">
|
<span class="icon is-small">
|
||||||
<Icon icon="lock" />
|
<Icon icon="lock" />
|
||||||
</span>
|
</span>
|
||||||
{{ $t('project.share.right.admin') }}
|
{{ $t('project.share.permission.admin') }}
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="s.right === RIGHTS.READ_WRITE">
|
<template v-else-if="s.permission === PERMISSIONS.READ_WRITE">
|
||||||
<span class="icon is-small">
|
<span class="icon is-small">
|
||||||
<Icon icon="pen" />
|
<Icon icon="pen" />
|
||||||
</span>
|
</span>
|
||||||
{{ $t('project.share.right.readWrite') }}
|
{{ $t('project.share.permission.readWrite') }}
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<span class="icon is-small">
|
<span class="icon is-small">
|
||||||
<Icon icon="users" />
|
<Icon icon="users" />
|
||||||
</span>
|
</span>
|
||||||
{{ $t('project.share.right.read') }}
|
{{ $t('project.share.permission.read') }}
|
||||||
</template>
|
</template>
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
|
|
@ -96,27 +96,27 @@
|
||||||
>
|
>
|
||||||
<div class="select">
|
<div class="select">
|
||||||
<select
|
<select
|
||||||
v-model="selectedRight[s.id]"
|
v-model="selectedPermission[s.id]"
|
||||||
class="mie-2"
|
class="mie-2"
|
||||||
@change="toggleType(s)"
|
@change="toggleType(s)"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
:selected="s.right === RIGHTS.READ"
|
:selected="s.permission === PERMISSIONS.READ"
|
||||||
:value="RIGHTS.READ"
|
:value="PERMISSIONS.READ"
|
||||||
>
|
>
|
||||||
{{ $t('project.share.right.read') }}
|
{{ $t('project.share.permission.read') }}
|
||||||
</option>
|
</option>
|
||||||
<option
|
<option
|
||||||
:selected="s.right === RIGHTS.READ_WRITE"
|
:selected="s.permission === PERMISSIONS.READ_WRITE"
|
||||||
:value="RIGHTS.READ_WRITE"
|
:value="PERMISSIONS.READ_WRITE"
|
||||||
>
|
>
|
||||||
{{ $t('project.share.right.readWrite') }}
|
{{ $t('project.share.permission.readWrite') }}
|
||||||
</option>
|
</option>
|
||||||
<option
|
<option
|
||||||
:selected="s.right === RIGHTS.ADMIN"
|
:selected="s.permission === PERMISSIONS.ADMIN"
|
||||||
:value="RIGHTS.ADMIN"
|
:value="PERMISSIONS.ADMIN"
|
||||||
>
|
>
|
||||||
{{ $t('project.share.right.admin') }}
|
{{ $t('project.share.permission.admin') }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -178,7 +178,7 @@ import TeamModel from '@/models/team'
|
||||||
import type {ITeam} from '@/modelTypes/ITeam'
|
import type {ITeam} from '@/modelTypes/ITeam'
|
||||||
|
|
||||||
|
|
||||||
import {RIGHTS} from '@/constants/rights'
|
import {PERMISSIONS} from '@/constants/permissions'
|
||||||
import Multiselect from '@/components/input/Multiselect.vue'
|
import Multiselect from '@/components/input/Multiselect.vue'
|
||||||
import Nothing from '@/components/misc/Nothing.vue'
|
import Nothing from '@/components/misc/Nothing.vue'
|
||||||
import {success} from '@/message'
|
import {success} from '@/message'
|
||||||
|
|
@ -209,7 +209,7 @@ let searchService: UserService | TeamService
|
||||||
let sharable: Ref<IUser | ITeam>
|
let sharable: Ref<IUser | ITeam>
|
||||||
|
|
||||||
const searchLabel = ref('')
|
const searchLabel = ref('')
|
||||||
const selectedRight = ref({})
|
const selectedPermission = ref({})
|
||||||
|
|
||||||
|
|
||||||
// This holds either teams or users who this namepace or project is shared with
|
// This holds either teams or users who this namepace or project is shared with
|
||||||
|
|
@ -275,8 +275,8 @@ load()
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
sharables.value = await stuffService.getAll(stuffModel)
|
sharables.value = await stuffService.getAll(stuffModel)
|
||||||
sharables.value.forEach(({id, right}) =>
|
sharables.value.forEach(({id, permission}) =>
|
||||||
selectedRight.value[id] = right,
|
selectedPermission.value[id] = permission,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -309,9 +309,9 @@ async function add(admin) {
|
||||||
if (admin === null) {
|
if (admin === null) {
|
||||||
admin = false
|
admin = false
|
||||||
}
|
}
|
||||||
stuffModel.right = RIGHTS.READ
|
stuffModel.permission = PERMISSIONS.READ
|
||||||
if (admin) {
|
if (admin) {
|
||||||
stuffModel.right = RIGHTS.ADMIN
|
stuffModel.permission = PERMISSIONS.ADMIN
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.shareType === 'user') {
|
if (props.shareType === 'user') {
|
||||||
|
|
@ -327,13 +327,13 @@ async function add(admin) {
|
||||||
|
|
||||||
async function toggleType(sharable) {
|
async function toggleType(sharable) {
|
||||||
if (
|
if (
|
||||||
selectedRight.value[sharable.id] !== RIGHTS.ADMIN &&
|
selectedPermission.value[sharable.id] !== PERMISSIONS.ADMIN &&
|
||||||
selectedRight.value[sharable.id] !== RIGHTS.READ &&
|
selectedPermission.value[sharable.id] !== PERMISSIONS.READ &&
|
||||||
selectedRight.value[sharable.id] !== RIGHTS.READ_WRITE
|
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') {
|
if (props.shareType === 'user') {
|
||||||
stuffModel.username = sharable.username
|
stuffModel.username = sharable.username
|
||||||
|
|
@ -350,7 +350,7 @@ async function toggleType(sharable) {
|
||||||
(sharables.value[i].id === stuffModel.teamId &&
|
(sharables.value[i].id === stuffModel.teamId &&
|
||||||
props.shareType === 'team')
|
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})})
|
success({message: t('project.share.userTeam.updatedSuccess', {type: shareTypeName.value})})
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
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 {IAbstract} from './IAbstract'
|
||||||
import type {IUser} from './IUser'
|
import type {IUser} from './IUser'
|
||||||
import type { Right } from '@/constants/rights'
|
import type { Permission } from '@/constants/permissions'
|
||||||
|
|
||||||
export interface ILinkShare extends IAbstract {
|
export interface ILinkShare extends IAbstract {
|
||||||
id: number
|
id: number
|
||||||
hash: string
|
hash: string
|
||||||
right: Right
|
permission: Permission
|
||||||
sharedBy: IUser
|
sharedBy: IUser
|
||||||
sharingType: number // FIXME: use correct numbers
|
sharingType: number // FIXME: use correct numbers
|
||||||
projectId: number
|
projectId: number
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import type {IAbstract} from './IAbstract'
|
import type {IAbstract} from './IAbstract'
|
||||||
import type {IUser} from './IUser'
|
import type {IUser} from './IUser'
|
||||||
import type {ITeamMember} from './ITeamMember'
|
import type {ITeamMember} from './ITeamMember'
|
||||||
import type {Right} from '@/constants/rights'
|
import type {Permission} from '@/constants/permissions'
|
||||||
|
|
||||||
export interface ITeam extends IAbstract {
|
export interface ITeam extends IAbstract {
|
||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
members: ITeamMember[]
|
members: ITeamMember[]
|
||||||
right: Right
|
permission: Permission
|
||||||
externalId: string
|
externalId: string
|
||||||
isPublic: boolean
|
isPublic: boolean
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import type {IAbstract} from './IAbstract'
|
import type {IAbstract} from './IAbstract'
|
||||||
import type {ITeam} from './ITeam'
|
import type {ITeam} from './ITeam'
|
||||||
import type {Right} from '@/constants/rights'
|
import type {Permission} from '@/constants/permissions'
|
||||||
|
|
||||||
export interface ITeamShareBase extends IAbstract {
|
export interface ITeamShareBase extends IAbstract {
|
||||||
teamId: ITeam['id']
|
teamId: ITeam['id']
|
||||||
right: Right
|
permission: Permission
|
||||||
|
|
||||||
created: Date
|
created: Date
|
||||||
updated: Date
|
updated: Date
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import type {IAbstract} from './IAbstract'
|
import type {IAbstract} from './IAbstract'
|
||||||
import type {IUser} from './IUser'
|
import type {IUser} from './IUser'
|
||||||
import type {Right} from '@/constants/rights'
|
import type {Permission} from '@/constants/permissions'
|
||||||
|
|
||||||
export interface IUserShareBase extends IAbstract {
|
export interface IUserShareBase extends IAbstract {
|
||||||
username: IUser['username']
|
username: IUser['username']
|
||||||
right: Right
|
permission: Permission
|
||||||
|
|
||||||
created: Date
|
created: Date
|
||||||
updated: Date
|
updated: Date
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import {objectToCamelCase} from '@/helpers/case'
|
import {objectToCamelCase} from '@/helpers/case'
|
||||||
import {omitBy, isNil} from '@/helpers/utils'
|
import {omitBy, isNil} from '@/helpers/utils'
|
||||||
import type {Right} from '@/constants/rights'
|
import type {Permission} from '@/constants/permissions'
|
||||||
import type {IAbstract} from '@/modelTypes/IAbstract'
|
import type {IAbstract} from '@/modelTypes/IAbstract'
|
||||||
|
|
||||||
export default abstract class AbstractModel<Model extends IAbstract = IAbstract> implements 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.
|
* Takes an object and merges its data with the default data of this model.
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import AbstractModel from './abstractModel'
|
import AbstractModel from './abstractModel'
|
||||||
import UserModel from './user'
|
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 {ILinkShare} from '@/modelTypes/ILinkShare'
|
||||||
import type {IUser} from '@/modelTypes/IUser'
|
import type {IUser} from '@/modelTypes/IUser'
|
||||||
|
|
||||||
export default class LinkShareModel extends AbstractModel<ILinkShare> implements ILinkShare {
|
export default class LinkShareModel extends AbstractModel<ILinkShare> implements ILinkShare {
|
||||||
id = 0
|
id = 0
|
||||||
hash = ''
|
hash = ''
|
||||||
right: Right = RIGHTS.READ
|
permission: Permission = PERMISSIONS.READ
|
||||||
sharedBy: IUser = UserModel
|
sharedBy: IUser = UserModel
|
||||||
sharingType = 0 // FIXME: use correct numbers
|
sharingType = 0 // FIXME: use correct numbers
|
||||||
projectId = 0
|
projectId = 0
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import AbstractModel from './abstractModel'
|
||||||
import UserModel from './user'
|
import UserModel from './user'
|
||||||
import TeamMemberModel from './teamMember'
|
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 {ITeam} from '@/modelTypes/ITeam'
|
||||||
import type {ITeamMember} from '@/modelTypes/ITeamMember'
|
import type {ITeamMember} from '@/modelTypes/ITeamMember'
|
||||||
import type {IUser} from '@/modelTypes/IUser'
|
import type {IUser} from '@/modelTypes/IUser'
|
||||||
|
|
@ -12,7 +12,7 @@ export default class TeamModel extends AbstractModel<ITeam> implements ITeam {
|
||||||
name = ''
|
name = ''
|
||||||
description = ''
|
description = ''
|
||||||
members: ITeamMember[] = []
|
members: ITeamMember[] = []
|
||||||
right: Right = RIGHTS.READ
|
permission: Permission = PERMISSIONS.READ
|
||||||
externalId = ''
|
externalId = ''
|
||||||
isPublic: boolean = false
|
isPublic: boolean = false
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import AbstractModel from './abstractModel'
|
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 {ITeamShareBase} from '@/modelTypes/ITeamShareBase'
|
||||||
import type {ITeam} from '@/modelTypes/ITeam'
|
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 {
|
export default class TeamShareBaseModel extends AbstractModel<ITeamShareBase> implements ITeamShareBase {
|
||||||
teamId: ITeam['id'] = 0
|
teamId: ITeam['id'] = 0
|
||||||
right: Right = RIGHTS.READ
|
permission: Permission = PERMISSIONS.READ
|
||||||
|
|
||||||
created: Date = null
|
created: Date = null
|
||||||
updated: Date = null
|
updated: Date = null
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import UserShareBaseModel from './userShareBase'
|
||||||
import type {IUserProject} from '@/modelTypes/IUserProject'
|
import type {IUserProject} from '@/modelTypes/IUserProject'
|
||||||
import type {IProject} from '@/modelTypes/IProject'
|
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 {
|
export default class UserProjectModel extends UserShareBaseModel implements IUserProject {
|
||||||
projectId: IProject['id'] = 0
|
projectId: IProject['id'] = 0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import AbstractModel from './abstractModel'
|
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 {IUserShareBase} from '@/modelTypes/IUserShareBase'
|
||||||
import type {IUser} from '@/modelTypes/IUser'
|
import type {IUser} from '@/modelTypes/IUser'
|
||||||
|
|
||||||
export default class UserShareBaseModel extends AbstractModel<IUserShareBase> implements IUserShareBase {
|
export default class UserShareBaseModel extends AbstractModel<IUserShareBase> implements IUserShareBase {
|
||||||
username: IUser['username'] = ''
|
username: IUser['username'] = ''
|
||||||
right: Right = RIGHTS.READ
|
permission: Permission = PERMISSIONS.READ
|
||||||
|
|
||||||
created: Date = null
|
created: Date = null
|
||||||
updated: Date = null
|
updated: Date = null
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import type {Method} from 'axios'
|
||||||
import {objectToSnakeCase} from '@/helpers/case'
|
import {objectToSnakeCase} from '@/helpers/case'
|
||||||
import AbstractModel from '@/models/abstractModel'
|
import AbstractModel from '@/models/abstractModel'
|
||||||
import type {IAbstract} from '@/modelTypes/IAbstract'
|
import type {IAbstract} from '@/modelTypes/IAbstract'
|
||||||
import type {Right} from '@/constants/rights'
|
import type {Permission} from '@/constants/permissions'
|
||||||
|
|
||||||
interface Paths {
|
interface Paths {
|
||||||
create : string
|
create : string
|
||||||
|
|
@ -310,7 +310,7 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
|
||||||
try {
|
try {
|
||||||
const response = await this.http.get(finalUrl, {params: prepareParams(params)})
|
const response = await this.http.get(finalUrl, {params: prepareParams(params)})
|
||||||
const result = this.modelGetFactory(response.data)
|
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
|
return result
|
||||||
} finally {
|
} finally {
|
||||||
cancel()
|
cancel()
|
||||||
|
|
@ -386,8 +386,8 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
|
||||||
try {
|
try {
|
||||||
const response = await this.http.put(finalUrl, model)
|
const response = await this.http.put(finalUrl, model)
|
||||||
const result = this.modelCreateFactory(response.data)
|
const result = this.modelCreateFactory(response.data)
|
||||||
if (typeof model.maxRight !== 'undefined') {
|
if (typeof model.maxPermission !== 'undefined') {
|
||||||
result.maxRight = model.maxRight
|
result.maxPermission = model.maxPermission
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -405,8 +405,8 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
|
||||||
try {
|
try {
|
||||||
const response = await this.http.post(url, model)
|
const response = await this.http.post(url, model)
|
||||||
const result = this.modelUpdateFactory(response.data)
|
const result = this.modelUpdateFactory(response.data)
|
||||||
if (typeof model.maxRight !== 'undefined') {
|
if (typeof model.maxPermission !== 'undefined') {
|
||||||
result.maxRight = model.maxRight
|
result.maxPermission = model.maxPermission
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import {useMenuActive} from '@/composables/useMenuActive'
|
||||||
|
|
||||||
import {useAuthStore} from '@/stores/auth'
|
import {useAuthStore} from '@/stores/auth'
|
||||||
import type {IProject} from '@/modelTypes/IProject'
|
import type {IProject} from '@/modelTypes/IProject'
|
||||||
import type {Right} from '@/constants/rights'
|
import type {Permission} from '@/constants/permissions'
|
||||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||||
|
|
||||||
export const useBaseStore = defineStore('base', () => {
|
export const useBaseStore = defineStore('base', () => {
|
||||||
|
|
@ -40,19 +40,19 @@ export const useBaseStore = defineStore('base', () => {
|
||||||
const updateAvailable = ref(false)
|
const updateAvailable = ref(false)
|
||||||
|
|
||||||
function setCurrentProject(newCurrentProject: IProject | null, currentViewId?: IProjectView['id']) {
|
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
|
// 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 right
|
// 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.
|
// when updating the project in global state.
|
||||||
let maxRight: Right | null = newCurrentProject?.maxRight || null
|
let maxPermission: Permission | null = newCurrentProject?.maxPermission || null
|
||||||
if (
|
if (
|
||||||
typeof currentProject.value?.maxRight !== 'undefined' &&
|
typeof currentProject.value?.maxPermission !== 'undefined' &&
|
||||||
newCurrentProject !== null &&
|
newCurrentProject !== null &&
|
||||||
(
|
(
|
||||||
typeof newCurrentProject.maxRight === 'undefined' ||
|
typeof newCurrentProject.maxPermission === 'undefined' ||
|
||||||
newCurrentProject.maxRight === null
|
newCurrentProject.maxPermission === null
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
maxRight = currentProject.value.maxRight
|
maxPermission = currentProject.value.maxPermission
|
||||||
}
|
}
|
||||||
if (newCurrentProject === null) {
|
if (newCurrentProject === null) {
|
||||||
currentProject.value = null
|
currentProject.value = null
|
||||||
|
|
@ -60,7 +60,7 @@ export const useBaseStore = defineStore('base', () => {
|
||||||
}
|
}
|
||||||
currentProject.value = {
|
currentProject.value = {
|
||||||
...newCurrentProject,
|
...newCurrentProject,
|
||||||
maxRight,
|
maxPermission,
|
||||||
}
|
}
|
||||||
setCurrentProjectViewId(currentViewId)
|
setCurrentProjectViewId(currentViewId)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import {success} from '@/message'
|
||||||
import {useBaseStore} from '@/stores/base'
|
import {useBaseStore} from '@/stores/base'
|
||||||
import {getSavedFilterIdFromProjectId} from '@/services/savedFilter'
|
import {getSavedFilterIdFromProjectId} from '@/services/savedFilter'
|
||||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
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'])
|
const {add, remove, search, update} = createNewIndexer('projects', ['title', 'description'])
|
||||||
|
|
||||||
|
|
@ -213,7 +213,7 @@ export const useProjectStore = defineStore('project', () => {
|
||||||
let page = 1
|
let page = 1
|
||||||
try {
|
try {
|
||||||
do {
|
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)
|
loadedProjects.push(...newProjects)
|
||||||
page++
|
page++
|
||||||
} while (page <= projectService.totalPages)
|
} while (page <= projectService.totalPages)
|
||||||
|
|
@ -320,7 +320,7 @@ export function useProject(projectId: MaybeRefOrGetter<IProject['id']>) {
|
||||||
|
|
||||||
const duplicate = await projectDuplicateService.create(projectDuplicate)
|
const duplicate = await projectDuplicateService.create(projectDuplicate)
|
||||||
if (duplicate.duplicatedProject) {
|
if (duplicate.duplicatedProject) {
|
||||||
duplicate.duplicatedProject.maxRight = RIGHTS.ADMIN
|
duplicate.duplicatedProject.maxPermission = PERMISSIONS.ADMIN
|
||||||
}
|
}
|
||||||
|
|
||||||
projectStore.setProject(duplicate.duplicatedProject)
|
projectStore.setProject(duplicate.duplicatedProject)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
:title="$t('project.edit.header')"
|
:title="$t('project.edit.header')"
|
||||||
primary-icon=""
|
primary-icon=""
|
||||||
:primary-label="$t('misc.save')"
|
: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"
|
@primary="save"
|
||||||
@tertiary="$router.push({ name: 'project.settings.delete', params: { id: projectId } })"
|
@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 {useProject} from '@/stores/projects'
|
||||||
|
|
||||||
import {useTitle} from '@/composables/useTitle'
|
import {useTitle} from '@/composables/useTitle'
|
||||||
import {RIGHTS} from '@/constants/rights'
|
import {PERMISSIONS} from '@/constants/permissions'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
projectId: IProject['id'],
|
projectId: IProject['id'],
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ import {useTitle} from '@vueuse/core'
|
||||||
import ProjectService from '@/services/project'
|
import ProjectService from '@/services/project'
|
||||||
import ProjectModel from '@/models/project'
|
import ProjectModel from '@/models/project'
|
||||||
import type {IProject} from '@/modelTypes/IProject'
|
import type {IProject} from '@/modelTypes/IProject'
|
||||||
import {RIGHTS} from '@/constants/rights'
|
import {PERMISSIONS} from '@/constants/permissions'
|
||||||
|
|
||||||
import CreateEdit from '@/components/misc/CreateEdit.vue'
|
import CreateEdit from '@/components/misc/CreateEdit.vue'
|
||||||
import LinkSharing from '@/components/sharing/LinkSharing.vue'
|
import LinkSharing from '@/components/sharing/LinkSharing.vue'
|
||||||
|
|
@ -57,7 +57,7 @@ useTitle(title)
|
||||||
const configStore = useConfigStore()
|
const configStore = useConfigStore()
|
||||||
|
|
||||||
const linkSharingEnabled = computed(() => configStore.linkSharingEnabled)
|
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) {
|
async function loadProject(projectId: number) {
|
||||||
const projectService = new ProjectService()
|
const projectService = new ProjectService()
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import XButton from '@/components/input/Button.vue'
|
||||||
import {error, success} from '@/message'
|
import {error, success} from '@/message'
|
||||||
import {useI18n} from 'vue-i18n'
|
import {useI18n} from 'vue-i18n'
|
||||||
import ProjectService from '@/services/project'
|
import ProjectService from '@/services/project'
|
||||||
import {RIGHTS} from '@/constants/rights'
|
import {PERMISSIONS} from '@/constants/permissions'
|
||||||
import ProjectModel from '@/models/project'
|
import ProjectModel from '@/models/project'
|
||||||
import Message from '@/components/misc/Message.vue'
|
import Message from '@/components/misc/Message.vue'
|
||||||
import draggable from 'zhyswan-vuedraggable'
|
import draggable from 'zhyswan-vuedraggable'
|
||||||
|
|
@ -49,7 +49,7 @@ watch(
|
||||||
async () => {
|
async () => {
|
||||||
const projectService = new ProjectService()
|
const projectService = new ProjectService()
|
||||||
const project = await projectService.get(new ProjectModel({id: props.projectId}))
|
const project = await projectService.get(new ProjectModel({id: props.projectId}))
|
||||||
isAdmin.value = project.maxRight === RIGHTS.ADMIN
|
isAdmin.value = project.maxPermission === PERMISSIONS.ADMIN
|
||||||
},
|
},
|
||||||
{immediate: true},
|
{immediate: true},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -603,7 +603,7 @@ import type {ITask} from '@/modelTypes/ITask'
|
||||||
import type {IProject} from '@/modelTypes/IProject'
|
import type {IProject} from '@/modelTypes/IProject'
|
||||||
|
|
||||||
import {PRIORITIES, type Priority} from '@/constants/priorities'
|
import {PRIORITIES, type Priority} from '@/constants/priorities'
|
||||||
import {RIGHTS} from '@/constants/rights'
|
import {PERMISSIONS} from '@/constants/permissions'
|
||||||
|
|
||||||
import BaseButton from '@/components/base/BaseButton.vue'
|
import BaseButton from '@/components/base/BaseButton.vue'
|
||||||
|
|
||||||
|
|
@ -703,8 +703,8 @@ const visible = ref(false)
|
||||||
const project = computed(() => projectStore.projects[task.value.projectId])
|
const project = computed(() => projectStore.projects[task.value.projectId])
|
||||||
|
|
||||||
const canWrite = computed(() => (
|
const canWrite = computed(() => (
|
||||||
task.value.maxRight !== null &&
|
task.value.maxPermission !== null &&
|
||||||
task.value.maxRight > RIGHTS.READ
|
task.value.maxPermission > PERMISSIONS.READ
|
||||||
))
|
))
|
||||||
|
|
||||||
const color = computed(() => {
|
const color = computed(() => {
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,7 @@ import TeamService from '@/services/team'
|
||||||
import TeamMemberService from '@/services/teamMember'
|
import TeamMemberService from '@/services/teamMember'
|
||||||
import UserService from '@/services/user'
|
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 {useTitle} from '@/composables/useTitle'
|
||||||
import {success} from '@/message'
|
import {success} from '@/message'
|
||||||
|
|
@ -292,8 +292,8 @@ const {t} = useI18n({useScope: 'global'})
|
||||||
const userIsAdmin = computed(() => {
|
const userIsAdmin = computed(() => {
|
||||||
return (
|
return (
|
||||||
team.value &&
|
team.value &&
|
||||||
team.value.maxRight &&
|
team.value.maxPermission &&
|
||||||
team.value.maxRight > Rights.READ
|
team.value.maxPermission > Permissions.READ
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
const userInfo = computed(() => authStore.info)
|
const userInfo = computed(() => authStore.info)
|
||||||
|
|
|
||||||
|
|
@ -1403,7 +1403,7 @@ func (Plugins) Build(pathToSourceFiles string) error {
|
||||||
if pathToSourceFiles == "" {
|
if pathToSourceFiles == "" {
|
||||||
return fmt.Errorf("please provide a plugin path")
|
return fmt.Errorf("please provide a plugin path")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert relative path to absolute path
|
// Convert relative path to absolute path
|
||||||
if !strings.HasPrefix(pathToSourceFiles, "/") {
|
if !strings.HasPrefix(pathToSourceFiles, "/") {
|
||||||
absPath, err := filepath.Abs(pathToSourceFiles)
|
absPath, err := filepath.Abs(pathToSourceFiles)
|
||||||
|
|
@ -1412,7 +1412,7 @@ func (Plugins) Build(pathToSourceFiles string) error {
|
||||||
}
|
}
|
||||||
pathToSourceFiles = absPath
|
pathToSourceFiles = absPath
|
||||||
}
|
}
|
||||||
|
|
||||||
out := filepath.Join(RootPath, "plugins", filepath.Base(pathToSourceFiles)+".so")
|
out := filepath.Join(RootPath, "plugins", filepath.Base(pathToSourceFiles)+".so")
|
||||||
runAndStreamOutput("go", "build", "-buildmode=plugin", "-o", out, pathToSourceFiles)
|
runAndStreamOutput("go", "build", "-buildmode=plugin", "-o", out, pathToSourceFiles)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
- id: 1
|
- id: 1
|
||||||
hash: test
|
hash: test
|
||||||
project_id: 1
|
project_id: 1
|
||||||
right: 0
|
permission: 0
|
||||||
sharing_type: 1
|
sharing_type: 1
|
||||||
shared_by_id: 1
|
shared_by_id: 1
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
- id: 2
|
- id: 2
|
||||||
hash: test2
|
hash: test2
|
||||||
project_id: 2
|
project_id: 2
|
||||||
right: 1
|
permission: 1
|
||||||
sharing_type: 1
|
sharing_type: 1
|
||||||
shared_by_id: 1
|
shared_by_id: 1
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
- id: 3
|
- id: 3
|
||||||
hash: test3
|
hash: test3
|
||||||
project_id: 3
|
project_id: 3
|
||||||
right: 2
|
permission: 2
|
||||||
sharing_type: 1
|
sharing_type: 1
|
||||||
shared_by_id: 1
|
shared_by_id: 1
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
hash: testWithPassword
|
hash: testWithPassword
|
||||||
name: testWithPassword
|
name: testWithPassword
|
||||||
project_id: 1
|
project_id: 1
|
||||||
right: 0
|
permission: 0
|
||||||
password: '$2a$04$X4aRMEt0ytgPwMIgv36cI..7X9.nhY/.tYwxpqSi0ykRHx2CwQ0S6' # 12345678
|
password: '$2a$04$X4aRMEt0ytgPwMIgv36cI..7X9.nhY/.tYwxpqSi0ykRHx2CwQ0S6' # 12345678
|
||||||
sharing_type: 2
|
sharing_type: 2
|
||||||
shared_by_id: 1
|
shared_by_id: 1
|
||||||
|
|
|
||||||
|
|
@ -1,96 +1,96 @@
|
||||||
- id: 1
|
- id: 1
|
||||||
team_id: 1
|
team_id: 1
|
||||||
project_id: 3
|
project_id: 3
|
||||||
right: 0
|
permission: 0
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
# This team has read only access on project 6
|
# This team has read only access on project 6
|
||||||
- id: 2
|
- id: 2
|
||||||
team_id: 2
|
team_id: 2
|
||||||
project_id: 6
|
project_id: 6
|
||||||
right: 0
|
permission: 0
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
# This team has write access on project 7
|
# This team has write access on project 7
|
||||||
- id: 3
|
- id: 3
|
||||||
team_id: 3
|
team_id: 3
|
||||||
project_id: 7
|
project_id: 7
|
||||||
right: 1
|
permission: 1
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
# This team has admin access on project 8
|
# This team has admin access on project 8
|
||||||
- id: 4
|
- id: 4
|
||||||
team_id: 4
|
team_id: 4
|
||||||
project_id: 8
|
project_id: 8
|
||||||
right: 2
|
permission: 2
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
# Readonly acces on project 19
|
# Readonly acces on project 19
|
||||||
- id: 5
|
- id: 5
|
||||||
team_id: 8
|
team_id: 8
|
||||||
project_id: 19
|
project_id: 19
|
||||||
right: 2
|
permission: 2
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
# Write acces on project 19
|
# Write acces on project 19
|
||||||
- id: 6
|
- id: 6
|
||||||
team_id: 9
|
team_id: 9
|
||||||
project_id: 19
|
project_id: 19
|
||||||
right: 1
|
permission: 1
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
# Admin acces on project 19
|
# Admin acces on project 19
|
||||||
- id: 7
|
- id: 7
|
||||||
team_id: 10
|
team_id: 10
|
||||||
project_id: 19
|
project_id: 19
|
||||||
right: 2
|
permission: 2
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 8
|
- id: 8
|
||||||
team_id: 1
|
team_id: 1
|
||||||
project_id: 21
|
project_id: 21
|
||||||
right: 0
|
permission: 0
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 9
|
- id: 9
|
||||||
team_id: 1
|
team_id: 1
|
||||||
project_id: 28
|
project_id: 28
|
||||||
right: 0
|
permission: 0
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 10
|
- id: 10
|
||||||
team_id: 11
|
team_id: 11
|
||||||
project_id: 29
|
project_id: 29
|
||||||
right: 0
|
permission: 0
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 11
|
- id: 11
|
||||||
team_id: 12
|
team_id: 12
|
||||||
project_id: 29
|
project_id: 29
|
||||||
right: 1
|
permission: 1
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 12
|
- id: 12
|
||||||
team_id: 13
|
team_id: 13
|
||||||
project_id: 29
|
project_id: 29
|
||||||
right: 2
|
permission: 2
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 13
|
- id: 13
|
||||||
team_id: 1
|
team_id: 1
|
||||||
project_id: 32
|
project_id: 32
|
||||||
right: 0
|
permission: 0
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 14
|
- id: 14
|
||||||
team_id: 1
|
team_id: 1
|
||||||
project_id: 33
|
project_id: 33
|
||||||
right: 1
|
permission: 1
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 15
|
- id: 15
|
||||||
team_id: 1
|
team_id: 1
|
||||||
project_id: 34
|
project_id: 34
|
||||||
right: 2
|
permission: 2
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
|
|
|
||||||
|
|
@ -1,114 +1,114 @@
|
||||||
- id: 1
|
- id: 1
|
||||||
user_id: 1
|
user_id: 1
|
||||||
project_id: 3
|
project_id: 3
|
||||||
right: 0
|
permission: 0
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 2
|
- id: 2
|
||||||
user_id: 2
|
user_id: 2
|
||||||
project_id: 3
|
project_id: 3
|
||||||
right: 0
|
permission: 0
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 3
|
- id: 3
|
||||||
user_id: 1
|
user_id: 1
|
||||||
project_id: 9
|
project_id: 9
|
||||||
right: 0
|
permission: 0
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 4
|
- id: 4
|
||||||
user_id: 1
|
user_id: 1
|
||||||
project_id: 10
|
project_id: 10
|
||||||
right: 1
|
permission: 1
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 5
|
- id: 5
|
||||||
user_id: 1
|
user_id: 1
|
||||||
project_id: 11
|
project_id: 11
|
||||||
right: 2
|
permission: 2
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 6
|
- id: 6
|
||||||
user_id: 4
|
user_id: 4
|
||||||
project_id: 19
|
project_id: 19
|
||||||
right: 0
|
permission: 0
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 7
|
- id: 7
|
||||||
user_id: 5
|
user_id: 5
|
||||||
project_id: 19
|
project_id: 19
|
||||||
right: 1
|
permission: 1
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 8
|
- id: 8
|
||||||
user_id: 6
|
user_id: 6
|
||||||
project_id: 19
|
project_id: 19
|
||||||
right: 2
|
permission: 2
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 9
|
- id: 9
|
||||||
user_id: 1
|
user_id: 1
|
||||||
project_id: 27
|
project_id: 27
|
||||||
right: 0
|
permission: 0
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 10
|
- id: 10
|
||||||
user_id: 11
|
user_id: 11
|
||||||
project_id: 29
|
project_id: 29
|
||||||
right: 0
|
permission: 0
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 11
|
- id: 11
|
||||||
user_id: 12
|
user_id: 12
|
||||||
project_id: 29
|
project_id: 29
|
||||||
right: 1
|
permission: 1
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 12
|
- id: 12
|
||||||
user_id: 13
|
user_id: 13
|
||||||
project_id: 29
|
project_id: 29
|
||||||
right: 2
|
permission: 2
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 13
|
- id: 13
|
||||||
user_id: 1
|
user_id: 1
|
||||||
project_id: 30
|
project_id: 30
|
||||||
right: 1
|
permission: 1
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 14
|
- id: 14
|
||||||
user_id: 1
|
user_id: 1
|
||||||
project_id: 31
|
project_id: 31
|
||||||
right: 2
|
permission: 2
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 15
|
- id: 15
|
||||||
user_id: 1
|
user_id: 1
|
||||||
project_id: 28
|
project_id: 28
|
||||||
right: 1
|
permission: 1
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 16
|
- id: 16
|
||||||
user_id: 1
|
user_id: 1
|
||||||
project_id: 29
|
project_id: 29
|
||||||
right: 2
|
permission: 2
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 17
|
- id: 17
|
||||||
user_id: 15
|
user_id: 15
|
||||||
project_id: 26
|
project_id: 26
|
||||||
right: 0
|
permission: 0
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 18
|
- id: 18
|
||||||
user_id: 15
|
user_id: 15
|
||||||
project_id: 36
|
project_id: 36
|
||||||
right: 0
|
permission: 0
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
- id: 19
|
- id: 19
|
||||||
user_id: 15
|
user_id: 15
|
||||||
project_id: 38
|
project_id: 38
|
||||||
right: 0
|
permission: 0
|
||||||
updated: 2018-12-02 15:13:12
|
updated: 2018-12-02 15:13:12
|
||||||
created: 2018-12-01 15:13:12
|
created: 2018-12-01 15:13:12
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ type Opts struct {
|
||||||
// ContentType represents mail content types
|
// ContentType represents mail content types
|
||||||
type ContentType int
|
type ContentType int
|
||||||
|
|
||||||
// Enumerate all the team rights
|
// Enumerate all the team permissions
|
||||||
const (
|
const (
|
||||||
ContentTypePlain ContentType = iota
|
ContentTypePlain ContentType = iota
|
||||||
ContentTypeHTML
|
ContentTypeHTML
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ type linkSharing20190818210133 struct {
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
|
||||||
Hash string `xorm:"varchar(40) not null unique" json:"hash" param:"hash"`
|
Hash string `xorm:"varchar(40) not null unique" json:"hash" param:"hash"`
|
||||||
ListID int64 `xorm:"int(11) not null" json:"list_id"`
|
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"`
|
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"`
|
SharedByID int64 `xorm:"int(11) INDEX not null"`
|
||||||
Created int64 `xorm:"created not null" json:"created"`
|
Created int64 `xorm:"created not null" json:"created"`
|
||||||
|
|
|
||||||
|
|
@ -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
|
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 {
|
func initSchema(tx *xorm.Engine) error {
|
||||||
schemeBeans := []interface{}{}
|
schemeBeans := []interface{}{}
|
||||||
schemeBeans = append(schemeBeans, models.GetTables()...)
|
schemeBeans = append(schemeBeans, models.GetTables()...)
|
||||||
|
|
|
||||||
|
|
@ -244,9 +244,9 @@ func CanDoAPIRoute(c echo.Context, token *APIToken) (can bool) {
|
||||||
routeGroupName = "other"
|
routeGroupName = "other"
|
||||||
}
|
}
|
||||||
|
|
||||||
group, hasGroup := token.Permissions[routeGroupName]
|
group, hasGroup := token.APIPermissions[routeGroupName]
|
||||||
if !hasGroup {
|
if !hasGroup {
|
||||||
group, hasGroup = token.Permissions[routeParts[0]]
|
group, hasGroup = token.APIPermissions[routeParts[0]]
|
||||||
if !hasGroup {
|
if !hasGroup {
|
||||||
return false
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,12 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"xorm.io/builder"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/utils"
|
"code.vikunja.io/api/pkg/utils"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/web"
|
"code.vikunja.io/api/pkg/web"
|
||||||
|
|
||||||
"golang.org/x/crypto/pbkdf2"
|
"golang.org/x/crypto/pbkdf2"
|
||||||
|
"xorm.io/builder"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -46,7 +45,7 @@ type APIToken struct {
|
||||||
TokenHash string `xorm:"not null unique" json:"-"`
|
TokenHash string `xorm:"not null unique" json:"-"`
|
||||||
TokenLastEight string `xorm:"not null index varchar(8)" 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"]}`.
|
// 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.
|
// The date when this key expires.
|
||||||
ExpiresAt time.Time `xorm:"not null" json:"expires_at" valid:"required"`
|
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:"-"`
|
OwnerID int64 `xorm:"bigint not null" json:"-"`
|
||||||
|
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const APITokenPrefix = `tk_`
|
const APITokenPrefix = `tk_`
|
||||||
|
|
@ -102,7 +101,7 @@ func (t *APIToken) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||||
|
|
||||||
t.OwnerID = a.GetID()
|
t.OwnerID = a.GetID()
|
||||||
|
|
||||||
if err := PermissionsAreValid(t.Permissions); err != nil {
|
if err := PermissionsAreValid(t.APIPermissions); err != nil {
|
||||||
return err
|
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."}
|
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
|
// ErrNoPermissionToSeeTask represents an error where a user does not have the permission to see a task
|
||||||
type ErrNoRightToSeeTask struct {
|
type ErrNoPermissionToSeeTask struct {
|
||||||
TaskID int64
|
TaskID int64
|
||||||
UserID int64
|
UserID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsErrNoRightToSeeTask checks if an error is ErrNoRightToSeeTask.
|
// IsErrNoPermissionToSeeTask checks if an error is ErrNoPermissionToSeeTask.
|
||||||
func IsErrNoRightToSeeTask(err error) bool {
|
func IsErrNoPermissionToSeeTask(err error) bool {
|
||||||
_, ok := err.(ErrNoRightToSeeTask)
|
_, ok := err.(ErrNoPermissionToSeeTask)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err ErrNoRightToSeeTask) Error() string {
|
func (err ErrNoPermissionToSeeTask) Error() string {
|
||||||
return fmt.Sprintf("User does not have the right to see the task [TaskID: %v, ID: %v]", err.TaskID, err.UserID)
|
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
|
// ErrCodeNoRightToSeeTask holds the unique world-error code of this error
|
||||||
const ErrCodeNoRightToSeeTask = 4005
|
const ErrCodeNoRightToSeeTask = 4005
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrNoRightToSeeTask) HTTPError() web.HTTPError {
|
func (err ErrNoPermissionToSeeTask) HTTPError() web.HTTPError {
|
||||||
return web.HTTPError{
|
return web.HTTPError{
|
||||||
HTTPCode: http.StatusForbidden,
|
HTTPCode: http.StatusForbidden,
|
||||||
Code: ErrCodeNoRightToSeeTask,
|
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 {
|
type ErrUserHasNoAccessToLabel struct {
|
||||||
LabelID int64
|
LabelID int64
|
||||||
UserID int64
|
UserID int64
|
||||||
|
|
@ -1526,33 +1526,33 @@ func (err ErrUserHasNoAccessToLabel) HTTPError() web.HTTPError {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========
|
// ========
|
||||||
// Rights
|
// Permissions
|
||||||
// ========
|
// ========
|
||||||
|
|
||||||
// ErrInvalidRight represents an error where a right is invalid
|
// ErrInvalidPermission represents an error where a permission is invalid
|
||||||
type ErrInvalidRight struct {
|
type ErrInvalidPermission struct {
|
||||||
Right Right
|
Permission Permission
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsErrInvalidRight checks if an error is ErrInvalidRight.
|
// IsErrInvalidPermission checks if an error is ErrInvalidPermission.
|
||||||
func IsErrInvalidRight(err error) bool {
|
func IsErrInvalidPermission(err error) bool {
|
||||||
_, ok := err.(ErrInvalidRight)
|
_, ok := err.(ErrInvalidPermission)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err ErrInvalidRight) Error() string {
|
func (err ErrInvalidPermission) Error() string {
|
||||||
return fmt.Sprintf("Right invalid [Right: %d]", err.Right)
|
return fmt.Sprintf("Permission invalid [Permission: %d]", err.Permission)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrCodeInvalidRight holds the unique world-error code of this error
|
// ErrCodeInvalidRight holds the unique world-error code of this error
|
||||||
const ErrCodeInvalidRight = 9001
|
const ErrCodeInvalidRight = 9001
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrInvalidRight) HTTPError() web.HTTPError {
|
func (err ErrInvalidPermission) HTTPError() web.HTTPError {
|
||||||
return web.HTTPError{
|
return web.HTTPError{
|
||||||
HTTPCode: http.StatusBadRequest,
|
HTTPCode: http.StatusBadRequest,
|
||||||
Code: ErrCodeInvalidRight,
|
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
|
// Including the task collection type so we can use task filters on kanban
|
||||||
TaskCollection `xorm:"-" json:"-"`
|
TaskCollection `xorm:"-" json:"-"`
|
||||||
|
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName returns the table name for this bucket.
|
// 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)
|
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) {
|
func (b *Bucket) canDoBucket(s *xorm.Session, a web.Auth) (bool, error) {
|
||||||
bb, err := getBucketByID(s, b.ID)
|
bb, err := getBucketByID(s, b.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -41,8 +41,8 @@ type TaskBucket struct {
|
||||||
ProjectID int64 `xorm:"-" json:"-" param:"project"`
|
ProjectID int64 `xorm:"-" json:"-" param:"project"`
|
||||||
Task *Task `xorm:"-" json:"task"`
|
Task *Task `xorm:"-" json:"task"`
|
||||||
|
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *TaskBucket) TableName() string {
|
func (b *TaskBucket) TableName() string {
|
||||||
|
|
|
||||||
|
|
@ -120,9 +120,9 @@ func TestBucket_ReadAll(t *testing.T) {
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
linkShare := &LinkSharing{
|
linkShare := &LinkSharing{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
Right: RightRead,
|
Permission: PermissionRead,
|
||||||
}
|
}
|
||||||
b := &TaskCollection{
|
b := &TaskCollection{
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,8 @@ type Label struct {
|
||||||
// A timestamp when this label was last updated. You cannot change this value.
|
// A timestamp when this label was last updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated not null" json:"updated"`
|
Updated time.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName makes a pretty table name
|
// 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
|
// 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)
|
linkShare, isLinkShare := a.(*LinkSharing)
|
||||||
|
|
||||||
|
|
@ -93,10 +93,10 @@ func (l *Label) hasAccessToLabel(s *xorm.Session, a web.Auth) (has bool, maxRigh
|
||||||
return
|
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 {
|
if ll.TaskID > 0 {
|
||||||
t := &Task{ID: ll.TaskID}
|
t := &Task{ID: ll.TaskID}
|
||||||
_, maxRight, err = t.CanRead(s, a)
|
_, maxPermission, err = t.CanRead(s, a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -40,8 +40,8 @@ type LabelTask struct {
|
||||||
// A timestamp when this task was created. You cannot change this value.
|
// A timestamp when this task was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName makes a pretty table name
|
// 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"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /tasks/{task}/labels [get]
|
// @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) {
|
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}
|
task := Task{ID: lt.TaskID}
|
||||||
canRead, _, err := task.CanRead(s, a)
|
canRead, _, err := task.CanRead(s, a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
}
|
}
|
||||||
if !canRead {
|
if !canRead {
|
||||||
return nil, 0, 0, ErrNoRightToSeeTask{lt.TaskID, a.GetID()}
|
return nil, 0, 0, ErrNoPermissionToSeeTask{lt.TaskID, a.GetID()}
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetLabelsByTaskIDs(s, &LabelByTaskIDsOptions{
|
return GetLabelsByTaskIDs(s, &LabelByTaskIDsOptions{
|
||||||
|
|
@ -377,7 +377,7 @@ func (t *Task) UpdateTaskLabels(s *xorm.Session, creator web.Auth, labels []*Lab
|
||||||
return err
|
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)
|
hasAccessToLabel, _, err := label.hasAccessToLabel(s, creator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -413,8 +413,8 @@ type LabelTaskBulk struct {
|
||||||
Labels []*Label `json:"labels"`
|
Labels []*Label `json:"labels"`
|
||||||
TaskID int64 `json:"-" param:"projecttask"`
|
TaskID int64 `json:"-" param:"projecttask"`
|
||||||
|
|
||||||
web.CRUDable `json:"-"`
|
web.CRUDable `json:"-"`
|
||||||
web.Rights `json:"-"`
|
web.Permissions `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create updates a bunch of labels on a task at once
|
// Create updates a bunch of labels on a task at once
|
||||||
|
|
|
||||||
|
|
@ -51,12 +51,12 @@ func TestLabelTask_ReadAll(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type fields struct {
|
type fields struct {
|
||||||
ID int64
|
ID int64
|
||||||
TaskID int64
|
TaskID int64
|
||||||
LabelID int64
|
LabelID int64
|
||||||
Created time.Time
|
Created time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Permissions web.Permissions
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
search string
|
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{
|
fields: fields{
|
||||||
TaskID: 14,
|
TaskID: 14,
|
||||||
},
|
},
|
||||||
|
|
@ -95,7 +95,7 @@ func TestLabelTask_ReadAll(t *testing.T) {
|
||||||
a: &user.User{ID: 1},
|
a: &user.User{ID: 1},
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
errType: IsErrNoRightToSeeTask,
|
errType: IsErrNoPermissionToSeeTask,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nonexistant task",
|
name: "nonexistant task",
|
||||||
|
|
@ -131,12 +131,12 @@ func TestLabelTask_ReadAll(t *testing.T) {
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
|
|
||||||
l := &LabelTask{
|
l := &LabelTask{
|
||||||
ID: tt.fields.ID,
|
ID: tt.fields.ID,
|
||||||
TaskID: tt.fields.TaskID,
|
TaskID: tt.fields.TaskID,
|
||||||
LabelID: tt.fields.LabelID,
|
LabelID: tt.fields.LabelID,
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
CRUDable: tt.fields.CRUDable,
|
CRUDable: tt.fields.CRUDable,
|
||||||
Rights: tt.fields.Rights,
|
Permissions: tt.fields.Permissions,
|
||||||
}
|
}
|
||||||
gotLabels, _, _, err := l.ReadAll(s, tt.args.a, tt.args.search, tt.args.page, 0)
|
gotLabels, _, _, err := l.ReadAll(s, tt.args.a, tt.args.search, tt.args.page, 0)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
|
|
@ -157,12 +157,12 @@ func TestLabelTask_ReadAll(t *testing.T) {
|
||||||
|
|
||||||
func TestLabelTask_Create(t *testing.T) {
|
func TestLabelTask_Create(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
ID int64
|
ID int64
|
||||||
TaskID int64
|
TaskID int64
|
||||||
LabelID int64
|
LabelID int64
|
||||||
Created time.Time
|
Created time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Permissions web.Permissions
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
a web.Auth
|
a web.Auth
|
||||||
|
|
@ -229,12 +229,12 @@ func TestLabelTask_Create(t *testing.T) {
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
|
|
||||||
l := &LabelTask{
|
l := &LabelTask{
|
||||||
ID: tt.fields.ID,
|
ID: tt.fields.ID,
|
||||||
TaskID: tt.fields.TaskID,
|
TaskID: tt.fields.TaskID,
|
||||||
LabelID: tt.fields.LabelID,
|
LabelID: tt.fields.LabelID,
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
CRUDable: tt.fields.CRUDable,
|
CRUDable: tt.fields.CRUDable,
|
||||||
Rights: tt.fields.Rights,
|
Permissions: tt.fields.Permissions,
|
||||||
}
|
}
|
||||||
allowed, err := l.CanCreate(s, tt.args.a)
|
allowed, err := l.CanCreate(s, tt.args.a)
|
||||||
if !allowed && !tt.wantForbidden {
|
if !allowed && !tt.wantForbidden {
|
||||||
|
|
@ -261,12 +261,12 @@ func TestLabelTask_Create(t *testing.T) {
|
||||||
|
|
||||||
func TestLabelTask_Delete(t *testing.T) {
|
func TestLabelTask_Delete(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
ID int64
|
ID int64
|
||||||
TaskID int64
|
TaskID int64
|
||||||
LabelID int64
|
LabelID int64
|
||||||
Created time.Time
|
Created time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Permissions web.Permissions
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|
@ -329,12 +329,12 @@ func TestLabelTask_Delete(t *testing.T) {
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
l := &LabelTask{
|
l := &LabelTask{
|
||||||
ID: tt.fields.ID,
|
ID: tt.fields.ID,
|
||||||
TaskID: tt.fields.TaskID,
|
TaskID: tt.fields.TaskID,
|
||||||
LabelID: tt.fields.LabelID,
|
LabelID: tt.fields.LabelID,
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
CRUDable: tt.fields.CRUDable,
|
CRUDable: tt.fields.CRUDable,
|
||||||
Rights: tt.fields.Rights,
|
Permissions: tt.fields.Permissions,
|
||||||
}
|
}
|
||||||
allowed, _ := l.CanDelete(s, tt.auth)
|
allowed, _ := l.CanDelete(s, tt.auth)
|
||||||
if !allowed && !tt.wantForbidden {
|
if !allowed && !tt.wantForbidden {
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ func TestLabel_ReadAll(t *testing.T) {
|
||||||
Created time.Time
|
Created time.Time
|
||||||
Updated time.Time
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Permissions web.Permissions
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
search string
|
search string
|
||||||
|
|
@ -146,7 +146,7 @@ func TestLabel_ReadAll(t *testing.T) {
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
Updated: tt.fields.Updated,
|
Updated: tt.fields.Updated,
|
||||||
CRUDable: tt.fields.CRUDable,
|
CRUDable: tt.fields.CRUDable,
|
||||||
Rights: tt.fields.Rights,
|
Permissions: tt.fields.Permissions,
|
||||||
}
|
}
|
||||||
db.LoadAndAssertFixtures(t)
|
db.LoadAndAssertFixtures(t)
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
|
|
@ -177,7 +177,7 @@ func TestLabel_ReadOne(t *testing.T) {
|
||||||
Created time.Time
|
Created time.Time
|
||||||
Updated time.Time
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Permissions web.Permissions
|
||||||
}
|
}
|
||||||
user1 := &user.User{
|
user1 := &user.User{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
|
|
@ -226,7 +226,7 @@ func TestLabel_ReadOne(t *testing.T) {
|
||||||
auth: &user.User{ID: 1},
|
auth: &user.User{ID: 1},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no rights",
|
name: "no permissions",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
ID: 3,
|
ID: 3,
|
||||||
},
|
},
|
||||||
|
|
@ -272,7 +272,7 @@ func TestLabel_ReadOne(t *testing.T) {
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
Updated: tt.fields.Updated,
|
Updated: tt.fields.Updated,
|
||||||
CRUDable: tt.fields.CRUDable,
|
CRUDable: tt.fields.CRUDable,
|
||||||
Rights: tt.fields.Rights,
|
Permissions: tt.fields.Permissions,
|
||||||
}
|
}
|
||||||
|
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
|
|
@ -308,7 +308,7 @@ func TestLabel_Create(t *testing.T) {
|
||||||
Created time.Time
|
Created time.Time
|
||||||
Updated time.Time
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Permissions web.Permissions
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
a web.Auth
|
a web.Auth
|
||||||
|
|
@ -344,7 +344,7 @@ func TestLabel_Create(t *testing.T) {
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
Updated: tt.fields.Updated,
|
Updated: tt.fields.Updated,
|
||||||
CRUDable: tt.fields.CRUDable,
|
CRUDable: tt.fields.CRUDable,
|
||||||
Rights: tt.fields.Rights,
|
Permissions: tt.fields.Permissions,
|
||||||
}
|
}
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
allowed, _ := l.CanCreate(s, tt.args.a)
|
allowed, _ := l.CanCreate(s, tt.args.a)
|
||||||
|
|
@ -378,7 +378,7 @@ func TestLabel_Update(t *testing.T) {
|
||||||
Created time.Time
|
Created time.Time
|
||||||
Updated time.Time
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Permissions web.Permissions
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|
@ -406,7 +406,7 @@ func TestLabel_Update(t *testing.T) {
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no rights",
|
name: "no permissions",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
ID: 3,
|
ID: 3,
|
||||||
Title: "new and better",
|
Title: "new and better",
|
||||||
|
|
@ -415,7 +415,7 @@ func TestLabel_Update(t *testing.T) {
|
||||||
wantForbidden: true,
|
wantForbidden: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no rights other creator but access",
|
name: "no permissions other creator but access",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
ID: 4,
|
ID: 4,
|
||||||
Title: "new and better",
|
Title: "new and better",
|
||||||
|
|
@ -436,7 +436,7 @@ func TestLabel_Update(t *testing.T) {
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
Updated: tt.fields.Updated,
|
Updated: tt.fields.Updated,
|
||||||
CRUDable: tt.fields.CRUDable,
|
CRUDable: tt.fields.CRUDable,
|
||||||
Rights: tt.fields.Rights,
|
Permissions: tt.fields.Permissions,
|
||||||
}
|
}
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
allowed, _ := l.CanUpdate(s, tt.auth)
|
allowed, _ := l.CanUpdate(s, tt.auth)
|
||||||
|
|
@ -468,7 +468,7 @@ func TestLabel_Delete(t *testing.T) {
|
||||||
Created time.Time
|
Created time.Time
|
||||||
Updated time.Time
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Permissions web.Permissions
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
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.
|
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{
|
fields: fields{
|
||||||
ID: 3,
|
ID: 3,
|
||||||
},
|
},
|
||||||
|
|
@ -502,7 +502,7 @@ func TestLabel_Delete(t *testing.T) {
|
||||||
wantForbidden: true,
|
wantForbidden: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no rights but visible",
|
name: "no permissions but visible",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
ID: 4,
|
ID: 4,
|
||||||
},
|
},
|
||||||
|
|
@ -522,7 +522,7 @@ func TestLabel_Delete(t *testing.T) {
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
Updated: tt.fields.Updated,
|
Updated: tt.fields.Updated,
|
||||||
CRUDable: tt.fields.CRUDable,
|
CRUDable: tt.fields.CRUDable,
|
||||||
Rights: tt.fields.Rights,
|
Permissions: tt.fields.Permissions,
|
||||||
}
|
}
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
allowed, _ := l.CanDelete(s, tt.auth)
|
allowed, _ := l.CanDelete(s, tt.auth)
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,8 @@ type LinkSharing struct {
|
||||||
Name string `xorm:"text null" json:"name"`
|
Name string `xorm:"text null" json:"name"`
|
||||||
// The ID of the shared project
|
// The ID of the shared project
|
||||||
ProjectID int64 `xorm:"bigint not null" json:"-" param:"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.
|
// The permission 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"`
|
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.
|
// 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"`
|
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.
|
// A timestamp when this share was last updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated not null" json:"updated"`
|
Updated time.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName holds the table name
|
// 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.ID = int64(claims["id"].(float64))
|
||||||
share.Hash = claims["hash"].(string)
|
share.Hash = claims["hash"].(string)
|
||||||
share.ProjectID = int64(projectID)
|
share.ProjectID = int64(projectID)
|
||||||
share.Right = Right(claims["right"].(float64))
|
share.Permission = Permission(claims["permission"].(float64))
|
||||||
share.SharedByID = int64(claims["sharedByID"].(float64))
|
share.SharedByID = int64(claims["sharedByID"].(float64))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +138,7 @@ func (share *LinkSharing) toUser() *user.User {
|
||||||
// @Router /projects/{project}/shares [put]
|
// @Router /projects/{project}/shares [put]
|
||||||
func (share *LinkSharing) Create(s *xorm.Session, a web.Auth) (err error) {
|
func (share *LinkSharing) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||||
|
|
||||||
err = share.Right.isValid()
|
err = share.Permission.isValid()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import (
|
||||||
"xorm.io/xorm"
|
"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) {
|
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
|
// Don't allow creating link shares if the user itself authenticated with a link share
|
||||||
if _, is := a.(*LinkSharing); is {
|
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)
|
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) {
|
func (share *LinkSharing) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||||
return share.canDoLinkShare(s, a)
|
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) {
|
func (share *LinkSharing) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||||
return share.canDoLinkShare(s, a)
|
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) {
|
func (share *LinkSharing) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||||
return share.canDoLinkShare(s, a)
|
return share.canDoLinkShare(s, a)
|
||||||
}
|
}
|
||||||
|
|
@ -61,8 +61,8 @@ func (share *LinkSharing) canDoLinkShare(s *xorm.Session, a web.Auth) (bool, err
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the user is admin when the link right is admin
|
// Check if the user is admin when the link permission is admin
|
||||||
if share.Right == RightAdmin {
|
if share.Permission == PermissionAdmin {
|
||||||
return l.IsAdmin(s, a)
|
return l.IsAdmin(s, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,8 +36,8 @@ func TestLinkSharing_Create(t *testing.T) {
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
share := &LinkSharing{
|
share := &LinkSharing{
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
Right: RightRead,
|
Permission: PermissionRead,
|
||||||
}
|
}
|
||||||
err := share.Create(s, doer)
|
err := share.Create(s, doer)
|
||||||
|
|
||||||
|
|
@ -49,19 +49,19 @@ func TestLinkSharing_Create(t *testing.T) {
|
||||||
"id": share.ID,
|
"id": share.ID,
|
||||||
}, false)
|
}, false)
|
||||||
})
|
})
|
||||||
t.Run("invalid right", func(t *testing.T) {
|
t.Run("invalid permission", func(t *testing.T) {
|
||||||
db.LoadAndAssertFixtures(t)
|
db.LoadAndAssertFixtures(t)
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
share := &LinkSharing{
|
share := &LinkSharing{
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
Right: Right(123),
|
Permission: Permission(123),
|
||||||
}
|
}
|
||||||
err := share.Create(s, doer)
|
err := share.Create(s, doer)
|
||||||
|
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
assert.True(t, IsErrInvalidRight(err))
|
assert.True(t, IsErrInvalidPermission(err))
|
||||||
})
|
})
|
||||||
t.Run("password should be hashed", func(t *testing.T) {
|
t.Run("password should be hashed", func(t *testing.T) {
|
||||||
db.LoadAndAssertFixtures(t)
|
db.LoadAndAssertFixtures(t)
|
||||||
|
|
@ -69,9 +69,9 @@ func TestLinkSharing_Create(t *testing.T) {
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
share := &LinkSharing{
|
share := &LinkSharing{
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
Right: RightRead,
|
Permission: PermissionRead,
|
||||||
Password: "somePassword",
|
Password: "somePassword",
|
||||||
}
|
}
|
||||||
err := share.Create(s, doer)
|
err := share.Create(s, doer)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@ type DatabaseNotifications struct {
|
||||||
// True is read, false is unread.
|
// True is read, false is unread.
|
||||||
Read bool `xorm:"-" json:"read"`
|
Read bool `xorm:"-" json:"read"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadAll returns all database notifications for a user
|
// ReadAll returns all database notifications for a user
|
||||||
|
|
|
||||||
|
|
@ -22,38 +22,38 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Right defines the rights users/teams can have for projects
|
// Permission defines the permissions users/teams can have for projects
|
||||||
type Right int
|
type Permission int
|
||||||
|
|
||||||
// define unknown right
|
// define unknown permission
|
||||||
const (
|
const (
|
||||||
RightUnknown = -1
|
PermissionUnknown = -1
|
||||||
// Can read projects in a
|
// 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.
|
// Can write in a like projects and tasks. Cannot create new projects.
|
||||||
RightWrite
|
PermissionWrite
|
||||||
// Can manage a project, can do everything
|
// Can manage a project, can do everything
|
||||||
RightAdmin
|
PermissionAdmin
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r Right) isValid() error {
|
func (r Permission) isValid() error {
|
||||||
if r != RightAdmin && r != RightRead && r != RightWrite {
|
if r != PermissionAdmin && r != PermissionRead && r != PermissionWrite {
|
||||||
return ErrInvalidRight{r}
|
return ErrInvalidPermission{r}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON marshals the enum as a quoted json string
|
// MarshalJSON marshals the enum as a quoted json string
|
||||||
func (r Right) MarshalJSON() ([]byte, error) {
|
func (r Permission) MarshalJSON() ([]byte, error) {
|
||||||
if r == RightUnknown {
|
if r == PermissionUnknown {
|
||||||
return []byte(`null`), nil
|
return []byte(`null`), nil
|
||||||
}
|
}
|
||||||
return []byte(strconv.Itoa(int(r))), nil
|
return []byte(strconv.Itoa(int(r))), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON unmarshals a quoted json string to the enum value
|
// 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
|
var s int
|
||||||
if err := json.Unmarshal(data, &s); err != nil {
|
if err := json.Unmarshal(data, &s); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -61,15 +61,15 @@ func (r *Right) UnmarshalJSON(data []byte) error {
|
||||||
|
|
||||||
switch s {
|
switch s {
|
||||||
case -1:
|
case -1:
|
||||||
*r = RightUnknown
|
*r = PermissionUnknown
|
||||||
case 0:
|
case 0:
|
||||||
*r = RightRead
|
*r = PermissionRead
|
||||||
case 1:
|
case 1:
|
||||||
*r = RightWrite
|
*r = PermissionWrite
|
||||||
case 2:
|
case 2:
|
||||||
*r = RightAdmin
|
*r = PermissionAdmin
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid Right %q", s)
|
return fmt.Errorf("invalid Permission %q", s)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -77,21 +77,21 @@ type Project struct {
|
||||||
|
|
||||||
Views []*ProjectView `xorm:"-" json:"views"`
|
Views []*ProjectView `xorm:"-" json:"views"`
|
||||||
|
|
||||||
Expand ProjectExpandable `xorm:"-" json:"-" query:"expand"`
|
Expand ProjectExpandable `xorm:"-" json:"-" query:"expand"`
|
||||||
MaxRight Right `xorm:"-" json:"max_right"`
|
MaxPermission Permission `xorm:"-" json:"max_permission"`
|
||||||
|
|
||||||
// A timestamp when this project was created. You cannot change this value.
|
// A timestamp when this project was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this project was last updated. You cannot change this value.
|
// A timestamp when this project was last updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated not null" json:"updated"`
|
Updated time.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProjectExpandable string
|
type ProjectExpandable string
|
||||||
|
|
||||||
const ProjectExpandableRights = `rights`
|
const ProjectExpandableRights = `permissions`
|
||||||
|
|
||||||
type ProjectWithTasksAndBuckets struct {
|
type ProjectWithTasksAndBuckets struct {
|
||||||
Project
|
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 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 s query string false "Search projects by title."
|
||||||
// @Param is_archived query bool false "If true, also returns all archived projects."
|
// @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
|
// @Security JWTKeyAuth
|
||||||
// @Success 200 {array} models.Project "The projects"
|
// @Success 200 {array} models.Project "The projects"
|
||||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the project"
|
// @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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = addMaxRightToProjects(s, prs, doer)
|
err = addMaxPermissionToProjects(s, prs, doer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, pr := range prs {
|
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
|
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))
|
projectIDs := make([]int64, 0, len(projects))
|
||||||
for _, project := range projects {
|
for _, project := range projects {
|
||||||
if GetSavedFilterIDFromProjectID(project.ID) > 0 {
|
if GetSavedFilterIDFromProjectID(project.ID) > 0 {
|
||||||
project.MaxRight = RightAdmin
|
project.MaxPermission = PermissionAdmin
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
projectIDs = append(projectIDs, project.ID)
|
projectIDs = append(projectIDs, project.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
rights, err := checkRightsForProjects(s, u, projectIDs)
|
permissions, err := checkPermissionsForProjects(s, u, projectIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, project := range projects {
|
for _, project := range projects {
|
||||||
right, has := rights[project.ID]
|
permission, has := permissions[project.ID]
|
||||||
if has {
|
if has {
|
||||||
project.MaxRight = right.MaxRight
|
project.MaxPermission = permission.MaxPermission
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,11 +35,11 @@ type ProjectDuplicate struct {
|
||||||
// The copied project
|
// The copied project
|
||||||
Project *Project `json:"duplicated_project,omitempty"`
|
Project *Project `json:"duplicated_project,omitempty"`
|
||||||
|
|
||||||
web.Rights `json:"-"`
|
web.Permissions `json:"-"`
|
||||||
web.CRUDable `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) {
|
func (pd *ProjectDuplicate) CanCreate(s *xorm.Session, a web.Auth) (canCreate bool, err error) {
|
||||||
// Project Exists + user has read access to project
|
// Project Exists + user has read access to project
|
||||||
pd.Project = &Project{ID: pd.ProjectID}
|
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
|
// Create duplicates a project
|
||||||
// @Summary Duplicate an existing 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
|
// @tags project
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
|
|
@ -114,8 +114,8 @@ func (pd *ProjectDuplicate) Create(s *xorm.Session, doer web.Auth) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rights / Shares
|
// Permissions / Shares
|
||||||
// To keep it simple(r) we will only copy rights which are directly used with the project, not the parent
|
// To keep it simple(r) we will only copy permissions which are directly used with the project, not the parent
|
||||||
users := []*ProjectUser{}
|
users := []*ProjectUser{}
|
||||||
err = s.Where("project_id = ?", pd.ProjectID).Find(&users)
|
err = s.Where("project_id = ?", pd.ProjectID).Find(&users)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/api/pkg/utils"
|
"code.vikunja.io/api/pkg/utils"
|
||||||
"code.vikunja.io/api/pkg/web"
|
"code.vikunja.io/api/pkg/web"
|
||||||
|
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -33,7 +34,7 @@ func (p *Project) CanWrite(s *xorm.Session, a web.Auth) (bool, error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the project and check the right
|
// Get the project and check the permission
|
||||||
originalProject, err := GetProjectSimpleByID(s, p.ID)
|
originalProject, err := GetProjectSimpleByID(s, p.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|
@ -49,7 +50,7 @@ func (p *Project) CanWrite(s *xorm.Session, a web.Auth) (bool, error) {
|
||||||
shareAuth, ok := a.(*LinkSharing)
|
shareAuth, ok := a.(*LinkSharing)
|
||||||
if ok {
|
if ok {
|
||||||
return originalProject.ID == shareAuth.ProjectID &&
|
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()}
|
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
|
return canWrite, errIsArchived
|
||||||
}
|
}
|
||||||
|
|
||||||
canWrite, _, err = originalProject.checkRight(s, u, RightWrite, RightAdmin)
|
canWrite, _, err = originalProject.checkPermission(s, u, PermissionWrite, PermissionAdmin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +83,7 @@ func (p *Project) CanRead(s *xorm.Session, a web.Auth) (bool, int, error) {
|
||||||
|
|
||||||
*p = FavoritesPseudoProject
|
*p = FavoritesPseudoProject
|
||||||
p.Owner = owner
|
p.Owner = owner
|
||||||
return true, int(RightRead), nil
|
return true, int(PermissionRead), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saved Filter Projects need a special case
|
// 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)
|
shareAuth, ok := a.(*LinkSharing)
|
||||||
if ok {
|
if ok {
|
||||||
return p.ID == shareAuth.ProjectID &&
|
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
|
// 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
|
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) {
|
func (p *Project) IsAdmin(s *xorm.Session, a web.Auth) (bool, error) {
|
||||||
// The favorite project can't be edited
|
// The favorite project can't be edited
|
||||||
if p.ID == FavoritesPseudoProject.ID {
|
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
|
// Check if we're dealing with a share auth
|
||||||
shareAuth, ok := a.(*LinkSharing)
|
shareAuth, ok := a.(*LinkSharing)
|
||||||
if ok {
|
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()}
|
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) {
|
if originalProject.isOwner(u) {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
is, _, err := originalProject.checkRight(s, u, RightAdmin)
|
is, _, err := originalProject.checkPermission(s, u, PermissionAdmin)
|
||||||
return is, err
|
return is, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,33 +211,33 @@ func (p *Project) isOwner(u *user.User) bool {
|
||||||
return p.OwnerID == u.ID
|
return p.OwnerID == u.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks n different rights for any given user
|
// Checks n different permissions for any given user
|
||||||
func (p *Project) checkRight(s *xorm.Session, u *user.User, rights ...Right) (bool, int, error) {
|
func (p *Project) checkPermission(s *xorm.Session, u *user.User, permissions ...Permission) (bool, int, error) {
|
||||||
projectRights, err := checkRightsForProjects(s, u, []int64{p.ID})
|
projectPermissions, err := checkPermissionsForProjects(s, u, []int64{p.ID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, 0, err
|
return false, 0, err
|
||||||
}
|
}
|
||||||
right, has := projectRights[p.ID]
|
permission, has := projectPermissions[p.ID]
|
||||||
if !has {
|
if !has {
|
||||||
return false, 0, nil
|
return false, 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range rights {
|
for _, r := range permissions {
|
||||||
if r == right.MaxRight {
|
if r == permission.MaxPermission {
|
||||||
return true, int(right.MaxRight), nil
|
return true, int(permission.MaxPermission), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false, 0, nil
|
return false, 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type projectRight struct {
|
type projectPermission struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
MaxRight Right
|
MaxPermission Permission
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkRightsForProjects(s *xorm.Session, u *user.User, projectIDs []int64) (projectRightMap map[int64]*projectRight, err error) {
|
func checkPermissionsForProjects(s *xorm.Session, u *user.User, projectIDs []int64) (projectPermissionMap map[int64]*projectPermission, err error) {
|
||||||
projectRightMap = make(map[int64]*projectRight)
|
projectPermissionMap = make(map[int64]*projectPermission)
|
||||||
|
|
||||||
if len(projectIDs) < 1 {
|
if len(projectIDs) < 1 {
|
||||||
return
|
return
|
||||||
|
|
@ -277,9 +278,9 @@ WITH RECURSIVE
|
||||||
ph.original_project_id,
|
ph.original_project_id,
|
||||||
CASE
|
CASE
|
||||||
WHEN p.owner_id = ? THEN 2
|
WHEN p.owner_id = ? THEN 2
|
||||||
WHEN COALESCE(ul.right, 0) > COALESCE(tl.right, 0) THEN ul.right
|
WHEN COALESCE(ul.permission, 0) > COALESCE(tl.permission, 0) THEN ul.permission
|
||||||
ELSE COALESCE(tl.right, 0)
|
ELSE COALESCE(tl.permission, 0)
|
||||||
END AS project_right,
|
END AS project_permission,
|
||||||
CASE
|
CASE
|
||||||
WHEN p.owner_id = ? THEN 1 -- Direct project ownership
|
WHEN p.owner_id = ? THEN 1 -- Direct project ownership
|
||||||
ELSE ph.level + 1 -- Derived from parent project
|
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 = ?)
|
WHERE p.owner_id = ? OR ul.user_id = ? OR tm.user_id = ?)
|
||||||
|
|
||||||
SELECT ph.original_project_id AS 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
|
FROM project_hierarchy ph
|
||||||
LEFT JOIN (SELECT *,
|
LEFT JOIN (SELECT *,
|
||||||
ROW_NUMBER() OVER (PARTITION BY original_project_id ORDER BY priority) AS rn
|
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
|
FROM project_permissions) pp ON ph.id = pp.id AND pp.rn = 1
|
||||||
GROUP BY ph.original_project_id`, args...).
|
GROUP BY ph.original_project_id`, args...).
|
||||||
Find(&projectRightMap)
|
Find(&projectPermissionMap)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -34,16 +34,16 @@ type TeamProject struct {
|
||||||
TeamID int64 `xorm:"bigint not null INDEX" json:"team_id" param:"team"`
|
TeamID int64 `xorm:"bigint not null INDEX" json:"team_id" param:"team"`
|
||||||
// The project id.
|
// The project id.
|
||||||
ProjectID int64 `xorm:"bigint not null INDEX" json:"-" param:"project"`
|
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.
|
// The permission 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"`
|
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.
|
// A timestamp when this relation was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this relation was last updated. You cannot change this value.
|
// A timestamp when this relation was last updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated not null" json:"updated"`
|
Updated time.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName makes beautiful table names
|
// TableName makes beautiful table names
|
||||||
|
|
@ -51,10 +51,10 @@ func (*TeamProject) TableName() string {
|
||||||
return "team_projects"
|
return "team_projects"
|
||||||
}
|
}
|
||||||
|
|
||||||
// TeamWithRight represents a team, combined with rights.
|
// TeamWithPermission represents a team, combined with permissions.
|
||||||
type TeamWithRight struct {
|
type TeamWithPermission struct {
|
||||||
Team `xorm:"extends"`
|
Team `xorm:"extends"`
|
||||||
Right Right `json:"right"`
|
Permission Permission `json:"permission"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates a new team <-> project relation
|
// Create creates a new team <-> project relation
|
||||||
|
|
@ -74,8 +74,8 @@ type TeamWithRight struct {
|
||||||
// @Router /projects/{id}/teams [put]
|
// @Router /projects/{id}/teams [put]
|
||||||
func (tl *TeamProject) Create(s *xorm.Session, a web.Auth) (err error) {
|
func (tl *TeamProject) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||||
|
|
||||||
// Check if the rights are valid
|
// Check if the permissions are valid
|
||||||
if err = tl.Right.isValid(); err != nil {
|
if err = tl.Permission.isValid(); err != nil {
|
||||||
return
|
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 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."
|
// @Param s query string false "Search teams by its name."
|
||||||
// @Security JWTKeyAuth
|
// @Security JWTKeyAuth
|
||||||
// @Success 200 {array} models.TeamWithRight "The teams with their right."
|
// @Success 200 {array} models.TeamWithPermission "The teams with their permission."
|
||||||
// @Failure 403 {object} web.HTTPError "No right to see the project."
|
// @Failure 403 {object} web.HTTPError "No permission to see the project."
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /projects/{id}/teams [get]
|
// @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) {
|
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)
|
limit, start := getLimitFromPageIndex(page, perPage)
|
||||||
|
|
||||||
// Get the teams
|
// Get the teams
|
||||||
all := []*TeamWithRight{}
|
all := []*TeamWithPermission{}
|
||||||
query := s.
|
query := s.
|
||||||
Table("teams").
|
Table("teams").
|
||||||
Join("INNER", "team_projects", "team_id = teams.id").
|
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").
|
Join("INNER", "team_projects", "team_id = teams.id").
|
||||||
Where("team_projects.project_id = ?", tl.ProjectID).
|
Where("team_projects.project_id = ?", tl.ProjectID).
|
||||||
Where("teams.name LIKE ?", "%"+search+"%").
|
Where("teams.name LIKE ?", "%"+search+"%").
|
||||||
Count(&TeamWithRight{})
|
Count(&TeamWithPermission{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
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
|
// Update updates a team <-> project relation
|
||||||
// @Summary Update 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
|
// @tags sharing
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce 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]
|
// @Router /projects/{projectID}/teams/{teamID} [post]
|
||||||
func (tl *TeamProject) Update(s *xorm.Session, _ web.Auth) (err error) {
|
func (tl *TeamProject) Update(s *xorm.Session, _ web.Auth) (err error) {
|
||||||
|
|
||||||
// Check if the right is valid
|
// Check if the permission is valid
|
||||||
if err := tl.Right.isValid(); err != nil {
|
if err := tl.Permission.isValid(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = s.
|
_, err = s.
|
||||||
Where("project_id = ? AND team_id = ?", tl.ProjectID, tl.TeamID).
|
Where("project_id = ? AND team_id = ?", tl.ProjectID, tl.TeamID).
|
||||||
Cols("right").
|
Cols("permission").
|
||||||
Update(tl)
|
Update(tl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -60,9 +60,9 @@ func TestTeamProject_ReadAll(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("no access", func(t *testing.T) {
|
t.Run("no access", func(t *testing.T) {
|
||||||
tl := TeamProject{
|
tl := TeamProject{
|
||||||
TeamID: 1,
|
TeamID: 1,
|
||||||
ProjectID: 5,
|
ProjectID: 5,
|
||||||
Right: RightAdmin,
|
Permission: PermissionAdmin,
|
||||||
}
|
}
|
||||||
db.LoadAndAssertFixtures(t)
|
db.LoadAndAssertFixtures(t)
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
|
|
@ -80,7 +80,7 @@ func TestTeamProject_ReadAll(t *testing.T) {
|
||||||
teams, _, _, err := tl.ReadAll(s, u, "TEAM9", 1, 50)
|
teams, _, _, err := tl.ReadAll(s, u, "TEAM9", 1, 50)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, reflect.Slice, reflect.TypeOf(teams).Kind())
|
assert.Equal(t, reflect.Slice, reflect.TypeOf(teams).Kind())
|
||||||
ts := teams.([]*TeamWithRight)
|
ts := teams.([]*TeamWithPermission)
|
||||||
assert.Len(t, ts, 1)
|
assert.Len(t, ts, 1)
|
||||||
assert.Equal(t, int64(9), ts[0].ID)
|
assert.Equal(t, int64(9), ts[0].ID)
|
||||||
_ = s.Close()
|
_ = s.Close()
|
||||||
|
|
@ -93,9 +93,9 @@ func TestTeamProject_Create(t *testing.T) {
|
||||||
db.LoadAndAssertFixtures(t)
|
db.LoadAndAssertFixtures(t)
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
tl := TeamProject{
|
tl := TeamProject{
|
||||||
TeamID: 1,
|
TeamID: 1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
Right: RightAdmin,
|
Permission: PermissionAdmin,
|
||||||
}
|
}
|
||||||
allowed, _ := tl.CanCreate(s, u)
|
allowed, _ := tl.CanCreate(s, u)
|
||||||
assert.True(t, allowed)
|
assert.True(t, allowed)
|
||||||
|
|
@ -106,33 +106,33 @@ func TestTeamProject_Create(t *testing.T) {
|
||||||
db.AssertExists(t, "team_projects", map[string]interface{}{
|
db.AssertExists(t, "team_projects", map[string]interface{}{
|
||||||
"team_id": 1,
|
"team_id": 1,
|
||||||
"project_id": 1,
|
"project_id": 1,
|
||||||
"right": RightAdmin,
|
"permission": PermissionAdmin,
|
||||||
}, false)
|
}, false)
|
||||||
})
|
})
|
||||||
t.Run("team already has access", func(t *testing.T) {
|
t.Run("team already has access", func(t *testing.T) {
|
||||||
db.LoadAndAssertFixtures(t)
|
db.LoadAndAssertFixtures(t)
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
tl := TeamProject{
|
tl := TeamProject{
|
||||||
TeamID: 1,
|
TeamID: 1,
|
||||||
ProjectID: 3,
|
ProjectID: 3,
|
||||||
Right: RightAdmin,
|
Permission: PermissionAdmin,
|
||||||
}
|
}
|
||||||
err := tl.Create(s, u)
|
err := tl.Create(s, u)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
assert.True(t, IsErrTeamAlreadyHasAccess(err))
|
assert.True(t, IsErrTeamAlreadyHasAccess(err))
|
||||||
_ = s.Close()
|
_ = s.Close()
|
||||||
})
|
})
|
||||||
t.Run("wrong rights", func(t *testing.T) {
|
t.Run("wrong permissions", func(t *testing.T) {
|
||||||
db.LoadAndAssertFixtures(t)
|
db.LoadAndAssertFixtures(t)
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
tl := TeamProject{
|
tl := TeamProject{
|
||||||
TeamID: 1,
|
TeamID: 1,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
Right: RightUnknown,
|
Permission: PermissionUnknown,
|
||||||
}
|
}
|
||||||
err := tl.Create(s, u)
|
err := tl.Create(s, u)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
assert.True(t, IsErrInvalidRight(err))
|
assert.True(t, IsErrInvalidPermission(err))
|
||||||
_ = s.Close()
|
_ = s.Close()
|
||||||
})
|
})
|
||||||
t.Run("nonexistant team", func(t *testing.T) {
|
t.Run("nonexistant team", func(t *testing.T) {
|
||||||
|
|
@ -208,14 +208,14 @@ func TestTeamProject_Delete(t *testing.T) {
|
||||||
|
|
||||||
func TestTeamProject_Update(t *testing.T) {
|
func TestTeamProject_Update(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
ID int64
|
ID int64
|
||||||
TeamID int64
|
TeamID int64
|
||||||
ProjectID int64
|
ProjectID int64
|
||||||
Right Right
|
Permission Permission
|
||||||
Created time.Time
|
Created time.Time
|
||||||
Updated time.Time
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Permissions web.Permissions
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|
@ -226,36 +226,36 @@ func TestTeamProject_Update(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "Test Update Normally",
|
name: "Test Update Normally",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
ProjectID: 3,
|
ProjectID: 3,
|
||||||
TeamID: 1,
|
TeamID: 1,
|
||||||
Right: RightAdmin,
|
Permission: PermissionAdmin,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Test Update to write",
|
name: "Test Update to write",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
ProjectID: 3,
|
ProjectID: 3,
|
||||||
TeamID: 1,
|
TeamID: 1,
|
||||||
Right: RightWrite,
|
Permission: PermissionWrite,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Test Update to Read",
|
name: "Test Update to Read",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
ProjectID: 3,
|
ProjectID: 3,
|
||||||
TeamID: 1,
|
TeamID: 1,
|
||||||
Right: RightRead,
|
Permission: PermissionRead,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Test Update with invalid right",
|
name: "Test Update with invalid permission",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
ProjectID: 3,
|
ProjectID: 3,
|
||||||
TeamID: 1,
|
TeamID: 1,
|
||||||
Right: 500,
|
Permission: 500,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
errType: IsErrInvalidRight,
|
errType: IsErrInvalidPermission,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
@ -264,14 +264,14 @@ func TestTeamProject_Update(t *testing.T) {
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
|
|
||||||
tl := &TeamProject{
|
tl := &TeamProject{
|
||||||
ID: tt.fields.ID,
|
ID: tt.fields.ID,
|
||||||
TeamID: tt.fields.TeamID,
|
TeamID: tt.fields.TeamID,
|
||||||
ProjectID: tt.fields.ProjectID,
|
ProjectID: tt.fields.ProjectID,
|
||||||
Right: tt.fields.Right,
|
Permission: tt.fields.Permission,
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
Updated: tt.fields.Updated,
|
Updated: tt.fields.Updated,
|
||||||
CRUDable: tt.fields.CRUDable,
|
CRUDable: tt.fields.CRUDable,
|
||||||
Rights: tt.fields.Rights,
|
Permissions: tt.fields.Permissions,
|
||||||
}
|
}
|
||||||
err := tl.Update(s, &user.User{ID: 1})
|
err := tl.Update(s, &user.User{ID: 1})
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
|
|
@ -286,7 +286,7 @@ func TestTeamProject_Update(t *testing.T) {
|
||||||
db.AssertExists(t, "team_projects", map[string]interface{}{
|
db.AssertExists(t, "team_projects", map[string]interface{}{
|
||||||
"project_id": tt.fields.ProjectID,
|
"project_id": tt.fields.ProjectID,
|
||||||
"team_id": tt.fields.TeamID,
|
"team_id": tt.fields.TeamID,
|
||||||
"right": tt.fields.Right,
|
"permission": tt.fields.Permission,
|
||||||
}, false)
|
}, false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -37,16 +37,16 @@ type ProjectUser struct {
|
||||||
UserID int64 `xorm:"bigint not null INDEX" json:"-"`
|
UserID int64 `xorm:"bigint not null INDEX" json:"-"`
|
||||||
// The project id.
|
// The project id.
|
||||||
ProjectID int64 `xorm:"bigint not null INDEX" json:"-" param:"project"`
|
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.
|
// The permission 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"`
|
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.
|
// A timestamp when this relation was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this relation was last updated. You cannot change this value.
|
// A timestamp when this relation was last updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated not null" json:"updated"`
|
Updated time.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName is the table name for ProjectUser
|
// TableName is the table name for ProjectUser
|
||||||
|
|
@ -54,10 +54,10 @@ func (*ProjectUser) TableName() string {
|
||||||
return "users_projects"
|
return "users_projects"
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserWithRight represents a user in combination with the right it can have on a project
|
// UserWithPermission represents a user in combination with the permission it can have on a project
|
||||||
type UserWithRight struct {
|
type UserWithPermission struct {
|
||||||
user.User `xorm:"extends"`
|
user.User `xorm:"extends"`
|
||||||
Right Right `json:"right"`
|
Permission Permission `json:"permission"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates a new project <-> user relation
|
// Create creates a new project <-> user relation
|
||||||
|
|
@ -77,8 +77,8 @@ type UserWithRight struct {
|
||||||
// @Router /projects/{id}/users [put]
|
// @Router /projects/{id}/users [put]
|
||||||
func (lu *ProjectUser) Create(s *xorm.Session, a web.Auth) (err error) {
|
func (lu *ProjectUser) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||||
|
|
||||||
// Check if the right is valid
|
// Check if the permission is valid
|
||||||
if err := lu.Right.isValid(); err != nil {
|
if err := lu.Permission.isValid(); err != nil {
|
||||||
return err
|
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 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."
|
// @Param s query string false "Search users by its name."
|
||||||
// @Security JWTKeyAuth
|
// @Security JWTKeyAuth
|
||||||
// @Success 200 {array} models.UserWithRight "The users with the right they have."
|
// @Success 200 {array} models.UserWithPermission "The users with the permission they have."
|
||||||
// @Failure 403 {object} web.HTTPError "No right to see the project."
|
// @Failure 403 {object} web.HTTPError "No permission to see the project."
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /projects/{id}/users [get]
|
// @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) {
|
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)
|
limit, start := getLimitFromPageIndex(page, perPage)
|
||||||
|
|
||||||
// Get all users
|
// Get all users
|
||||||
all := []*UserWithRight{}
|
all := []*UserWithPermission{}
|
||||||
query := s.
|
query := s.
|
||||||
Join("INNER", "users_projects", "user_id = users.id").
|
Join("INNER", "users_projects", "user_id = users.id").
|
||||||
Where("users_projects.project_id = ?", lu.ProjectID).
|
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").
|
Join("INNER", "users_projects", "user_id = users.id").
|
||||||
Where("users_projects.project_id = ?", lu.ProjectID).
|
Where("users_projects.project_id = ?", lu.ProjectID).
|
||||||
Where("users.username LIKE ?", "%"+search+"%").
|
Where("users.username LIKE ?", "%"+search+"%").
|
||||||
Count(&UserWithRight{})
|
Count(&UserWithPermission{})
|
||||||
|
|
||||||
return all, len(all), numberOfTotalItems, err
|
return all, len(all), numberOfTotalItems, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update updates a user <-> project relation
|
// Update updates a user <-> project relation
|
||||||
// @Summary Update 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
|
// @tags sharing
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce 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]
|
// @Router /projects/{projectID}/users/{userID} [post]
|
||||||
func (lu *ProjectUser) Update(s *xorm.Session, _ web.Auth) (err error) {
|
func (lu *ProjectUser) Update(s *xorm.Session, _ web.Auth) (err error) {
|
||||||
|
|
||||||
// Check if the right is valid
|
// Check if the permission is valid
|
||||||
if err := lu.Right.isValid(); err != nil {
|
if err := lu.Permission.isValid(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,7 +259,7 @@ func (lu *ProjectUser) Update(s *xorm.Session, _ web.Auth) (err error) {
|
||||||
|
|
||||||
_, err = s.
|
_, err = s.
|
||||||
Where("project_id = ? AND user_id = ?", lu.ProjectID, lu.UserID).
|
Where("project_id = ? AND user_id = ?", lu.ProjectID, lu.UserID).
|
||||||
Cols("right").
|
Cols("permission").
|
||||||
Update(lu)
|
Update(lu)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -28,14 +28,14 @@ import (
|
||||||
|
|
||||||
func TestProjectUser_CanDoSomething(t *testing.T) {
|
func TestProjectUser_CanDoSomething(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
ID int64
|
ID int64
|
||||||
UserID int64
|
UserID int64
|
||||||
ProjectID int64
|
ProjectID int64
|
||||||
Right Right
|
Permission Permission
|
||||||
Created time.Time
|
Created time.Time
|
||||||
Updated time.Time
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Permissions web.Permissions
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
a web.Auth
|
a web.Auth
|
||||||
|
|
@ -67,7 +67,7 @@ func TestProjectUser_CanDoSomething(t *testing.T) {
|
||||||
want: map[string]bool{"CanCreate": false, "CanDelete": false, "CanUpdate": false},
|
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{
|
fields: fields{
|
||||||
ProjectID: 3,
|
ProjectID: 3,
|
||||||
},
|
},
|
||||||
|
|
@ -83,14 +83,14 @@ func TestProjectUser_CanDoSomething(t *testing.T) {
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
|
|
||||||
lu := &ProjectUser{
|
lu := &ProjectUser{
|
||||||
ID: tt.fields.ID,
|
ID: tt.fields.ID,
|
||||||
UserID: tt.fields.UserID,
|
UserID: tt.fields.UserID,
|
||||||
ProjectID: tt.fields.ProjectID,
|
ProjectID: tt.fields.ProjectID,
|
||||||
Right: tt.fields.Right,
|
Permission: tt.fields.Permission,
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
Updated: tt.fields.Updated,
|
Updated: tt.fields.Updated,
|
||||||
CRUDable: tt.fields.CRUDable,
|
CRUDable: tt.fields.CRUDable,
|
||||||
Rights: tt.fields.Rights,
|
Permissions: tt.fields.Permissions,
|
||||||
}
|
}
|
||||||
if got, _ := lu.CanCreate(s, tt.args.a); got != tt.want["CanCreate"] {
|
if got, _ := lu.CanCreate(s, tt.args.a); got != tt.want["CanCreate"] {
|
||||||
t.Errorf("ProjectUser.CanCreate() = %v, want %v", 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) {
|
func TestProjectUser_Create(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
ID int64
|
ID int64
|
||||||
UserID int64
|
UserID int64
|
||||||
Username string
|
Username string
|
||||||
ProjectID int64
|
ProjectID int64
|
||||||
Right Right
|
Permission Permission
|
||||||
Created time.Time
|
Created time.Time
|
||||||
Updated time.Time
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Permissions web.Permissions
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
a web.Auth
|
a web.Auth
|
||||||
|
|
@ -69,14 +69,14 @@ func TestProjectUser_Create(t *testing.T) {
|
||||||
errType: IsErrUserAlreadyHasAccess,
|
errType: IsErrUserAlreadyHasAccess,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ListUsers Create with invalid right",
|
name: "ListUsers Create with invalid permission",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
Username: "user1",
|
Username: "user1",
|
||||||
ProjectID: 2,
|
ProjectID: 2,
|
||||||
Right: 500,
|
Permission: 500,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
errType: IsErrInvalidRight,
|
errType: IsErrInvalidPermission,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ListUsers Create with inexisting project",
|
name: "ListUsers Create with inexisting project",
|
||||||
|
|
@ -112,15 +112,15 @@ func TestProjectUser_Create(t *testing.T) {
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
|
|
||||||
ul := &ProjectUser{
|
ul := &ProjectUser{
|
||||||
ID: tt.fields.ID,
|
ID: tt.fields.ID,
|
||||||
UserID: tt.fields.UserID,
|
UserID: tt.fields.UserID,
|
||||||
Username: tt.fields.Username,
|
Username: tt.fields.Username,
|
||||||
ProjectID: tt.fields.ProjectID,
|
ProjectID: tt.fields.ProjectID,
|
||||||
Right: tt.fields.Right,
|
Permission: tt.fields.Permission,
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
Updated: tt.fields.Updated,
|
Updated: tt.fields.Updated,
|
||||||
CRUDable: tt.fields.CRUDable,
|
CRUDable: tt.fields.CRUDable,
|
||||||
Rights: tt.fields.Rights,
|
Permissions: tt.fields.Permissions,
|
||||||
}
|
}
|
||||||
err := ul.Create(s, tt.args.a)
|
err := ul.Create(s, tt.args.a)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
|
|
@ -144,7 +144,7 @@ func TestProjectUser_Create(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProjectUser_ReadAll(t *testing.T) {
|
func TestProjectUser_ReadAll(t *testing.T) {
|
||||||
user1Read := &UserWithRight{
|
user1Read := &UserWithPermission{
|
||||||
User: user.User{
|
User: user.User{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
Username: "user1",
|
Username: "user1",
|
||||||
|
|
@ -157,9 +157,9 @@ func TestProjectUser_ReadAll(t *testing.T) {
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
ExportFileID: 1,
|
ExportFileID: 1,
|
||||||
},
|
},
|
||||||
Right: RightRead,
|
Permission: PermissionRead,
|
||||||
}
|
}
|
||||||
user2Read := &UserWithRight{
|
user2Read := &UserWithPermission{
|
||||||
User: user.User{
|
User: user.User{
|
||||||
ID: 2,
|
ID: 2,
|
||||||
Username: "user2",
|
Username: "user2",
|
||||||
|
|
@ -172,18 +172,18 @@ func TestProjectUser_ReadAll(t *testing.T) {
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
Right: RightRead,
|
Permission: PermissionRead,
|
||||||
}
|
}
|
||||||
|
|
||||||
type fields struct {
|
type fields struct {
|
||||||
ID int64
|
ID int64
|
||||||
UserID int64
|
UserID int64
|
||||||
ProjectID int64
|
ProjectID int64
|
||||||
Right Right
|
Permission Permission
|
||||||
Created time.Time
|
Created time.Time
|
||||||
Updated time.Time
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Permissions web.Permissions
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
search string
|
search string
|
||||||
|
|
@ -206,7 +206,7 @@ func TestProjectUser_ReadAll(t *testing.T) {
|
||||||
args: args{
|
args: args{
|
||||||
a: &user.User{ID: 3},
|
a: &user.User{ID: 3},
|
||||||
},
|
},
|
||||||
want: []*UserWithRight{
|
want: []*UserWithPermission{
|
||||||
user1Read,
|
user1Read,
|
||||||
user2Read,
|
user2Read,
|
||||||
},
|
},
|
||||||
|
|
@ -231,7 +231,7 @@ func TestProjectUser_ReadAll(t *testing.T) {
|
||||||
a: &user.User{ID: 3},
|
a: &user.User{ID: 3},
|
||||||
search: "USER2",
|
search: "USER2",
|
||||||
},
|
},
|
||||||
want: []*UserWithRight{
|
want: []*UserWithPermission{
|
||||||
user2Read,
|
user2Read,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -242,14 +242,14 @@ func TestProjectUser_ReadAll(t *testing.T) {
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
|
|
||||||
ul := &ProjectUser{
|
ul := &ProjectUser{
|
||||||
ID: tt.fields.ID,
|
ID: tt.fields.ID,
|
||||||
UserID: tt.fields.UserID,
|
UserID: tt.fields.UserID,
|
||||||
ProjectID: tt.fields.ProjectID,
|
ProjectID: tt.fields.ProjectID,
|
||||||
Right: tt.fields.Right,
|
Permission: tt.fields.Permission,
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
Updated: tt.fields.Updated,
|
Updated: tt.fields.Updated,
|
||||||
CRUDable: tt.fields.CRUDable,
|
CRUDable: tt.fields.CRUDable,
|
||||||
Rights: tt.fields.Rights,
|
Permissions: tt.fields.Permissions,
|
||||||
}
|
}
|
||||||
got, _, _, err := ul.ReadAll(s, tt.args.a, tt.args.search, tt.args.page, 50)
|
got, _, _, err := ul.ReadAll(s, tt.args.a, tt.args.search, tt.args.page, 50)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
|
|
@ -268,14 +268,14 @@ func TestProjectUser_ReadAll(t *testing.T) {
|
||||||
|
|
||||||
func TestProjectUser_Update(t *testing.T) {
|
func TestProjectUser_Update(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
ID int64
|
ID int64
|
||||||
Username string
|
Username string
|
||||||
ProjectID int64
|
ProjectID int64
|
||||||
Right Right
|
Permission Permission
|
||||||
Created time.Time
|
Created time.Time
|
||||||
Updated time.Time
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Permissions web.Permissions
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|
@ -286,36 +286,36 @@ func TestProjectUser_Update(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "Test Update Normally",
|
name: "Test Update Normally",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
ProjectID: 3,
|
ProjectID: 3,
|
||||||
Username: "user1",
|
Username: "user1",
|
||||||
Right: RightAdmin,
|
Permission: PermissionAdmin,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Test Update to write",
|
name: "Test Update to write",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
ProjectID: 3,
|
ProjectID: 3,
|
||||||
Username: "user1",
|
Username: "user1",
|
||||||
Right: RightWrite,
|
Permission: PermissionWrite,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Test Update to Read",
|
name: "Test Update to Read",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
ProjectID: 3,
|
ProjectID: 3,
|
||||||
Username: "user1",
|
Username: "user1",
|
||||||
Right: RightRead,
|
Permission: PermissionRead,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Test Update with invalid right",
|
name: "Test Update with invalid permission",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
ProjectID: 3,
|
ProjectID: 3,
|
||||||
Username: "user1",
|
Username: "user1",
|
||||||
Right: 500,
|
Permission: 500,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
errType: IsErrInvalidRight,
|
errType: IsErrInvalidPermission,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
@ -324,14 +324,14 @@ func TestProjectUser_Update(t *testing.T) {
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
|
|
||||||
lu := &ProjectUser{
|
lu := &ProjectUser{
|
||||||
ID: tt.fields.ID,
|
ID: tt.fields.ID,
|
||||||
Username: tt.fields.Username,
|
Username: tt.fields.Username,
|
||||||
ProjectID: tt.fields.ProjectID,
|
ProjectID: tt.fields.ProjectID,
|
||||||
Right: tt.fields.Right,
|
Permission: tt.fields.Permission,
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
Updated: tt.fields.Updated,
|
Updated: tt.fields.Updated,
|
||||||
CRUDable: tt.fields.CRUDable,
|
CRUDable: tt.fields.CRUDable,
|
||||||
Rights: tt.fields.Rights,
|
Permissions: tt.fields.Permissions,
|
||||||
}
|
}
|
||||||
err := lu.Update(s, &user.User{ID: 1})
|
err := lu.Update(s, &user.User{ID: 1})
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
|
|
@ -348,7 +348,7 @@ func TestProjectUser_Update(t *testing.T) {
|
||||||
db.AssertExists(t, "users_projects", map[string]interface{}{
|
db.AssertExists(t, "users_projects", map[string]interface{}{
|
||||||
"project_id": tt.fields.ProjectID,
|
"project_id": tt.fields.ProjectID,
|
||||||
"user_id": lu.UserID,
|
"user_id": lu.UserID,
|
||||||
"right": tt.fields.Right,
|
"permission": tt.fields.Permission,
|
||||||
}, false)
|
}, false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -357,15 +357,15 @@ func TestProjectUser_Update(t *testing.T) {
|
||||||
|
|
||||||
func TestProjectUser_Delete(t *testing.T) {
|
func TestProjectUser_Delete(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
ID int64
|
ID int64
|
||||||
Username string
|
Username string
|
||||||
UserID int64
|
UserID int64
|
||||||
ProjectID int64
|
ProjectID int64
|
||||||
Right Right
|
Permission Permission
|
||||||
Created time.Time
|
Created time.Time
|
||||||
Updated time.Time
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Permissions web.Permissions
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|
@ -406,14 +406,14 @@ func TestProjectUser_Delete(t *testing.T) {
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
|
|
||||||
lu := &ProjectUser{
|
lu := &ProjectUser{
|
||||||
ID: tt.fields.ID,
|
ID: tt.fields.ID,
|
||||||
Username: tt.fields.Username,
|
Username: tt.fields.Username,
|
||||||
ProjectID: tt.fields.ProjectID,
|
ProjectID: tt.fields.ProjectID,
|
||||||
Right: tt.fields.Right,
|
Permission: tt.fields.Permission,
|
||||||
Created: tt.fields.Created,
|
Created: tt.fields.Created,
|
||||||
Updated: tt.fields.Updated,
|
Updated: tt.fields.Updated,
|
||||||
CRUDable: tt.fields.CRUDable,
|
CRUDable: tt.fields.CRUDable,
|
||||||
Rights: tt.fields.Rights,
|
Permissions: tt.fields.Permissions,
|
||||||
}
|
}
|
||||||
err := lu.Delete(s, &user.User{ID: 1})
|
err := lu.Delete(s, &user.User{ID: 1})
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
|
|
|
||||||
|
|
@ -157,8 +157,8 @@ type ProjectView struct {
|
||||||
// A timestamp when this reaction was created. You cannot change this value.
|
// A timestamp when this reaction was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pv *ProjectView) TableName() string {
|
func (pv *ProjectView) TableName() string {
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,8 @@ type Reaction struct {
|
||||||
// A timestamp when this reaction was created. You cannot change this value.
|
// A timestamp when this reaction was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Reaction) TableName() string {
|
func (*Reaction) TableName() string {
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,8 @@ type SavedFilter struct {
|
||||||
// A timestamp when this filter was last updated. You cannot change this value.
|
// A timestamp when this filter was last updated. You cannot change this value.
|
||||||
Updated time.Time `xorm:"updated not null" json:"updated"`
|
Updated time.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName returns a better table name for saved filters
|
// 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"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /filters/{id} [get]
|
// @Router /filters/{id} [get]
|
||||||
func (sf *SavedFilter) ReadOne(s *xorm.Session, _ web.Auth) error {
|
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)
|
u, err := user.GetUserByID(s, sf.OwnerID)
|
||||||
sf.Owner = u
|
sf.Owner = u
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -21,25 +21,25 @@ import (
|
||||||
"xorm.io/xorm"
|
"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) {
|
func (sf *SavedFilter) CanRead(s *xorm.Session, auth web.Auth) (bool, int, error) {
|
||||||
can, err := sf.canDoFilter(s, auth)
|
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) {
|
func (sf *SavedFilter) CanDelete(s *xorm.Session, auth web.Auth) (bool, error) {
|
||||||
return sf.canDoFilter(s, auth)
|
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) {
|
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.
|
// 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}
|
sff := &SavedFilter{ID: sf.ID}
|
||||||
return sff.canDoFilter(s, auth)
|
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) {
|
func (sf *SavedFilter) CanCreate(_ *xorm.Session, auth web.Auth) (bool, error) {
|
||||||
if _, is := auth.(*LinkSharing); is {
|
if _, is := auth.(*LinkSharing); is {
|
||||||
return false, nil
|
return false, nil
|
||||||
|
|
@ -48,7 +48,7 @@ func (sf *SavedFilter) CanCreate(_ *xorm.Session, auth web.Auth) (bool, error) {
|
||||||
return true, nil
|
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) {
|
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
|
// Link shares can't view or modify saved filters, therefore we can error out right away
|
||||||
if _, is := auth.(*LinkSharing); is {
|
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}
|
user1 := &user.User{ID: 1}
|
||||||
user2 := &user.User{ID: 2}
|
user2 := &user.User{ID: 2}
|
||||||
ls := &LinkSharing{ID: 1}
|
ls := &LinkSharing{ID: 1}
|
||||||
|
|
@ -216,9 +216,9 @@ func TestSavedFilter_Rights(t *testing.T) {
|
||||||
ID: 1,
|
ID: 1,
|
||||||
Title: "Lorem",
|
Title: "Lorem",
|
||||||
}
|
}
|
||||||
can, maxRight, err := sf.CanRead(s, user1)
|
can, maxPermission, err := sf.CanRead(s, user1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, int(RightAdmin), maxRight)
|
assert.Equal(t, int(PermissionAdmin), maxPermission)
|
||||||
assert.True(t, can)
|
assert.True(t, can)
|
||||||
})
|
})
|
||||||
t.Run("not owner", func(t *testing.T) {
|
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.
|
// A timestamp when this subscription was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SubscriptionWithUser struct {
|
type SubscriptionWithUser struct {
|
||||||
|
|
@ -145,7 +145,7 @@ func (sb *Subscription) TableName() string {
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /subscriptions/{entity}/{entityID} [put]
|
// @Router /subscriptions/{entity}/{entityID} [put]
|
||||||
func (sb *Subscription) Create(s *xorm.Session, auth web.Auth) (err error) {
|
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.ID = 0
|
||||||
sb.UserID = auth.GetID()
|
sb.UserID = auth.GetID()
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ func TestSubscription_Create(t *testing.T) {
|
||||||
assert.True(t, IsErrTaskDoesNotExist(err))
|
assert.True(t, IsErrTaskDoesNotExist(err))
|
||||||
assert.False(t, can)
|
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)
|
db.LoadAndAssertFixtures(t)
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
@ -151,7 +151,7 @@ func TestSubscription_Create(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.False(t, can)
|
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)
|
db.LoadAndAssertFixtures(t)
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,8 @@ type TaskAssginee struct {
|
||||||
UserID int64 `xorm:"bigint INDEX not null" json:"user_id" param:"user"`
|
UserID int64 `xorm:"bigint INDEX not null" json:"user_id" param:"user"`
|
||||||
Created time.Time `xorm:"created not null"`
|
Created time.Time `xorm:"created not null"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName makes a pretty table name
|
// TableName makes a pretty table name
|
||||||
|
|
@ -355,8 +355,8 @@ type BulkAssignees struct {
|
||||||
Assignees []*user.User `json:"assignees"`
|
Assignees []*user.User `json:"assignees"`
|
||||||
TaskID int64 `json:"-" param:"projecttask"`
|
TaskID int64 `json:"-" param:"projecttask"`
|
||||||
|
|
||||||
web.CRUDable `json:"-"`
|
web.CRUDable `json:"-"`
|
||||||
web.Rights `json:"-"`
|
web.Permissions `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create adds new assignees to a task
|
// Create adds new assignees to a task
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,8 @@ type TaskAttachment struct {
|
||||||
|
|
||||||
Created time.Time `xorm:"created" json:"created"`
|
Created time.Time `xorm:"created" json:"created"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName returns the table name for task attachments
|
// 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}
|
u := &user.User{ID: 1}
|
||||||
t.Run("Can Read", func(t *testing.T) {
|
t.Run("Can Read", func(t *testing.T) {
|
||||||
t.Run("Allowed", func(t *testing.T) {
|
t.Run("Allowed", func(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,8 @@ type TaskCollection struct {
|
||||||
|
|
||||||
isSavedFilter bool
|
isSavedFilter bool
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TaskCollectionExpandable string
|
type TaskCollectionExpandable string
|
||||||
|
|
|
||||||
|
|
@ -661,8 +661,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
|
|
||||||
Expand []TaskCollectionExpandable
|
Expand []TaskCollectionExpandable
|
||||||
|
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Permissions web.Permissions
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
search string
|
search string
|
||||||
|
|
@ -1644,8 +1644,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
|
|
||||||
Expand: tt.fields.Expand,
|
Expand: tt.fields.Expand,
|
||||||
|
|
||||||
CRUDable: tt.fields.CRUDable,
|
CRUDable: tt.fields.CRUDable,
|
||||||
Rights: tt.fields.Rights,
|
Permissions: tt.fields.Permissions,
|
||||||
}
|
}
|
||||||
got, _, _, err := lt.ReadAll(s, tt.args.a, tt.args.search, tt.args.page, 50)
|
got, _, _, err := lt.ReadAll(s, tt.args.a, tt.args.search, tt.args.page, 50)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,8 @@ type TaskComment struct {
|
||||||
Created time.Time `xorm:"created" json:"created"`
|
Created time.Time `xorm:"created" json:"created"`
|
||||||
Updated time.Time `xorm:"updated" json:"updated"`
|
Updated time.Time `xorm:"updated" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName holds the table name for the task comments table
|
// 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.
|
// 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"`
|
Position float64 `xorm:"double not null" json:"position"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tp *TaskPosition) TableName() string {
|
func (tp *TaskPosition) TableName() string {
|
||||||
|
|
|
||||||
|
|
@ -95,8 +95,8 @@ type TaskRelation struct {
|
||||||
// A timestamp when this label was created. You cannot change this value.
|
// A timestamp when this label was created. You cannot change this value.
|
||||||
Created time.Time `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName holds the table name for the task relation table
|
// 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
|
// Create creates a new task relation
|
||||||
// @Summary Create a new relation between two tasks
|
// @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
|
// @tags task
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
|
|
|
||||||
|
|
@ -347,7 +347,7 @@ func TestTaskRelation_CanCreate(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.True(t, can)
|
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)
|
db.LoadAndAssertFixtures(t)
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
@ -361,7 +361,7 @@ func TestTaskRelation_CanCreate(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.False(t, can)
|
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)
|
db.LoadAndAssertFixtures(t)
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
@ -375,7 +375,7 @@ func TestTaskRelation_CanCreate(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.False(t, can)
|
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)
|
db.LoadAndAssertFixtures(t)
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
|
||||||
|
|
@ -141,8 +141,8 @@ type Task struct {
|
||||||
CreatedBy *user.User `xorm:"-" json:"created_by" valid:"-"`
|
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
|
CreatedByID int64 `xorm:"bigint not null" json:"-"` // ID of the user who put that task on the project
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Permissions `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TaskWithComments struct {
|
type TaskWithComments struct {
|
||||||
|
|
@ -1184,7 +1184,7 @@ func (t *Task) Update(s *xorm.Session, a web.Auth) (err error) {
|
||||||
//
|
//
|
||||||
// Maybe FIXME:
|
// Maybe FIXME:
|
||||||
// I've disabled this for now, because it requires significant changes in the way we do updates (using the
|
// 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
|
// 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
|
// 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.
|
// 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)
|
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) {
|
func (t *Task) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||||
return t.canDoTask(s, a)
|
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) {
|
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
|
// A user can do a task if he has write acces to its project
|
||||||
l := &Project{ID: t.ProjectID}
|
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
|
// 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
|
expand := t.Expand
|
||||||
// Get the task, error out if it doesn't exist
|
// Get the task, error out if it doesn't exist
|
||||||
*t, err = GetTaskByIDSimple(s, t.ID)
|
*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
|
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 {
|
if t.ProjectID != 0 && t.ProjectID != ot.ProjectID {
|
||||||
newProject := &Project{ID: t.ProjectID}
|
newProject := &Project{ID: t.ProjectID}
|
||||||
can, err := newProject.CanWrite(s, a)
|
can, err := newProject.CanWrite(s, a)
|
||||||
|
|
@ -36,7 +36,7 @@ func TestTask_Create(t *testing.T) {
|
||||||
Email: "user1@example.com",
|
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) {
|
t.Run("normal", func(t *testing.T) {
|
||||||
db.LoadAndAssertFixtures(t)
|
db.LoadAndAssertFixtures(t)
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ func (tm *TeamMember) MembershipExists(s *xorm.Session) (exists bool, err error)
|
||||||
// @Security JWTKeyAuth
|
// @Security JWTKeyAuth
|
||||||
// @Param id path int true "Team ID"
|
// @Param id path int true "Team ID"
|
||||||
// @Param userID path int true "User 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"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /teams/{id}/members/{userID}/admin [post]
|
// @Router /teams/{id}/members/{userID}/admin [post]
|
||||||
func (tm *TeamMember) Update(s *xorm.Session, _ web.Auth) (err error) {
|
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
|
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{}
|
ttm := &TeamMember{}
|
||||||
_, err = s.
|
_, err = s.
|
||||||
Where("team_id = ? AND user_id = ?", tm.TeamID, tm.UserID).
|
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).
|
Where("team_id = ? AND user_id = ?", tm.TeamID, tm.UserID).
|
||||||
Cols("admin").
|
Cols("admin").
|
||||||
Update(ttm)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue