Skip to content

Commit 41ccd5c

Browse files
spezamichelvocks
authored andcommitted
Basic permissions (#154)
* Begin work on a simple permission system * Add delete permission group & fix create group ui error * Add the ability to save permissions into a UsersPermissions store * First pass of adding permissions to API endpoints * Correct Secret remove permission * Remove Permission Groups for now - keep it simple * Remove final permission group reference * Rename Permissions to Roles and add roles claim to JWTs * Improve auth middleware * UI improvements * Restore go sym * More UI changes * Checksum * Grab go.sum from master * Go mod being messed up by IntellJ? * Fix a UI issue and improve some server code (+tests) * Fix rogue failing test * Bug fix & CR changes * Add doc for left over exported funcs & remove unused struct * Remove unused old perms js code * Fixed checksum error
1 parent 1ef15c4 commit 41ccd5c

29 files changed

+1708
-642
lines changed

auth/role.go

+206
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
package auth
2+
3+
import "github.com/gaia-pipeline/gaia"
4+
5+
// Constructor for creating new UserRoleEndpoints.
6+
func NewUserRoleEndpoint(method string, path string) *gaia.UserRoleEndpoint {
7+
return &gaia.UserRoleEndpoint{Path: path, Method: method}
8+
}
9+
10+
// Creates a full user role name {category}{role}.
11+
func FullUserRoleName(category *gaia.UserRoleCategory, role *gaia.UserRole) string {
12+
return category.Name + role.Name
13+
}
14+
15+
// Flattens the given user categories into a single slice of {category}{role}s.
16+
func FlattenUserCategoryRoles(cats []*gaia.UserRoleCategory) []string {
17+
var roles []string
18+
for _, category := range cats {
19+
for _, r := range category.Roles {
20+
roles = append(roles, FullUserRoleName(category, r))
21+
}
22+
}
23+
return roles
24+
}
25+
26+
var (
27+
// All the default user categories and roles.
28+
DefaultUserRoles = []*gaia.UserRoleCategory{
29+
{
30+
Name: "Pipeline",
31+
Description: "Managing and initiating pipelines.",
32+
Roles: []*gaia.UserRole{
33+
{
34+
Name: "Create",
35+
ApiEndpoint: []*gaia.UserRoleEndpoint{
36+
NewUserRoleEndpoint("POST", "/api/v1/pipeline"),
37+
NewUserRoleEndpoint("POST", "/api/v1/pipeline/gitlsremote"),
38+
NewUserRoleEndpoint("GET", "/api/v1/pipeline/name"),
39+
NewUserRoleEndpoint("POST", "/api/v1/pipeline/githook"),
40+
},
41+
Description: "Create new pipelines.",
42+
},
43+
{
44+
Name: "List",
45+
ApiEndpoint: []*gaia.UserRoleEndpoint{
46+
NewUserRoleEndpoint("GET", "/api/v1/pipeline/created"),
47+
NewUserRoleEndpoint("GET", "/api/v1/pipeline"),
48+
NewUserRoleEndpoint("GET", "/api/v1/pipeline/latest"),
49+
},
50+
Description: "List created pipelines.",
51+
},
52+
{
53+
Name: "Get",
54+
ApiEndpoint: []*gaia.UserRoleEndpoint{
55+
NewUserRoleEndpoint("GET", "/api/v1/pipeline/:pipelineid"),
56+
},
57+
Description: "Get created pipelines.",
58+
},
59+
{
60+
Name: "Update",
61+
ApiEndpoint: []*gaia.UserRoleEndpoint{
62+
NewUserRoleEndpoint("PUT", "/api/v1/pipeline/:pipelineid"),
63+
},
64+
Description: "Update created pipelines.",
65+
},
66+
{
67+
Name: "Delete",
68+
ApiEndpoint: []*gaia.UserRoleEndpoint{
69+
NewUserRoleEndpoint("DELETE", "/api/v1/pipeline/:pipelineid"),
70+
},
71+
Description: "Delete created pipelines.",
72+
},
73+
{
74+
Name: "Start",
75+
ApiEndpoint: []*gaia.UserRoleEndpoint{
76+
NewUserRoleEndpoint("POST", "/api/v1/pipeline/:pipelineid/start"),
77+
},
78+
Description: "Start created pipelines.",
79+
},
80+
},
81+
},
82+
{
83+
Name: "PipelineRun",
84+
Description: "Managing of pipeline runs.",
85+
Roles: []*gaia.UserRole{
86+
{
87+
Name: "Stop",
88+
ApiEndpoint: []*gaia.UserRoleEndpoint{
89+
NewUserRoleEndpoint("POST", "/api/v1/pipelinerun/:pipelineid/:runid/stop"),
90+
},
91+
Description: "Stop running pipelines.",
92+
},
93+
{
94+
Name: "Get",
95+
ApiEndpoint: []*gaia.UserRoleEndpoint{
96+
NewUserRoleEndpoint("GET", "/api/v1/pipelinerun/:pipelineid/:runid"),
97+
NewUserRoleEndpoint("GET", "/api/v1/pipelinerun/:pipelineid/latest"),
98+
},
99+
Description: "Get pipeline runs.",
100+
},
101+
{
102+
Name: "List",
103+
ApiEndpoint: []*gaia.UserRoleEndpoint{
104+
NewUserRoleEndpoint("GET", "pipelinerun/:pipelineid"),
105+
},
106+
Description: "List pipeline runs.",
107+
},
108+
{
109+
Name: "Logs",
110+
ApiEndpoint: []*gaia.UserRoleEndpoint{
111+
NewUserRoleEndpoint("GET", "/api/v1/pipelinerun/:pipelineid/:runid/latest"),
112+
},
113+
Description: "Get logs for pipeline runs.",
114+
},
115+
},
116+
},
117+
{
118+
Name: "Secret",
119+
Description: "Managing of stored secrets used within pipelines.",
120+
Roles: []*gaia.UserRole{
121+
{
122+
Name: "List",
123+
ApiEndpoint: []*gaia.UserRoleEndpoint{
124+
NewUserRoleEndpoint("GET", "/api/v1/secrets"),
125+
},
126+
Description: "List created secrets.",
127+
},
128+
{
129+
Name: "Delete",
130+
ApiEndpoint: []*gaia.UserRoleEndpoint{
131+
NewUserRoleEndpoint("DELETE", "/api/v1/secret/:key"),
132+
},
133+
Description: "Delete created secrets.",
134+
},
135+
{
136+
Name: "Create",
137+
ApiEndpoint: []*gaia.UserRoleEndpoint{
138+
NewUserRoleEndpoint("POST", "/api/v1/secret"),
139+
},
140+
Description: "Create new secrets.",
141+
},
142+
{
143+
Name: "Update",
144+
ApiEndpoint: []*gaia.UserRoleEndpoint{
145+
NewUserRoleEndpoint("PUT", "/api/v1/secret/update"),
146+
},
147+
Description: "Update created secrets.",
148+
},
149+
},
150+
},
151+
{
152+
Name: "User",
153+
Description: "Managing of users.",
154+
Roles: []*gaia.UserRole{
155+
{
156+
Name: "Create",
157+
ApiEndpoint: []*gaia.UserRoleEndpoint{
158+
NewUserRoleEndpoint("POST", "/api/v1/user"),
159+
},
160+
Description: "Create new users.",
161+
},
162+
{
163+
Name: "List",
164+
ApiEndpoint: []*gaia.UserRoleEndpoint{
165+
NewUserRoleEndpoint("GET", "/api/v1/users"),
166+
},
167+
Description: "List created users.",
168+
},
169+
{
170+
Name: "ChangePassword",
171+
ApiEndpoint: []*gaia.UserRoleEndpoint{
172+
NewUserRoleEndpoint("POST", "/api/v1/user/password"),
173+
},
174+
Description: "Change created users passwords.",
175+
},
176+
{
177+
Name: "Delete",
178+
ApiEndpoint: []*gaia.UserRoleEndpoint{
179+
NewUserRoleEndpoint("DELETE", "/api/v1/user/:username"),
180+
},
181+
Description: "Delete created users.",
182+
},
183+
},
184+
},
185+
{
186+
Name: "UserPermission",
187+
Description: "Managing of user permissions.",
188+
Roles: []*gaia.UserRole{
189+
{
190+
Name: "Get",
191+
ApiEndpoint: []*gaia.UserRoleEndpoint{
192+
NewUserRoleEndpoint("GET", "/api/v1/user/:username/permissions"),
193+
},
194+
Description: "Get created users permissions.",
195+
},
196+
{
197+
Name: "Update",
198+
ApiEndpoint: []*gaia.UserRoleEndpoint{
199+
NewUserRoleEndpoint("PUT", "/api/v1/user/:username/permissions"),
200+
},
201+
Description: "Update created users permissions.",
202+
},
203+
},
204+
},
205+
}
206+
)

auth/role_test.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package auth
2+
3+
import (
4+
"testing"
5+
6+
"github.com/gaia-pipeline/gaia"
7+
)
8+
9+
var mockData = []*gaia.UserRoleCategory{
10+
{
11+
Name: "CategoryA",
12+
Roles: []*gaia.UserRole{
13+
{
14+
Name: "RoleA",
15+
},
16+
{
17+
Name: "RoleB",
18+
},
19+
},
20+
},
21+
{
22+
Name: "CategoryB",
23+
Roles: []*gaia.UserRole{
24+
{
25+
Name: "RoleA",
26+
},
27+
{
28+
Name: "RoleB",
29+
},
30+
},
31+
},
32+
}
33+
34+
func TestFlatRoleName(t *testing.T) {
35+
value := FullUserRoleName(mockData[0], mockData[0].Roles[0])
36+
expect := "CategoryARoleA"
37+
if value != expect {
38+
t.Fatalf("value %s should equal: %s", value, expect)
39+
}
40+
}
41+
42+
func TestFlattenUserCategoryRoles(t *testing.T) {
43+
value := FlattenUserCategoryRoles(mockData)
44+
expect := []string{"CategoryARoleA", "CategoryARoleB", "CategoryBRoleA", "CategoryBRoleB"}
45+
46+
for i := range expect {
47+
if expect[i] != value[i] {
48+
t.Fatalf("value %s should exist: %s", expect[i], value[i])
49+
}
50+
}
51+
}

frontend/client/App.vue

+3-3
Original file line numberDiff line numberDiff line change
@@ -245,19 +245,19 @@ html {
245245
height: 50px;
246246
}
247247
248-
.table td {
248+
.table-grid td {
249249
border: 0 !important;
250250
color: #8c91a0 !important;
251251
text-align: center !important;
252252
}
253253
254-
.table th {
254+
.table-grid th {
255255
border-top: solid black 2px !important;
256256
border-bottom: solid black 2px !important;
257257
color: #4da2fc !important;
258258
}
259259
260-
.table thead th {
260+
.table-grid thead th {
261261
color: #4da2fc;
262262
text-align: center !important;
263263
}

frontend/client/app.js

+36-21
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import Vue from 'vue'
22
import axios from 'axios'
33
import NProgress from 'vue-nprogress'
4-
import { sync } from 'vuex-router-sync'
4+
import {sync} from 'vuex-router-sync'
55
import App from './App.vue'
66
import router from './router'
77
import store from './store'
88
import * as filters from './filters'
9-
import { TOGGLE_SIDEBAR } from 'vuex-store/mutation-types'
9+
import {TOGGLE_SIDEBAR} from 'vuex-store/mutation-types'
1010
import Notification from 'vue-bulma-notification-fixed'
1111
import auth from './auth'
1212
import lodash from 'lodash'
@@ -72,31 +72,43 @@ function handleError (error) {
7272
})
7373
console.log(error.response.data.error)
7474
} else {
75-
if (error.response.status === 404) {
76-
openNotification({
77-
title: 'Error: 404',
78-
message: 'Not found',
79-
type: 'danger'
80-
})
81-
} else if (error.response.status === 403) {
82-
// Access denied
83-
openNotification({
84-
title: 'Error: 403',
85-
message: 'Not authorized. Please login first.',
86-
type: 'danger'
87-
})
88-
} else {
89-
openNotification({
90-
title: 'Error: ' + error.response.status.toString(),
91-
message: error.response.data,
92-
type: 'danger'
93-
})
75+
switch (error.response.status) {
76+
case 404:
77+
openNotification({
78+
title: 'Error: 404',
79+
message: 'Not found',
80+
type: 'danger'
81+
})
82+
break
83+
case 401:
84+
openNotification({
85+
title: 'Error: 401',
86+
message: 'Not authorized. Please login first.',
87+
type: 'danger'
88+
})
89+
break
90+
default:
91+
openNotification({
92+
title: 'Error: ' + error.response.status.toString(),
93+
message: error.response.data,
94+
type: 'danger'
95+
})
96+
break
9497
}
9598
console.log(error.response.data)
9699
}
97100
}
98101
Vue.prototype.$onError = handleError
99102

103+
Vue.prototype.$onSuccess = (title, message) => {
104+
openNotification({
105+
title: title,
106+
message: message,
107+
type: 'success',
108+
duration: message > 60 ? 20000 : 4500
109+
})
110+
}
111+
100112
router.beforeEach((route, redirect, next) => {
101113
if (state.app.device.isMobile && state.app.sidebar.opened) {
102114
store.commit(TOGGLE_SIDEBAR, false)
@@ -115,4 +127,7 @@ const app = new Vue({
115127
...App
116128
})
117129

130+
// A simple event bus
131+
export const EventBus = new Vue()
132+
118133
export { app, router, store }

frontend/client/views/pipeline/create.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@
124124
:global-search="true"
125125
:defaultSortBy="{field: 'created', type: 'desc'}"
126126
globalSearchPlaceholder="Search ..."
127-
styleClass="table table-own-bordered">
127+
styleClass="table table-grid table-own-bordered">
128128
<template slot="table-row" slot-scope="props">
129129
<td>{{ props.row.pipeline.name }}</td>
130130
<td class="progress-bar-height">

frontend/client/views/pipeline/detail.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
:global-search="true"
3535
:defaultSortBy="{field: 'id', type: 'desc'}"
3636
globalSearchPlaceholder="Search ..."
37-
styleClass="table table-own-bordered">
37+
styleClass="table table-grid table-own-bordered">
3838
<template slot="table-row" slot-scope="props">
3939
<td>
4040
<router-link :to="{ path: '/pipeline/detail', query: { pipelineid: pipelineID, runid: props.row.id }}" class="is-blue">

0 commit comments

Comments
 (0)