-
Notifications
You must be signed in to change notification settings - Fork 881
feat: add template RBAC #4125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add template RBAC #4125
Changes from all commits
5a47132
03f69bf
91a358d
8f837b7
8378c9b
54a0d13
72ea751
d533a16
f56fcf9
f162694
ea25c08
5a081eb
072b3e4
205c36c
ba32928
5c6344f
ef15908
8ab5200
c040e8e
1f4ceee
7cc71e1
131d5ed
8c3ee6a
fe2af91
0218c4e
6883106
4fbd9be
c96a6ca
57ba8b3
c66d247
0af367a
f6c3f51
6e72286
44bcbde
0f80beb
d274d62
943c76b
7f7f1d3
967a1a9
1324991
bd34d20
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
BEGIN; | ||
|
||
ALTER TABLE templates DROP COLUMN user_acl; | ||
ALTER TABLE templates DROP COLUMN is_private; | ||
DROP TYPE template_role; | ||
|
||
COMMIT; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
BEGIN; | ||
|
||
ALTER TABLE templates ADD COLUMN user_acl jsonb NOT NULL default '{}'; | ||
ALTER TABLE templates ADD COLUMN is_private boolean NOT NULL default 'false'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
CREATE TYPE template_role AS ENUM ( | ||
'read', | ||
'write', | ||
'admin' | ||
); | ||
|
||
COMMIT; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package database | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
|
||
"github.com/google/uuid" | ||
"golang.org/x/xerrors" | ||
) | ||
|
||
// customQuerier encompasses all non-generated queries. | ||
// It provides a flexible way to write queries for cases | ||
// where sqlc proves inadequate. | ||
type customQuerier interface { | ||
templateQuerier | ||
} | ||
|
||
type templateQuerier interface { | ||
UpdateTemplateUserACLByID(ctx context.Context, id uuid.UUID, acl UserACL) error | ||
GetTemplateUserRoles(ctx context.Context, id uuid.UUID) ([]TemplateUser, error) | ||
} | ||
|
||
type TemplateUser struct { | ||
User | ||
Role TemplateRole `db:"role"` | ||
} | ||
|
||
func (q *sqlQuerier) UpdateTemplateUserACLByID(ctx context.Context, id uuid.UUID, acl UserACL) error { | ||
raw, err := json.Marshal(acl) | ||
if err != nil { | ||
return xerrors.Errorf("marshal user acl: %w", err) | ||
} | ||
|
||
const query = ` | ||
UPDATE | ||
templates | ||
SET | ||
user_acl = $2 | ||
WHERE | ||
id = $1` | ||
Comment on lines
+35
to
+40
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why can't this be done with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. because it exposes a |
||
|
||
_, err = q.db.ExecContext(ctx, query, id.String(), raw) | ||
if err != nil { | ||
return xerrors.Errorf("update user acl: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (q *sqlQuerier) GetTemplateUserRoles(ctx context.Context, id uuid.UUID) ([]TemplateUser, error) { | ||
const query = ` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not convinced we should escape sqlc here... this function is only used once, so why not just do the struct conversion where it's queried from instead? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there's unfortunately multiple problems when I tried to write this with |
||
SELECT | ||
perms.value as role, users.* | ||
FROM | ||
users | ||
JOIN | ||
( | ||
SELECT | ||
* | ||
FROM | ||
jsonb_each_text( | ||
( | ||
SELECT | ||
templates.user_acl | ||
FROM | ||
templates | ||
WHERE | ||
id = $1 | ||
) | ||
) | ||
) AS perms | ||
ON | ||
users.id::text = perms.key; | ||
` | ||
|
||
var tus []TemplateUser | ||
err := q.db.SelectContext(ctx, &tus, query, id.String()) | ||
if err != nil { | ||
return nil, xerrors.Errorf("select context: %w", err) | ||
} | ||
|
||
return tus, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why store this as a massive
jsonb
blob instead of a table? How do we plan on efficiently getting a list of all templates the user has access to?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's basically the same way everything else is done. You get a list of all the templates and then you filter it through the auth filter for the subset of templates that you have access to. Just wrote a test for this here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For workspaces, it kinda does this, but we still query by the owner so it's lessened significantly.
I guess the use-case I have in mind is 10 templates with 10 users on each... for every HTTP request we'll load 100 entries into memory and check against them? That sounds like a lot of excess when we could (I don't see why not, but maybe there's a reason) have a table that just has the user ID indexed...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The memory overhead is unfortunate but this way plays better with rego is the short answer. We can look into rewriting it if you think it's a showstopper, I'm not sold on
jsonb
but joining tables doesn't play well with sqlc either so the maintainability of the code might suffer as a result of trying to glue everything together.