Skip to content

Commit 8378c9b

Browse files
committed
feat: add template ACL
1 parent 8f837b7 commit 8378c9b

22 files changed

+380
-59
lines changed

coderd/database/databasefake/databasefake.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,6 +1202,20 @@ func (q *fakeQuerier) GetTemplates(_ context.Context) ([]database.Template, erro
12021202
return templates, nil
12031203
}
12041204

1205+
func (q *fakeQuerier) UpdateTemplateUserACLByID(_ context.Context, id uuid.UUID, acl database.UserACL) error {
1206+
q.mutex.RLock()
1207+
defer q.mutex.RUnlock()
1208+
1209+
for i, t := range q.templates {
1210+
if t.ID == id {
1211+
t = t.SetUserACL(acl)
1212+
q.templates[i] = t
1213+
return nil
1214+
}
1215+
}
1216+
return sql.ErrNoRows
1217+
}
1218+
12051219
func (q *fakeQuerier) GetOrganizationMemberByUserID(_ context.Context, arg database.GetOrganizationMemberByUserIDParams) (database.OrganizationMember, error) {
12061220
q.mutex.RLock()
12071221
defer q.mutex.RUnlock()
@@ -1657,6 +1671,7 @@ func (q *fakeQuerier) InsertTemplate(_ context.Context, arg database.InsertTempl
16571671
MinAutostartInterval: arg.MinAutostartInterval,
16581672
CreatedBy: arg.CreatedBy,
16591673
}
1674+
template = template.SetUserACL(database.UserACL{})
16601675
q.templates = append(q.templates, template)
16611676
return template, nil
16621677
}

coderd/database/db.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"database/sql"
1414
"errors"
1515

16+
"github.com/jmoiron/sqlx"
1617
"golang.org/x/xerrors"
1718
)
1819

@@ -36,18 +37,25 @@ type DBTX interface {
3637
func New(sdb *sql.DB) Store {
3738
return &sqlQuerier{
3839
db: sdb,
39-
sdb: sdb,
40+
sdb: sqlx.NewDb(sdb, "postgres"),
4041
}
4142
}
4243

44+
// queries encompasses both are sqlc generated
45+
// queries and our custom queries.
46+
type querier interface {
47+
sqlcQuerier
48+
customQuerier
49+
}
50+
4351
type sqlQuerier struct {
44-
sdb *sql.DB
52+
sdb *sqlx.DB
4553
db DBTX
4654
}
4755

4856
// InTx performs database operations inside a transaction.
4957
func (q *sqlQuerier) InTx(function func(Store) error) error {
50-
if _, ok := q.db.(*sql.Tx); ok {
58+
if _, ok := q.db.(*sqlx.Tx); ok {
5159
// If the current inner "db" is already a transaction, we just reuse it.
5260
// We do not need to handle commit/rollback as the outer tx will handle
5361
// that.

coderd/database/dump.sql

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/generate.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}")
4242
rm -f queries/*.go
4343

4444
# Fix struct/interface names.
45-
gofmt -w -r 'Querier -> querier' -- *.go
45+
gofmt -w -r 'Querier -> sqlcQuerier' -- *.go
4646
gofmt -w -r 'Queries -> sqlQuerier' -- *.go
4747

4848
# Ensure correct imports exist. Modules must all be downloaded so we get correct

coderd/database/migrations/000050_template_acl.down.sql

Whitespace-only changes.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
BEGIN;
2+
3+
ALTER TABLE templates ADD COLUMN user_acl jsonb NOT NULL default '{}';
4+
5+
CREATE TYPE template_role AS ENUM (
6+
'read',
7+
'write',
8+
'admin'
9+
);
10+
11+
COMMIT;

coderd/database/modelmethods.go

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,63 @@
11
package database
22

33
import (
4+
"encoding/json"
5+
"fmt"
6+
47
"github.com/coder/coder/coderd/rbac"
58
)
69

10+
// UserACL is a map of user_ids to permissions.
11+
type UserACL map[string]TemplateRole
12+
13+
func (u UserACL) Actions() map[string][]rbac.Action {
14+
aclRBAC := make(map[string][]rbac.Action, len(u))
15+
for k, v := range u {
16+
aclRBAC[k] = templateRoleToActions(v)
17+
}
18+
19+
return aclRBAC
20+
}
21+
22+
func (t Template) UserACL() UserACL {
23+
var acl UserACL
24+
err := json.Unmarshal(t.userACL, &acl)
25+
if err != nil {
26+
panic(fmt.Sprintf("failed to unmarshal template.userACL: %v", err.Error()))
27+
}
28+
29+
return acl
30+
}
31+
32+
func (t Template) SetUserACL(acl UserACL) Template {
33+
raw, err := json.Marshal(acl)
34+
if err != nil {
35+
panic(fmt.Sprintf("marshal user acl: %v", err))
36+
}
37+
38+
t.userACL = raw
39+
return t
40+
}
41+
42+
func templateRoleToActions(t TemplateRole) []rbac.Action {
43+
switch t {
44+
case TemplateRoleRead:
45+
return []rbac.Action{rbac.ActionRead}
46+
case TemplateRoleWrite:
47+
return []rbac.Action{rbac.ActionRead, rbac.ActionUpdate}
48+
case TemplateRoleAdmin:
49+
return []rbac.Action{rbac.WildcardSymbol}
50+
}
51+
return nil
52+
}
53+
754
func (t Template) RBACObject() rbac.Object {
8-
return rbac.ResourceTemplate.InOrg(t.OrganizationID)
55+
return rbac.ResourceTemplate.InOrg(t.OrganizationID).WithACLUserList(t.UserACL().Actions())
956
}
1057

11-
func (t TemplateVersion) RBACObject() rbac.Object {
58+
func (t TemplateVersion) RBACObject(template Template) rbac.Object {
1259
// Just use the parent template resource for controlling versions
13-
return rbac.ResourceTemplate.InOrg(t.OrganizationID)
60+
return rbac.ResourceTemplate.InOrg(t.OrganizationID).WithACLUserList(template.UserACL().Actions())
1461
}
1562

1663
func (w Workspace) RBACObject() rbac.Object {

coderd/database/modelqueries.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package database
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
7+
"github.com/google/uuid"
8+
"golang.org/x/xerrors"
9+
)
10+
11+
// customQuerier encompasses all non-generated queries.
12+
// It provides a flexible way to write queries for cases
13+
// where sqlc proves inadequate.
14+
type customQuerier interface {
15+
templateQuerier
16+
}
17+
18+
type templateQuerier interface {
19+
UpdateTemplateUserACLByID(ctx context.Context, id uuid.UUID, acl UserACL) error
20+
}
21+
22+
type TemplateUser struct {
23+
User
24+
Role TemplateRole `db:"role"`
25+
}
26+
27+
func (q *sqlQuerier) UpdateTemplateUserACLByID(ctx context.Context, id uuid.UUID, acl UserACL) error {
28+
raw, err := json.Marshal(acl)
29+
if err != nil {
30+
return xerrors.Errorf("marshal user acl: %w", err)
31+
}
32+
33+
const query = `
34+
UPDATE
35+
templates
36+
SET
37+
user_acl = $2
38+
WHERE
39+
id = $1`
40+
41+
_, err = q.db.ExecContext(ctx, query, id.String(), raw)
42+
if err != nil {
43+
return xerrors.Errorf("update user acl: %w", err)
44+
}
45+
46+
return nil
47+
}

coderd/database/models.go

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/models_custom.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package database

coderd/database/querier.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

Lines changed: 16 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)