Skip to content

Commit bba2cdb

Browse files
committed
chore: Ensure Authorized SQL clause
In authorized database calls we want to ensure the authorized sql string is always present. If it is not, the sql query has no Authorized filter. This is just a safeguard
1 parent 8dd567d commit bba2cdb

File tree

2 files changed

+36
-3
lines changed

2 files changed

+36
-3
lines changed

coderd/database/modelqueries.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,10 @@ type workspaceQuerier interface {
121121
func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspacesParams, authorizedFilter rbac.AuthorizeFilter) ([]Workspace, error) {
122122
// In order to properly use ORDER BY, OFFSET, and LIMIT, we need to inject the
123123
// authorizedFilter between the end of the where clause and those statements.
124-
filter := strings.Replace(getWorkspaces, "-- @authorize_filter", fmt.Sprintf(" AND %s", authorizedFilter.SQLString(rbac.NoACLConfig())), 1)
124+
filter, err := insertAuthorizedFilter(getWorkspaceCount, authorizedFilter, rbac.NoACLConfig())
125+
if err != nil {
126+
return nil, err
127+
}
125128
// The name comment is for metric tracking
126129
query := fmt.Sprintf("-- name: GetAuthorizedWorkspaces :many\n%s", filter)
127130
rows, err := q.db.QueryContext(ctx, query,
@@ -171,7 +174,10 @@ func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspa
171174
func (q *sqlQuerier) GetAuthorizedWorkspaceCount(ctx context.Context, arg GetWorkspaceCountParams, authorizedFilter rbac.AuthorizeFilter) (int64, error) {
172175
// In order to properly use ORDER BY, OFFSET, and LIMIT, we need to inject the
173176
// authorizedFilter between the end of the where clause and those statements.
174-
filter := strings.Replace(getWorkspaceCount, "-- @authorize_filter", fmt.Sprintf(" AND %s", authorizedFilter.SQLString(rbac.NoACLConfig())), 1)
177+
filter, err := insertAuthorizedFilter(getWorkspaceCount, authorizedFilter, rbac.NoACLConfig())
178+
if err != nil {
179+
return -1, err
180+
}
175181
// The name comment is for metric tracking
176182
query := fmt.Sprintf("-- name: GetAuthorizedWorkspaceCount :one\n%s", filter)
177183
row := q.db.QueryRowContext(ctx, query,
@@ -184,6 +190,19 @@ func (q *sqlQuerier) GetAuthorizedWorkspaceCount(ctx context.Context, arg GetWor
184190
arg.Name,
185191
)
186192
var count int64
187-
err := row.Scan(&count)
193+
err = row.Scan(&count)
188194
return count, err
189195
}
196+
197+
// insertAuthorizedFilter is used to replace the @authorized_filter placeholder.
198+
// It is crucial the placeholder exists, otherwise no auth checks are done. This
199+
// is to prevent that mistake. If this function returns an error, it is
200+
// a developer error.
201+
func insertAuthorizedFilter(query string, authorizedFilter rbac.AuthorizeFilter, config rbac.SQLConfig) (string, error) {
202+
replace := "-- @authorize_filter"
203+
if !strings.Contains(query, replace) {
204+
return "", xerrors.Errorf("query does not contain authorized replace string, this is not an authorized query")
205+
}
206+
filter := strings.Replace(getWorkspaces, "-- @authorize_filter", fmt.Sprintf(" AND %s", authorizedFilter.SQLString(config)), 1)
207+
return filter, nil
208+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package database
2+
3+
import (
4+
"testing"
5+
6+
"github.com/coder/coder/coderd/rbac"
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestInsertAuthorized(t *testing.T) {
11+
query := `SELECT true;`
12+
_, err := insertAuthorizedFilter(query, nil, rbac.NoACLConfig())
13+
require.ErrorContains(t, err, "does not contain authorized replace string", "ensure replace string")
14+
}

0 commit comments

Comments
 (0)