Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7fa5afa

Browse files
EmyrkBrunoQuaresma
andauthored
fix: Users that can update a template can also read the file (#6776)
* fix: Users that can update a template can also read the file This currently has a strange RBAC story. An issue will be filed to streamline this. This is a hotfix to resolve current functionality * Only showsource code tab if the user has permission to edit the template --------- Co-authored-by: Bruno Quaresma <[email protected]>
1 parent fc21e15 commit 7fa5afa

File tree

9 files changed

+262
-13
lines changed

9 files changed

+262
-13
lines changed

coderd/database/dbauthz/querier.go

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,57 @@ func (q *querier) GetAuditLogsOffset(ctx context.Context, arg database.GetAuditL
8888
}
8989

9090
func (q *querier) GetFileByHashAndCreator(ctx context.Context, arg database.GetFileByHashAndCreatorParams) (database.File, error) {
91-
return fetch(q.log, q.auth, q.db.GetFileByHashAndCreator)(ctx, arg)
91+
file, err := q.db.GetFileByHashAndCreator(ctx, arg)
92+
if err != nil {
93+
return database.File{}, err
94+
}
95+
err = q.authorizeContext(ctx, rbac.ActionRead, file)
96+
if err != nil {
97+
// Check the user's access to the file's templates.
98+
if q.authorizeUpdateFileTemplate(ctx, file) != nil {
99+
return database.File{}, err
100+
}
101+
}
102+
103+
return file, nil
92104
}
93105

94106
func (q *querier) GetFileByID(ctx context.Context, id uuid.UUID) (database.File, error) {
95-
return fetch(q.log, q.auth, q.db.GetFileByID)(ctx, id)
107+
file, err := q.db.GetFileByID(ctx, id)
108+
if err != nil {
109+
return database.File{}, err
110+
}
111+
err = q.authorizeContext(ctx, rbac.ActionRead, file)
112+
if err != nil {
113+
// Check the user's access to the file's templates.
114+
if q.authorizeUpdateFileTemplate(ctx, file) != nil {
115+
return database.File{}, err
116+
}
117+
}
118+
119+
return file, nil
120+
}
121+
122+
// authorizeReadFile is a hotfix for the fact that file permissions are
123+
// independent of template permissions. This function checks if the user has
124+
// update access to any of the file's templates.
125+
func (q *querier) authorizeUpdateFileTemplate(ctx context.Context, file database.File) error {
126+
tpls, err := q.db.GetFileTemplates(ctx, file.ID)
127+
if err != nil {
128+
return err
129+
}
130+
// There __should__ only be 1 template per file, but there can be more than
131+
// 1, so check them all.
132+
for _, tpl := range tpls {
133+
// If the user has update access to any template, they have read access to the file.
134+
if err := q.authorizeContext(ctx, rbac.ActionUpdate, tpl); err == nil {
135+
return nil
136+
}
137+
}
138+
139+
return NotAuthorizedError{
140+
Err: xerrors.Errorf("not authorized to read file %s", file.ID),
141+
}
96142
}
97143

98144
func (q *querier) InsertFile(ctx context.Context, arg database.InsertFileParams) (database.File, error) {

coderd/database/dbauthz/system.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ import (
1010
"github.com/coder/coder/coderd/rbac"
1111
)
1212

13+
func (q *querier) GetFileTemplates(ctx context.Context, fileID uuid.UUID) ([]database.GetFileTemplatesRow, error) {
14+
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
15+
return nil, err
16+
}
17+
return q.db.GetFileTemplates(ctx, fileID)
18+
}
19+
1320
// GetWorkspaceAppsByAgentIDs
1421
// The workspace/job is already fetched.
1522
func (q *querier) GetWorkspaceAppsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceApp, error) {

coderd/database/dbfake/databasefake.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,47 @@ func (q *fakeQuerier) GetFileByID(_ context.Context, id uuid.UUID) (database.Fil
685685
return database.File{}, sql.ErrNoRows
686686
}
687687

688+
func (q *fakeQuerier) GetFileTemplates(_ context.Context, id uuid.UUID) ([]database.GetFileTemplatesRow, error) {
689+
q.mutex.RLock()
690+
defer q.mutex.RUnlock()
691+
692+
rows := make([]database.GetFileTemplatesRow, 0)
693+
var file database.File
694+
for _, f := range q.files {
695+
if f.ID == id {
696+
file = f
697+
break
698+
}
699+
}
700+
if file.Hash == "" {
701+
return rows, nil
702+
}
703+
704+
for _, job := range q.provisionerJobs {
705+
if job.FileID == id {
706+
for _, version := range q.templateVersions {
707+
if version.JobID == job.ID {
708+
for _, template := range q.templates {
709+
if template.ID == version.TemplateID.UUID {
710+
rows = append(rows, database.GetFileTemplatesRow{
711+
FileID: file.ID,
712+
FileCreatedBy: file.CreatedBy,
713+
TemplateID: template.ID,
714+
TemplateOrganizationID: template.OrganizationID,
715+
TemplateCreatedBy: template.CreatedBy,
716+
UserACL: template.UserACL,
717+
GroupACL: template.GroupACL,
718+
})
719+
}
720+
}
721+
}
722+
}
723+
}
724+
}
725+
726+
return rows, nil
727+
}
728+
688729
func (q *fakeQuerier) GetUserByEmailOrUsername(_ context.Context, arg database.GetUserByEmailOrUsernameParams) (database.User, error) {
689730
if err := validateDatabaseType(arg); err != nil {
690731
return database.User{}, err

coderd/database/modelmethods.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ func (t Template) RBACObject() rbac.Object {
8888
WithGroupACL(t.GroupACL)
8989
}
9090

91+
func (t GetFileTemplatesRow) RBACObject() rbac.Object {
92+
return rbac.ResourceTemplate.WithID(t.TemplateID).
93+
InOrg(t.TemplateOrganizationID).
94+
WithACLUserList(t.UserACL).
95+
WithGroupACL(t.GroupACL)
96+
}
97+
9198
func (t Template) DeepCopy() Template {
9299
cpy := t
93100
cpy.UserACL = maps.Clone(t.UserACL)

coderd/database/querier.go

Lines changed: 2 additions & 0 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: 69 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/files.sql

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,31 @@ INSERT INTO
2626
files (id, hash, created_at, created_by, mimetype, "data")
2727
VALUES
2828
($1, $2, $3, $4, $5, $6) RETURNING *;
29+
30+
-- name: GetFileTemplates :many
31+
-- Get all templates that use a file.
32+
SELECT
33+
files.id AS file_id,
34+
files.created_by AS file_created_by,
35+
templates.id AS template_id,
36+
templates.organization_id AS template_organization_id,
37+
templates.created_by AS template_created_by,
38+
templates.user_acl,
39+
templates.group_acl
40+
FROM
41+
templates
42+
INNER JOIN
43+
template_versions
44+
ON templates.id = template_versions.template_id
45+
INNER JOIN
46+
provisioner_jobs
47+
ON job_id = provisioner_jobs.id
48+
INNER JOIN
49+
files
50+
ON files.id = provisioner_jobs.file_id
51+
WHERE
52+
-- Only fetch template version associated files.
53+
storage_method = 'file'
54+
AND provisioner_jobs.type = 'template_version_import'
55+
AND file_id = @file_id
56+
;

enterprise/coderd/templates_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,53 @@ func TestUpdateTemplateACL(t *testing.T) {
976976
})
977977
}
978978

979+
func TestReadFileWithTemplateUpdate(t *testing.T) {
980+
t.Parallel()
981+
t.Run("HasTemplateUpdate", func(t *testing.T) {
982+
t.Parallel()
983+
ctx := testutil.Context(t, testutil.WaitMedium)
984+
985+
// Upload a file
986+
client := coderdenttest.New(t, nil)
987+
first := coderdtest.CreateFirstUser(t, client)
988+
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
989+
Features: license.Features{
990+
codersdk.FeatureTemplateRBAC: 1,
991+
},
992+
})
993+
994+
resp, err := client.Upload(ctx, codersdk.ContentTypeTar, bytes.NewReader(make([]byte, 1024)))
995+
require.NoError(t, err)
996+
997+
// Make a new user
998+
member, memberData := coderdtest.CreateAnotherUser(t, client, first.OrganizationID)
999+
1000+
// Try to download file, this should fail
1001+
_, _, err = member.Download(ctx, resp.ID)
1002+
require.Error(t, err, "no template yet")
1003+
1004+
// Make a new template version with the file
1005+
version := coderdtest.CreateTemplateVersion(t, client, first.OrganizationID, nil, func(request *codersdk.CreateTemplateVersionRequest) {
1006+
request.FileID = resp.ID
1007+
})
1008+
template := coderdtest.CreateTemplate(t, client, first.OrganizationID, version.ID)
1009+
1010+
// Not in acl yet
1011+
_, _, err = member.Download(ctx, resp.ID)
1012+
require.Error(t, err, "not in acl yet")
1013+
1014+
err = client.UpdateTemplateACL(ctx, template.ID, codersdk.UpdateTemplateACL{
1015+
UserPerms: map[string]codersdk.TemplateRole{
1016+
memberData.ID.String(): codersdk.TemplateRoleAdmin,
1017+
},
1018+
})
1019+
require.NoError(t, err)
1020+
1021+
_, _, err = member.Download(ctx, resp.ID)
1022+
require.NoError(t, err)
1023+
})
1024+
}
1025+
9791026
// TestTemplateAccess tests the rego -> sql conversion. We need to implement
9801027
// this test on at least 1 table type to ensure that the conversion is correct.
9811028
// The rbac tests only assert against static SQL queries.

site/src/components/TemplateLayout/TemplateLayout.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -110,17 +110,19 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
110110
>
111111
Summary
112112
</NavLink>
113-
<NavLink
114-
to={`/templates/${templateName}/files`}
115-
className={({ isActive }) =>
116-
combineClasses([
117-
styles.tabItem,
118-
isActive ? styles.tabItemActive : undefined,
119-
])
120-
}
121-
>
122-
Source Code
123-
</NavLink>
113+
{data.permissions.canUpdateTemplate && (
114+
<NavLink
115+
to={`/templates/${templateName}/files`}
116+
className={({ isActive }) =>
117+
combineClasses([
118+
styles.tabItem,
119+
isActive ? styles.tabItemActive : undefined,
120+
])
121+
}
122+
>
123+
Source Code
124+
</NavLink>
125+
)}
124126
</Stack>
125127
</Margins>
126128
</div>

0 commit comments

Comments
 (0)