mirror of
https://github.com/semaphoreui/semaphore.git
synced 2024-11-25 06:15:56 +01:00
commit
36570ae15c
@ -150,10 +150,10 @@ var pathSubPatterns = []func() string{
|
||||
func() string { return strconv.Itoa(userProject.ID) },
|
||||
func() string { return strconv.Itoa(userPathTestUser.ID) },
|
||||
func() string { return strconv.Itoa(userKey.ID) },
|
||||
func() string { return strconv.Itoa(int(repoID)) },
|
||||
func() string { return strconv.Itoa(int(inventoryID)) },
|
||||
func() string { return strconv.Itoa(int(environmentID)) },
|
||||
func() string { return strconv.Itoa(int(templateID)) },
|
||||
func() string { return strconv.Itoa(repoID) },
|
||||
func() string { return strconv.Itoa(inventoryID) },
|
||||
func() string { return strconv.Itoa(environmentID) },
|
||||
func() string { return strconv.Itoa(templateID) },
|
||||
func() string { return strconv.Itoa(task.ID) },
|
||||
func() string { return strconv.Itoa(schedule.ID) },
|
||||
func() string { return strconv.Itoa(view.ID) },
|
||||
|
@ -104,6 +104,7 @@ func main() {
|
||||
h.Before("project > /api/project/{project_id}/tasks/{task_id} > Get a single task > 200 > application/json", capabilityWrapper("task"))
|
||||
h.Before("project > /api/project/{project_id}/tasks/{task_id} > Deletes task (including output) > 204 > application/json", capabilityWrapper("task"))
|
||||
h.Before("project > /api/project/{project_id}/tasks/{task_id}/output > Get task output > 200 > application/json", capabilityWrapper("task"))
|
||||
h.Before("project > /api/project/{project_id}/tasks/{task_id}/stop > Stop a job > 204 > application/json", capabilityWrapper("task"))
|
||||
|
||||
h.Before("schedule > /api/project/{project_id}/schedules/{schedule_id} > Get schedule > 200 > application/json", capabilityWrapper("schedule"))
|
||||
h.Before("schedule > /api/project/{project_id}/schedules/{schedule_id} > Updates schedule > 204 > application/json", capabilityWrapper("schedule"))
|
||||
|
44
api-docs.yml
44
api-docs.yml
@ -928,6 +928,28 @@ paths:
|
||||
204:
|
||||
description: Project deleted
|
||||
|
||||
|
||||
/project/{project_id}/role:
|
||||
parameters:
|
||||
- $ref: "#/parameters/project_id"
|
||||
get:
|
||||
tags:
|
||||
- project
|
||||
summary: Fetch permissions of the current user for project
|
||||
responses:
|
||||
200:
|
||||
description: Permissions
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
role:
|
||||
type: string
|
||||
example: owner
|
||||
permissions:
|
||||
type: number
|
||||
example: 0
|
||||
|
||||
|
||||
/project/{project_id}/events:
|
||||
parameters:
|
||||
- $ref: '#/parameters/project_id'
|
||||
@ -1487,7 +1509,6 @@ paths:
|
||||
description: view removed
|
||||
|
||||
|
||||
|
||||
# tasks
|
||||
/project/{project_id}/tasks:
|
||||
parameters:
|
||||
@ -1533,6 +1554,8 @@ paths:
|
||||
description: Task queued
|
||||
schema:
|
||||
$ref: "#/definitions/Task"
|
||||
|
||||
|
||||
/project/{project_id}/tasks/last:
|
||||
parameters:
|
||||
- $ref: "#/parameters/project_id"
|
||||
@ -1547,6 +1570,22 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Task'
|
||||
|
||||
|
||||
/project/{project_id}/tasks/{task_id}/stop:
|
||||
parameters:
|
||||
- $ref: "#/parameters/project_id"
|
||||
- $ref: '#/parameters/task_id'
|
||||
post:
|
||||
tags:
|
||||
- project
|
||||
summary: Stop a job
|
||||
responses:
|
||||
204:
|
||||
description: Task queued
|
||||
|
||||
|
||||
|
||||
/project/{project_id}/tasks/{task_id}:
|
||||
parameters:
|
||||
- $ref: "#/parameters/project_id"
|
||||
@ -1567,6 +1606,9 @@ paths:
|
||||
responses:
|
||||
204:
|
||||
description: task deleted
|
||||
|
||||
|
||||
|
||||
/project/{project_id}/tasks/{task_id}/output:
|
||||
parameters:
|
||||
- $ref: '#/parameters/project_id'
|
||||
|
@ -24,7 +24,7 @@ func ProjectMiddleware(next http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
// check if user in project's team
|
||||
_, err = helpers.Store(r).GetProjectUser(projectID, user.ID)
|
||||
projectUser, err := helpers.Store(r).GetProjectUser(projectID, user.ID)
|
||||
|
||||
if err != nil {
|
||||
helpers.WriteError(w, err)
|
||||
@ -38,38 +38,24 @@ func ProjectMiddleware(next http.Handler) http.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
context.Set(r, "projectUserRole", projectUser.Role)
|
||||
context.Set(r, "project", project)
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// GetMustCanMiddlewareFor ensures that the user has administrator rights
|
||||
func GetMustCanMiddlewareFor(permissions db.ProjectUserPermission) mux.MiddlewareFunc {
|
||||
// GetMustCanMiddleware ensures that the user has administrator rights
|
||||
func GetMustCanMiddleware(permissions db.ProjectUserPermission) mux.MiddlewareFunc {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
project := context.Get(r, "project").(db.Project)
|
||||
user := context.Get(r, "user").(*db.User)
|
||||
projectUserRole := context.Get(r, "projectUserRole").(db.ProjectUserRole)
|
||||
|
||||
if !user.Admin {
|
||||
// check if user in project's team
|
||||
projectUser, err := helpers.Store(r).GetProjectUser(project.ID, user.ID)
|
||||
|
||||
if err == db.ErrNotFound {
|
||||
if !user.Admin && r.Method != "GET" && r.Method != "HEAD" && !projectUserRole.Can(permissions) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
helpers.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method != "GET" && r.Method != "HEAD" && !projectUser.Can(permissions) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
@ -80,6 +66,16 @@ func GetProject(w http.ResponseWriter, r *http.Request) {
|
||||
helpers.WriteJSON(w, http.StatusOK, context.Get(r, "project"))
|
||||
}
|
||||
|
||||
func GetUserRole(w http.ResponseWriter, r *http.Request) {
|
||||
var permissions struct {
|
||||
Role db.ProjectUserRole `json:"role"`
|
||||
Permissions db.ProjectUserPermission `json:"permissions"`
|
||||
}
|
||||
permissions.Role = context.Get(r, "projectUserRole").(db.ProjectUserRole)
|
||||
permissions.Permissions = permissions.Role.GetPermissions()
|
||||
helpers.WriteJSON(w, http.StatusOK, permissions)
|
||||
}
|
||||
|
||||
// UpdateProject saves updated project details to the database
|
||||
func UpdateProject(w http.ResponseWriter, r *http.Request) {
|
||||
project := context.Get(r, "project").(db.Project)
|
||||
|
@ -128,17 +128,19 @@ func Route() *mux.Router {
|
||||
//
|
||||
// Start and Stop tasks
|
||||
projectTaskStart := authenticatedAPI.PathPrefix("/project/{project_id}").Subrouter()
|
||||
projectTaskStart.Use(projects.ProjectMiddleware, projects.GetMustCanMiddlewareFor(db.CanRunProjectTasks))
|
||||
projectTaskStart.Use(projects.ProjectMiddleware, projects.GetMustCanMiddleware(db.CanRunProjectTasks))
|
||||
projectTaskStart.Path("/tasks").HandlerFunc(projects.AddTask).Methods("POST")
|
||||
|
||||
projectTaskStop := authenticatedAPI.PathPrefix("/project/{project_id}").Subrouter()
|
||||
projectTaskStop.Use(projects.ProjectMiddleware, projects.GetTaskMiddleware, projects.GetMustCanMiddlewareFor(db.CanRunProjectTasks))
|
||||
projectTaskStop.Use(projects.ProjectMiddleware, projects.GetTaskMiddleware, projects.GetMustCanMiddleware(db.CanRunProjectTasks))
|
||||
projectTaskStop.HandleFunc("/tasks/{task_id}/stop", projects.StopTask).Methods("POST")
|
||||
|
||||
//
|
||||
// Project resources CRUD
|
||||
projectUserAPI := authenticatedAPI.PathPrefix("/project/{project_id}").Subrouter()
|
||||
projectUserAPI.Use(projects.ProjectMiddleware, projects.GetMustCanMiddlewareFor(db.CanManageProjectResources))
|
||||
projectUserAPI.Use(projects.ProjectMiddleware, projects.GetMustCanMiddleware(db.CanManageProjectResources))
|
||||
|
||||
projectUserAPI.Path("/role").HandlerFunc(projects.GetUserRole).Methods("GET", "HEAD")
|
||||
|
||||
projectUserAPI.Path("/events").HandlerFunc(getAllEvents).Methods("GET", "HEAD")
|
||||
projectUserAPI.HandleFunc("/events/last", getLastEvents).Methods("GET", "HEAD")
|
||||
@ -173,14 +175,14 @@ func Route() *mux.Router {
|
||||
//
|
||||
// Updating and deleting project
|
||||
projectAdminAPI := authenticatedAPI.Path("/project/{project_id}").Subrouter()
|
||||
projectAdminAPI.Use(projects.ProjectMiddleware, projects.GetMustCanMiddlewareFor(db.CanUpdateProject))
|
||||
projectAdminAPI.Use(projects.ProjectMiddleware, projects.GetMustCanMiddleware(db.CanUpdateProject))
|
||||
projectAdminAPI.Methods("PUT").HandlerFunc(projects.UpdateProject)
|
||||
projectAdminAPI.Methods("DELETE").HandlerFunc(projects.DeleteProject)
|
||||
|
||||
//
|
||||
// Manage project users
|
||||
projectAdminUsersAPI := authenticatedAPI.PathPrefix("/project/{project_id}").Subrouter()
|
||||
projectAdminUsersAPI.Use(projects.ProjectMiddleware, projects.GetMustCanMiddlewareFor(db.CanManageProjectUsers))
|
||||
projectAdminUsersAPI.Use(projects.ProjectMiddleware, projects.GetMustCanMiddleware(db.CanManageProjectUsers))
|
||||
projectAdminUsersAPI.Path("/users").HandlerFunc(projects.AddUser).Methods("POST")
|
||||
|
||||
projectUserManagement := projectAdminUsersAPI.PathPrefix("/users").Subrouter()
|
||||
|
11
api/user.go
11
api/user.go
@ -5,6 +5,7 @@ import (
|
||||
"encoding/base64"
|
||||
"github.com/ansible-semaphore/semaphore/api/helpers"
|
||||
"github.com/ansible-semaphore/semaphore/db"
|
||||
"github.com/ansible-semaphore/semaphore/util"
|
||||
"github.com/gorilla/context"
|
||||
"github.com/gorilla/mux"
|
||||
"io"
|
||||
@ -18,7 +19,15 @@ func getUser(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
helpers.WriteJSON(w, http.StatusOK, context.Get(r, "user"))
|
||||
var user struct {
|
||||
db.User
|
||||
CanCreateProject bool `json:"can_create_project"`
|
||||
}
|
||||
|
||||
user.User = *context.Get(r, "user").(*db.User)
|
||||
user.CanCreateProject = user.Admin || util.Config.NonAdminCanCreateProject
|
||||
|
||||
helpers.WriteJSON(w, http.StatusOK, user)
|
||||
}
|
||||
|
||||
func getAPITokens(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -19,8 +19,8 @@ const (
|
||||
)
|
||||
|
||||
var rolePermissions = map[ProjectUserRole]ProjectUserPermission{
|
||||
ProjectOwner: CanRunProjectTasks | CanUpdateProject | CanManageProjectResources,
|
||||
ProjectManager: CanRunProjectTasks | CanManageProjectResources,
|
||||
ProjectOwner: CanRunProjectTasks | CanManageProjectResources | CanUpdateProject | CanManageProjectUsers,
|
||||
ProjectManager: CanRunProjectTasks | CanManageProjectResources | CanManageProjectUsers,
|
||||
ProjectTaskRunner: CanRunProjectTasks,
|
||||
ProjectGuest: 0,
|
||||
}
|
||||
@ -39,5 +39,13 @@ type ProjectUser struct {
|
||||
|
||||
func (u *ProjectUser) Can(permissions ProjectUserPermission) bool {
|
||||
userPermissions := rolePermissions[u.Role]
|
||||
return (userPermissions & userPermissions) == permissions
|
||||
return (userPermissions & permissions) == permissions
|
||||
}
|
||||
|
||||
func (r ProjectUserRole) Can(permissions ProjectUserPermission) bool {
|
||||
return (rolePermissions[r] & permissions) == permissions
|
||||
}
|
||||
|
||||
func (r ProjectUserRole) GetPermissions() ProjectUserPermission {
|
||||
return rolePermissions[r]
|
||||
}
|
||||
|
15
db/ProjectUser_test.go
Normal file
15
db/ProjectUser_test.go
Normal file
@ -0,0 +1,15 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestProjectUsers_RoleCan(t *testing.T) {
|
||||
if !ProjectManager.Can(CanManageProjectResources) {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
if ProjectManager.Can(CanUpdateProject) {
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
@ -135,6 +135,7 @@
|
||||
<v-list-item-title class="app__project-selector-title">
|
||||
{{ project.name }}
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ userRole.role }}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
|
||||
<v-list-item-icon>
|
||||
@ -162,7 +163,7 @@
|
||||
<v-list-item-content>{{ item.name }}</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item @click="newProjectDialog = true" v-if="user.admin">
|
||||
<v-list-item @click="newProjectDialog = true" v-if="user.can_create_project">
|
||||
<v-list-item-icon>
|
||||
<v-icon>mdi-plus</v-icon>
|
||||
</v-list-item-icon>
|
||||
@ -341,14 +342,14 @@
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item key="password" @click="passwordDialog = true">-->
|
||||
<!-- <v-list-item-icon>-->
|
||||
<!-- <v-icon>mdi-lock</v-icon>-->
|
||||
<!-- </v-list-item-icon>-->
|
||||
<!-- <v-list-item-icon>-->
|
||||
<!-- <v-icon>mdi-lock</v-icon>-->
|
||||
<!-- </v-list-item-icon>-->
|
||||
|
||||
<!-- <v-list-item-content>-->
|
||||
<!-- Change Password-->
|
||||
<!-- </v-list-item-content>-->
|
||||
<!-- </v-list-item>-->
|
||||
<!-- <v-list-item-content>-->
|
||||
<!-- Change Password-->
|
||||
<!-- </v-list-item-content>-->
|
||||
<!-- </v-list-item>-->
|
||||
|
||||
<v-list-item key="sign_out" @click="signOut()">
|
||||
<v-list-item-icon>
|
||||
@ -365,7 +366,11 @@
|
||||
</v-navigation-drawer>
|
||||
|
||||
<v-main>
|
||||
<router-view :projectId="projectId" :userId="user ? user.id : null"></router-view>
|
||||
<router-view
|
||||
:projectId="projectId"
|
||||
:userPermissions="userRole.permissions"
|
||||
:userId="user ? user.id : null"
|
||||
></router-view>
|
||||
</v-main>
|
||||
|
||||
</v-app>
|
||||
@ -421,6 +426,7 @@
|
||||
.v-dialog > .v-card > .v-card__title {
|
||||
flex-wrap: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
& * {
|
||||
white-space: nowrap;
|
||||
}
|
||||
@ -560,6 +566,7 @@ export default {
|
||||
return {
|
||||
drawer: null,
|
||||
user: null,
|
||||
userRole: 0,
|
||||
systemInfo: null,
|
||||
state: 'loading',
|
||||
snackbar: false,
|
||||
@ -826,10 +833,17 @@ export default {
|
||||
},
|
||||
|
||||
async selectProject(projectId) {
|
||||
this.userRole = (await axios({
|
||||
method: 'get',
|
||||
url: `/api/project/${projectId}/role`,
|
||||
responseType: 'json',
|
||||
})).data;
|
||||
|
||||
localStorage.setItem('projectId', projectId);
|
||||
if (this.projectId === projectId) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.$router.push({ path: `/project/${projectId}` });
|
||||
},
|
||||
|
||||
|
@ -5,6 +5,7 @@ import YesNoDialog from '@/components/YesNoDialog.vue';
|
||||
import ObjectRefsDialog from '@/components/ObjectRefsDialog.vue';
|
||||
|
||||
import { getErrorMessage } from '@/lib/error';
|
||||
import { USER_PERMISSIONS } from '@/lib/constants';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -16,11 +17,16 @@ export default {
|
||||
props: {
|
||||
projectId: Number,
|
||||
userId: Number,
|
||||
userPermissions: Number,
|
||||
},
|
||||
|
||||
data() {
|
||||
const allowActions = this.allowActions();
|
||||
|
||||
const headers = this.getHeaders().filter((header) => allowActions || header.value !== 'actions');
|
||||
|
||||
return {
|
||||
headers: this.getHeaders(),
|
||||
headers,
|
||||
items: null,
|
||||
|
||||
itemId: null,
|
||||
@ -29,6 +35,8 @@ export default {
|
||||
|
||||
itemRefs: null,
|
||||
itemRefsDialog: null,
|
||||
|
||||
USER_PERMISSIONS,
|
||||
};
|
||||
},
|
||||
|
||||
@ -38,6 +46,15 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
allowActions() {
|
||||
return this.can(USER_PERMISSIONS.manageProjectResources);
|
||||
},
|
||||
|
||||
can(permission) {
|
||||
// eslint-disable-next-line no-bitwise
|
||||
return (this.userPermissions & permission) === permission;
|
||||
},
|
||||
|
||||
// eslint-disable-next-line no-empty-function
|
||||
async beforeLoadItems() {
|
||||
},
|
||||
|
@ -22,15 +22,22 @@
|
||||
:disabled="formSaving"
|
||||
></v-select>
|
||||
|
||||
<v-checkbox
|
||||
v-model="item.admin"
|
||||
:label="$t('administrator')"
|
||||
></v-checkbox>
|
||||
<v-select
|
||||
v-model="item.role"
|
||||
:label="$t('role')"
|
||||
:items="USER_ROLES"
|
||||
item-value="slug"
|
||||
item-text="title"
|
||||
:rules="[v => !!v || $t('user_required')]"
|
||||
required
|
||||
:disabled="formSaving"
|
||||
></v-select>
|
||||
</v-form>
|
||||
</template>
|
||||
<script>
|
||||
import ItemFormBase from '@/components/ItemFormBase';
|
||||
import axios from 'axios';
|
||||
import { USER_ROLES } from '@/lib/constants';
|
||||
|
||||
export default {
|
||||
mixins: [ItemFormBase],
|
||||
@ -40,6 +47,7 @@ export default {
|
||||
users: null,
|
||||
userId: null,
|
||||
teamMembers: null,
|
||||
USER_ROLES,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -15,3 +15,24 @@ export const TEMPLATE_TYPE_ACTION_TITLES = {
|
||||
build: 'Build',
|
||||
deploy: 'Deploy',
|
||||
};
|
||||
|
||||
export const USER_PERMISSIONS = {
|
||||
runProjectTasks: 1,
|
||||
updateProject: 2,
|
||||
manageProjectResources: 4,
|
||||
manageProjectUsers: 8,
|
||||
};
|
||||
|
||||
export const USER_ROLES = [{
|
||||
slug: 'owner',
|
||||
title: 'Owner',
|
||||
}, {
|
||||
slug: 'manager',
|
||||
title: 'Manager',
|
||||
}, {
|
||||
slug: 'task_runner',
|
||||
title: 'Task Runner',
|
||||
}, {
|
||||
slug: 'guest',
|
||||
title: 'Guest',
|
||||
}];
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
|
||||
<div v-if="items">
|
||||
<v-toolbar flat >
|
||||
<v-toolbar flat>
|
||||
<v-app-bar-nav-icon @click="showDrawer()"></v-app-bar-nav-icon>
|
||||
<v-toolbar-title>{{ $t('dashboard') }}</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
@ -8,7 +8,12 @@
|
||||
<v-tabs centered>
|
||||
<v-tab key="history" :to="`/project/${projectId}/history`">{{ $t('history') }}</v-tab>
|
||||
<v-tab key="activity" :to="`/project/${projectId}/activity`">{{ $t('activity') }}</v-tab>
|
||||
<v-tab key="settings" :to="`/project/${projectId}/settings`">{{ $t('settings') }}</v-tab>
|
||||
<v-tab
|
||||
v-if="can(USER_PERMISSIONS.updateProject)"
|
||||
key="settings"
|
||||
:to="`/project/${projectId}/settings`"
|
||||
>{{ $t('settings') }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
</div>
|
||||
</v-toolbar>
|
||||
@ -27,8 +32,14 @@
|
||||
</template>
|
||||
<script>
|
||||
import ItemListPageBase from '@/components/ItemListPageBase';
|
||||
import { USER_PERMISSIONS } from '@/lib/constants';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
USER_PERMISSIONS() {
|
||||
return USER_PERMISSIONS;
|
||||
},
|
||||
},
|
||||
mixins: [ItemListPageBase],
|
||||
|
||||
methods: {
|
||||
|
@ -40,6 +40,7 @@
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="editItem('new')"
|
||||
v-if="can(USER_PERMISSIONS.manageProjectResources)"
|
||||
>{{ $t('newEnvironment') }}
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
|
@ -23,7 +23,12 @@
|
||||
<v-tabs centered>
|
||||
<v-tab key="history" :to="`/project/${projectId}/history`">{{ $t('history') }}</v-tab>
|
||||
<v-tab key="activity" :to="`/project/${projectId}/activity`">{{ $t('activity') }}</v-tab>
|
||||
<v-tab key="settings" :to="`/project/${projectId}/settings`">{{ $t('settings') }}</v-tab>
|
||||
<v-tab
|
||||
v-if="can(USER_PERMISSIONS.updateProject)"
|
||||
key="settings"
|
||||
:to="`/project/${projectId}/settings`"
|
||||
>{{ $t('settings') }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
</div>
|
||||
</v-toolbar>
|
||||
|
@ -40,6 +40,7 @@
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="editItem('new')"
|
||||
v-if="can(USER_PERMISSIONS.manageProjectResources)"
|
||||
>{{ $t('newInventory') }}</v-btn>
|
||||
</v-toolbar>
|
||||
|
||||
|
@ -41,6 +41,7 @@
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="editItem('new')"
|
||||
v-if="can(USER_PERMISSIONS.manageProjectResources)"
|
||||
>{{ $t('newKey') }}</v-btn>
|
||||
</v-toolbar>
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="editItem('new')"
|
||||
v-if="can(USER_PERMISSIONS.manageProjectResources)"
|
||||
>{{ $t('newRepository') }}</v-btn>
|
||||
</v-toolbar>
|
||||
|
||||
|
@ -32,7 +32,9 @@
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="editItem('new')"
|
||||
>{{ $t('newTeamMember') }}</v-btn>
|
||||
v-if="can(USER_PERMISSIONS.manageProjectUsers)"
|
||||
>{{ $t('newTeamMember') }}
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
|
||||
<v-data-table
|
||||
@ -45,10 +47,11 @@
|
||||
<template v-slot:item.role="{ item }">
|
||||
<v-select
|
||||
v-model="item.role"
|
||||
:items="roles"
|
||||
:items="USER_ROLES"
|
||||
item-value="slug"
|
||||
item-text="title"
|
||||
:style="{width: '200px'}"
|
||||
:disabled="!can(USER_PERMISSIONS.manageProjectUsers)"
|
||||
@change="updateProjectUser(item)"
|
||||
/>
|
||||
</template>
|
||||
@ -58,6 +61,7 @@
|
||||
icon
|
||||
:disabled="!isUserAdmin()"
|
||||
@click="askDeleteItem(item.id)"
|
||||
v-if="can(USER_PERMISSIONS.manageProjectUsers)"
|
||||
>
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
@ -70,25 +74,14 @@
|
||||
import ItemListPageBase from '@/components/ItemListPageBase';
|
||||
import TeamMemberForm from '@/components/TeamMemberForm.vue';
|
||||
import axios from 'axios';
|
||||
import { USER_PERMISSIONS, USER_ROLES } from '@/lib/constants';
|
||||
|
||||
export default {
|
||||
components: { TeamMemberForm },
|
||||
mixins: [ItemListPageBase],
|
||||
data() {
|
||||
return {
|
||||
roles: [{
|
||||
slug: 'owner',
|
||||
title: 'Owner',
|
||||
}, {
|
||||
slug: 'manager',
|
||||
title: 'Manager',
|
||||
}, {
|
||||
slug: 'task_runner',
|
||||
title: 'Task Runner',
|
||||
}, {
|
||||
slug: 'guest',
|
||||
title: 'Guest',
|
||||
}],
|
||||
USER_ROLES,
|
||||
};
|
||||
},
|
||||
|
||||
@ -103,6 +96,10 @@ export default {
|
||||
await this.loadItems();
|
||||
},
|
||||
|
||||
allowActions() {
|
||||
return this.can(USER_PERMISSIONS.manageProjectUsers);
|
||||
},
|
||||
|
||||
getHeaders() {
|
||||
return [
|
||||
{
|
||||
@ -129,6 +126,7 @@ export default {
|
||||
sortable: false,
|
||||
}];
|
||||
},
|
||||
|
||||
getSingleItemUrl() {
|
||||
return `/api/project/${this.projectId}/users/${this.itemId}`;
|
||||
},
|
||||
|
@ -78,6 +78,7 @@
|
||||
color="primary"
|
||||
@click="editItem('new')"
|
||||
class="mr-1"
|
||||
v-if="can(USER_PERMISSIONS.manageProjectResources)"
|
||||
>{{ $t('newTemplate') }}
|
||||
</v-btn>
|
||||
|
||||
@ -97,7 +98,12 @@
|
||||
>{{ view.title }}
|
||||
</v-tab>
|
||||
|
||||
<v-btn icon class="mt-2 ml-4" @click="editViewsDialog = true">
|
||||
<v-btn
|
||||
icon
|
||||
class="mt-2 ml-4"
|
||||
@click="editViewsDialog = true"
|
||||
v-if="can(USER_PERMISSIONS.manageProjectResources)"
|
||||
>
|
||||
<v-icon>mdi-pencil</v-icon>
|
||||
</v-btn>
|
||||
</v-tabs>
|
||||
@ -212,7 +218,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
@import '~vuetify/src/styles/settings/_variables';
|
||||
@import '~vuetify/src/styles/settings/variables';
|
||||
|
||||
.templates-table .text-start:first-child {
|
||||
padding-right: 0 !important;
|
||||
|
Loading…
Reference in New Issue
Block a user