Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,17 @@ func New(options *Options) *API {
// bugs that may only occur when a key isn't precached in tests and the latency cost is minimal.
cryptokeys.StartRotator(ctx, options.Logger, options.Database)

// Ensure system role permissions are current for all organizations.
// This backfills permissions for roles created by migration and
// updates permissions when new RBAC resources are added, while
// using advisory lock for multi-replica safety.
err = ReconcileOrgMemberRoles(ctx, options.Logger, options.Database)
if err != nil {
// TODO:(geokat) Not using fatal here and just continuing after
// logging the error would be a potential security hole, right?
options.Logger.Fatal(ctx, "failed to reconcile orgMember role permissions", slog.Error(err))
}

// AGPL uses a no-op build usage checker as there are no license
// entitlements to enforce. This is swapped out in
// enterprise/coderd/coderd.go.
Expand Down
4 changes: 4 additions & 0 deletions coderd/database/dbauthz/dbauthz.go
Original file line number Diff line number Diff line change
Expand Up @@ -4110,6 +4110,8 @@ func (q *querier) InsertCustomRole(ctx context.Context, arg database.InsertCusto
return database.CustomRole{}, err
}

// TODO(geokat): should we add MemberPermissions validation too
// now that we have them in system roles?
if err := q.customRoleCheck(ctx, database.CustomRole{
Name: arg.Name,
DisplayName: arg.DisplayName,
Expand Down Expand Up @@ -4864,6 +4866,8 @@ func (q *querier) UpdateCustomRole(ctx context.Context, arg database.UpdateCusto
return database.CustomRole{}, err
}

// TODO(geokat): should we add MemberPermissions validation too
// now that we have them in system roles?
if err := q.customRoleCheck(ctx, database.CustomRole{
Name: arg.Name,
DisplayName: arg.DisplayName,
Expand Down
11 changes: 8 additions & 3 deletions coderd/database/dump.sql

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions coderd/database/lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const (
LockIDNotificationsReportGenerator
LockIDCryptoKeyRotation
LockIDReconcilePrebuilds
LockIDReconcileOrgMemberRoles
)

// GenLockID generates a unique and consistent lock ID from a given string.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ALTER TABLE custom_roles DROP COLUMN IF EXISTS member_permissions;

ALTER TABLE custom_roles DROP COLUMN IF EXISTS is_system;
10 changes: 10 additions & 0 deletions coderd/database/migrations/000404_add_system_role_support.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- Add is_system column to identify system-managed roles.
ALTER TABLE custom_roles
ADD COLUMN is_system boolean NOT NULL DEFAULT false;

-- Add member_permissions column for member-scoped permissions within an organization.
ALTER TABLE custom_roles
ADD COLUMN member_permissions jsonb NOT NULL DEFAULT '[]'::jsonb;

COMMENT ON COLUMN custom_roles.is_system IS
'System roles are managed by Coder and cannot be modified or deleted by users.';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE organizations DROP COLUMN IF EXISTS workspace_sharing_disabled;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE organizations
ADD COLUMN workspace_sharing_disabled boolean NOT NULL DEFAULT false;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- Remove organization-member system roles created by the up migration.
DELETE FROM
custom_roles
WHERE
name = 'organization-member'
AND is_system = true;

-- Restore the original unique constraint (name only, no organization_id).
DROP INDEX IF EXISTS idx_custom_roles_name_lower_organization_id;

CREATE UNIQUE INDEX idx_custom_roles_name_lower ON custom_roles USING btree (lower(name));
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
-- Fix the unique constraint on custom_roles to allow the same role
-- name in different organizations. The original constraint only
-- covered name, but roles should be unique per (name,
-- organization_id).
DROP INDEX IF EXISTS idx_custom_roles_name_lower;

-- Use COALESCE to handle NULL organization_id. Site-wide custom roles
-- are currently not used, but that can change in the future and this
-- will be necessary. And there are no performance implications.
CREATE UNIQUE INDEX idx_custom_roles_name_lower_organization_id ON custom_roles USING btree (
lower(name),
COALESCE(organization_id, '00000000-0000-0000-0000-000000000000'::uuid)
);

-- Create placeholder organization-member system roles for existing
-- organizations. Permissions are empty here and will be populated by
-- the startup hook.
INSERT INTO custom_roles (
name,
display_name,
organization_id,
site_permissions,
org_permissions,
user_permissions,
member_permissions,
is_system,
created_at,
updated_at
)
SELECT
'organization-member', -- reserved role name, so it doesn't exist in DB yet
'',
id,
'[]'::jsonb,
'[]'::jsonb,
'[]'::jsonb,
'[]'::jsonb,
true,
NOW(),
NOW()
FROM
organizations
WHERE
deleted = false
AND NOT EXISTS (
SELECT 1
FROM custom_roles
WHERE
custom_roles.name = 'organization-member'
AND custom_roles.organization_id = organizations.id
);
22 changes: 13 additions & 9 deletions coderd/database/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading