mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add user_secrets table (#19162)
Closes https://github.com/coder/internal/issues/780 ## Summary of changes: - added `user_secrets` table - `user_secrets` table contains `env_name` and `file_path` fields which are not used at the moment, but will be used in later PRs - `user_secrets` table doesn't contain `value_key_id`, I will add it in a separate migration in a dbcrypt PR - on one hand I don't want to add fields which are not used (because it's a risk smth may change in implementation later), on the other hand I don't want to add too many migrations for user secrets table - added unique sql indexes - added sql queries for CRUD operations on user-secrets - introduced new `ResourceUserSecret` resource - basic unit-tests for CRUD ops and authorization behavior - Role updates: - owner: - remove `ResourceUserSecret` from site-wide perms - add `ResourceUserSecret` to user-wide perms - orgAdmin - remove `ResourceUserSecret` from org-wide perms; seems it's not strictly required, because `ResourceUserSecret` is not tied to organization in dbauthz wrappers? - memberRole - no need to change memberRole because it implicitly has access to user-secrets thanks to the `allPermsExcept` - is it enough changes to roles? Main questions: - [ ] We will have 2 migrations for user-secrets: - initial migration (in current PR) - adding `value_key_id` in dbcrypt PR - is this approach reasonable? - [ ] Are changes to roles's permissions are correct? - [ ] Are changes in roles_test.go are correct? --------- Co-authored-by: Steven Masley <Emyrk@users.noreply.github.com>
This commit is contained in:
co-authored by
Steven Masley
parent
34c46c0748
commit
c65996a041
@@ -13807,6 +13807,196 @@ func (q *sqlQuerier) UpdateUserLinkedID(ctx context.Context, arg UpdateUserLinke
|
||||
return i, err
|
||||
}
|
||||
|
||||
const createUserSecret = `-- name: CreateUserSecret :one
|
||||
INSERT INTO user_secrets (
|
||||
id,
|
||||
user_id,
|
||||
name,
|
||||
description,
|
||||
value,
|
||||
env_name,
|
||||
file_path
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6, $7
|
||||
) RETURNING id, user_id, name, description, value, env_name, file_path, created_at, updated_at
|
||||
`
|
||||
|
||||
type CreateUserSecretParams struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Description string `db:"description" json:"description"`
|
||||
Value string `db:"value" json:"value"`
|
||||
EnvName string `db:"env_name" json:"env_name"`
|
||||
FilePath string `db:"file_path" json:"file_path"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) CreateUserSecret(ctx context.Context, arg CreateUserSecretParams) (UserSecret, error) {
|
||||
row := q.db.QueryRowContext(ctx, createUserSecret,
|
||||
arg.ID,
|
||||
arg.UserID,
|
||||
arg.Name,
|
||||
arg.Description,
|
||||
arg.Value,
|
||||
arg.EnvName,
|
||||
arg.FilePath,
|
||||
)
|
||||
var i UserSecret
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Name,
|
||||
&i.Description,
|
||||
&i.Value,
|
||||
&i.EnvName,
|
||||
&i.FilePath,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteUserSecret = `-- name: DeleteUserSecret :exec
|
||||
DELETE FROM user_secrets
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) DeleteUserSecret(ctx context.Context, id uuid.UUID) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteUserSecret, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const getUserSecret = `-- name: GetUserSecret :one
|
||||
SELECT id, user_id, name, description, value, env_name, file_path, created_at, updated_at FROM user_secrets
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetUserSecret(ctx context.Context, id uuid.UUID) (UserSecret, error) {
|
||||
row := q.db.QueryRowContext(ctx, getUserSecret, id)
|
||||
var i UserSecret
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Name,
|
||||
&i.Description,
|
||||
&i.Value,
|
||||
&i.EnvName,
|
||||
&i.FilePath,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserSecretByUserIDAndName = `-- name: GetUserSecretByUserIDAndName :one
|
||||
SELECT id, user_id, name, description, value, env_name, file_path, created_at, updated_at FROM user_secrets
|
||||
WHERE user_id = $1 AND name = $2
|
||||
`
|
||||
|
||||
type GetUserSecretByUserIDAndNameParams struct {
|
||||
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) GetUserSecretByUserIDAndName(ctx context.Context, arg GetUserSecretByUserIDAndNameParams) (UserSecret, error) {
|
||||
row := q.db.QueryRowContext(ctx, getUserSecretByUserIDAndName, arg.UserID, arg.Name)
|
||||
var i UserSecret
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Name,
|
||||
&i.Description,
|
||||
&i.Value,
|
||||
&i.EnvName,
|
||||
&i.FilePath,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listUserSecrets = `-- name: ListUserSecrets :many
|
||||
SELECT id, user_id, name, description, value, env_name, file_path, created_at, updated_at FROM user_secrets
|
||||
WHERE user_id = $1
|
||||
ORDER BY name ASC
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) ListUserSecrets(ctx context.Context, userID uuid.UUID) ([]UserSecret, error) {
|
||||
rows, err := q.db.QueryContext(ctx, listUserSecrets, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []UserSecret
|
||||
for rows.Next() {
|
||||
var i UserSecret
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Name,
|
||||
&i.Description,
|
||||
&i.Value,
|
||||
&i.EnvName,
|
||||
&i.FilePath,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateUserSecret = `-- name: UpdateUserSecret :one
|
||||
UPDATE user_secrets
|
||||
SET
|
||||
description = $2,
|
||||
value = $3,
|
||||
env_name = $4,
|
||||
file_path = $5,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $1
|
||||
RETURNING id, user_id, name, description, value, env_name, file_path, created_at, updated_at
|
||||
`
|
||||
|
||||
type UpdateUserSecretParams struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
Description string `db:"description" json:"description"`
|
||||
Value string `db:"value" json:"value"`
|
||||
EnvName string `db:"env_name" json:"env_name"`
|
||||
FilePath string `db:"file_path" json:"file_path"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) UpdateUserSecret(ctx context.Context, arg UpdateUserSecretParams) (UserSecret, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateUserSecret,
|
||||
arg.ID,
|
||||
arg.Description,
|
||||
arg.Value,
|
||||
arg.EnvName,
|
||||
arg.FilePath,
|
||||
)
|
||||
var i UserSecret
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Name,
|
||||
&i.Description,
|
||||
&i.Value,
|
||||
&i.EnvName,
|
||||
&i.FilePath,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const allUserIDs = `-- name: AllUserIDs :many
|
||||
SELECT DISTINCT id FROM USERS
|
||||
WHERE CASE WHEN $1::bool THEN TRUE ELSE is_system = false END
|
||||
|
||||
Reference in New Issue
Block a user