mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: configurable default org member roles (#25994)
Refs #25936. Adds a configurable per-org default member role set. Unioned into each member's effective roles at read time. <sub>with Coder Agents on behalf of @Emyrk.</sub>
This commit is contained in:
@@ -902,10 +902,11 @@ func Organization(organization database.Organization) codersdk.Organization {
|
||||
DisplayName: organization.DisplayName,
|
||||
Icon: organization.Icon,
|
||||
},
|
||||
Description: organization.Description,
|
||||
CreatedAt: organization.CreatedAt,
|
||||
UpdatedAt: organization.UpdatedAt,
|
||||
IsDefault: organization.IsDefault,
|
||||
Description: organization.Description,
|
||||
CreatedAt: organization.CreatedAt,
|
||||
UpdatedAt: organization.UpdatedAt,
|
||||
IsDefault: organization.IsDefault,
|
||||
DefaultOrgMemberRoles: organization.DefaultOrgMemberRoles,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1602,6 +1602,19 @@ func (q *querier) authorizeProvisionerJob(ctx context.Context, job database.Prov
|
||||
return nil
|
||||
}
|
||||
|
||||
// scopedOrgRoleIdentifiers wraps each role name as a RoleIdentifier scoped
|
||||
// to orgID. Used to feed rbac.ChangeRoleSet from a stored []string.
|
||||
func scopedOrgRoleIdentifiers(names []string, orgID uuid.UUID) []rbac.RoleIdentifier {
|
||||
if len(names) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]rbac.RoleIdentifier, len(names))
|
||||
for i, name := range names {
|
||||
out[i] = rbac.RoleIdentifier{Name: name, OrganizationID: orgID}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (q *querier) AcquireChats(ctx context.Context, arg database.AcquireChatsParams) ([]database.Chat, error) {
|
||||
// AcquireChats is a system-level operation used by the chat processor.
|
||||
// Authorization is done at the system level, not per-user.
|
||||
@@ -5779,9 +5792,23 @@ func (q *querier) InsertOrganizationMember(ctx context.Context, arg database.Ins
|
||||
return database.OrganizationMember{}, xerrors.Errorf("converting to organization roles: %w", err)
|
||||
}
|
||||
|
||||
// The org's default_org_member_roles are implied at request time by
|
||||
// GetAuthorizationUserRoles. Include them in canAssignRoles so the
|
||||
// caller is required to be authorized to grant the full effective set
|
||||
// (the explicit roles, organization-member, plus the defaults).
|
||||
org, err := q.db.GetOrganizationByID(ctx, arg.OrganizationID)
|
||||
if err != nil {
|
||||
return database.OrganizationMember{}, xerrors.Errorf("get organization: %w", err)
|
||||
}
|
||||
defaultRoles, err := q.convertToOrganizationRoles(arg.OrganizationID, org.DefaultOrgMemberRoles)
|
||||
if err != nil {
|
||||
return database.OrganizationMember{}, xerrors.Errorf("convert default member roles: %w", err)
|
||||
}
|
||||
|
||||
// All roles are added roles. Org member is always implied.
|
||||
//nolint:gocritic
|
||||
addedRoles := append(orgRoles, rbac.ScopedRoleOrgMember(arg.OrganizationID))
|
||||
addedRoles = append(addedRoles, defaultRoles...)
|
||||
err = q.canAssignRoles(ctx, arg.OrganizationID, addedRoles, []rbac.RoleIdentifier{})
|
||||
if err != nil {
|
||||
return database.OrganizationMember{}, err
|
||||
@@ -7049,9 +7076,23 @@ func (q *querier) UpdateMemberRoles(ctx context.Context, arg database.UpdateMemb
|
||||
return database.OrganizationMember{}, err
|
||||
}
|
||||
|
||||
// The org's default_org_member_roles are implied at request time by
|
||||
// GetAuthorizationUserRoles. Include them in the implied set so
|
||||
// canAssignRoles validates the caller can grant the full effective set
|
||||
// (the granted roles, organization-member, plus the defaults).
|
||||
org, err := q.db.GetOrganizationByID(ctx, arg.OrgID)
|
||||
if err != nil {
|
||||
return database.OrganizationMember{}, xerrors.Errorf("get organization: %w", err)
|
||||
}
|
||||
defaultRoles, err := q.convertToOrganizationRoles(arg.OrgID, org.DefaultOrgMemberRoles)
|
||||
if err != nil {
|
||||
return database.OrganizationMember{}, xerrors.Errorf("convert default member roles: %w", err)
|
||||
}
|
||||
|
||||
// The org member role is always implied.
|
||||
//nolint:gocritic
|
||||
impliedTypes := append(scopedGranted, rbac.ScopedRoleOrgMember(arg.OrgID))
|
||||
impliedTypes = append(impliedTypes, defaultRoles...)
|
||||
|
||||
added, removed := rbac.ChangeRoleSet(originalRoles, impliedTypes)
|
||||
err = q.canAssignRoles(ctx, arg.OrgID, added, removed)
|
||||
@@ -7092,10 +7133,29 @@ func (q *querier) UpdateOAuth2ProviderAppByID(ctx context.Context, arg database.
|
||||
}
|
||||
|
||||
func (q *querier) UpdateOrganization(ctx context.Context, arg database.UpdateOrganizationParams) (database.Organization, error) {
|
||||
fetch := func(ctx context.Context, arg database.UpdateOrganizationParams) (database.Organization, error) {
|
||||
return q.db.GetOrganizationByID(ctx, arg.ID)
|
||||
existing, err := q.db.GetOrganizationByID(ctx, arg.ID)
|
||||
if err != nil {
|
||||
return database.Organization{}, err
|
||||
}
|
||||
return updateWithReturn(q.log, q.auth, fetch, q.db.UpdateOrganization)(ctx, arg)
|
||||
if err := q.authorizeContext(ctx, policy.ActionUpdate, existing); err != nil {
|
||||
return database.Organization{}, err
|
||||
}
|
||||
// Treat a change to default_org_member_roles as assigning the added
|
||||
// roles, and unassigning the removed roles, for every member of the
|
||||
// org. Mirror the InsertOrganizationMember and UpdateMemberRoles
|
||||
// guard so the caller cannot grant roles they could not grant
|
||||
// individually, nor inject a malformed role name that would later
|
||||
// break RoleNameFromString.
|
||||
if !slices.Equal(existing.DefaultOrgMemberRoles, arg.DefaultOrgMemberRoles) {
|
||||
added, removed := rbac.ChangeRoleSet(
|
||||
scopedOrgRoleIdentifiers(existing.DefaultOrgMemberRoles, arg.ID),
|
||||
scopedOrgRoleIdentifiers(arg.DefaultOrgMemberRoles, arg.ID),
|
||||
)
|
||||
if err := q.canAssignRoles(ctx, arg.ID, added, removed); err != nil {
|
||||
return database.Organization{}, err
|
||||
}
|
||||
}
|
||||
return q.db.UpdateOrganization(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) UpdateOrganizationDeletedByID(ctx context.Context, arg database.UpdateOrganizationDeletedByIDParams) error {
|
||||
|
||||
@@ -2266,9 +2266,10 @@ func (s *MethodTestSuite) TestOrganization() {
|
||||
check.Args(arg).Asserts(org, policy.ActionUpdate).Returns(org)
|
||||
}))
|
||||
s.Run("InsertOrganizationMember", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
|
||||
o := testutil.Fake(s.T(), faker, database.Organization{})
|
||||
o := testutil.Fake(s.T(), faker, database.Organization{DefaultOrgMemberRoles: []string{}})
|
||||
u := testutil.Fake(s.T(), faker, database.User{})
|
||||
arg := database.InsertOrganizationMemberParams{OrganizationID: o.ID, UserID: u.ID, Roles: []string{codersdk.RoleOrganizationAdmin}}
|
||||
dbm.EXPECT().GetOrganizationByID(gomock.Any(), o.ID).Return(o, nil).AnyTimes()
|
||||
dbm.EXPECT().InsertOrganizationMember(gomock.Any(), arg).Return(database.OrganizationMember{OrganizationID: o.ID, UserID: u.ID, Roles: arg.Roles}, nil).AnyTimes()
|
||||
check.Args(arg).Asserts(
|
||||
rbac.ResourceAssignOrgRole.InOrg(o.ID), policy.ActionAssign,
|
||||
@@ -2305,12 +2306,17 @@ func (s *MethodTestSuite) TestOrganization() {
|
||||
).WithNotAuthorized("no rows").WithCancelled(sql.ErrNoRows.Error())
|
||||
}))
|
||||
s.Run("UpdateOrganization", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
|
||||
o := testutil.Fake(s.T(), faker, database.Organization{Name: "something-unique"})
|
||||
arg := database.UpdateOrganizationParams{ID: o.ID, Name: "something-different"}
|
||||
o := testutil.Fake(s.T(), faker, database.Organization{Name: "something-unique", DefaultOrgMemberRoles: []string{}})
|
||||
// Change DefaultOrgMemberRoles so canAssignRoles fires alongside the
|
||||
// ActionUpdate check; mirrors the InsertOrganizationMember pattern.
|
||||
arg := database.UpdateOrganizationParams{ID: o.ID, Name: "something-different", DefaultOrgMemberRoles: []string{codersdk.RoleOrganizationAdmin}}
|
||||
|
||||
dbm.EXPECT().GetOrganizationByID(gomock.Any(), o.ID).Return(o, nil).AnyTimes()
|
||||
dbm.EXPECT().UpdateOrganization(gomock.Any(), arg).Return(o, nil).AnyTimes()
|
||||
check.Args(arg).Asserts(o, policy.ActionUpdate)
|
||||
check.Args(arg).Asserts(
|
||||
o, policy.ActionUpdate,
|
||||
rbac.ResourceAssignOrgRole.InOrg(o.ID), policy.ActionAssign,
|
||||
)
|
||||
}))
|
||||
s.Run("UpdateOrganizationDeletedByID", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
|
||||
o := testutil.Fake(s.T(), faker, database.Organization{Name: "doomed"})
|
||||
@@ -2347,13 +2353,14 @@ func (s *MethodTestSuite) TestOrganization() {
|
||||
check.Args(arg).Asserts(rbac.ResourceOrganizationMember.InOrg(o.ID), policy.ActionRead).Returns(rows)
|
||||
}))
|
||||
s.Run("UpdateMemberRoles", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
|
||||
o := testutil.Fake(s.T(), faker, database.Organization{})
|
||||
o := testutil.Fake(s.T(), faker, database.Organization{DefaultOrgMemberRoles: []string{}})
|
||||
u := testutil.Fake(s.T(), faker, database.User{})
|
||||
mem := testutil.Fake(s.T(), faker, database.OrganizationMember{OrganizationID: o.ID, UserID: u.ID, Roles: []string{codersdk.RoleOrganizationAdmin}})
|
||||
out := mem
|
||||
out.Roles = []string{}
|
||||
|
||||
dbm.EXPECT().OrganizationMembers(gomock.Any(), database.OrganizationMembersParams{OrganizationID: o.ID, UserID: u.ID, IncludeSystem: false}).Return([]database.OrganizationMembersRow{{OrganizationMember: mem}}, nil).AnyTimes()
|
||||
dbm.EXPECT().GetOrganizationByID(gomock.Any(), o.ID).Return(o, nil).AnyTimes()
|
||||
arg := database.UpdateMemberRolesParams{GrantedRoles: []string{}, UserID: u.ID, OrgID: o.ID}
|
||||
dbm.EXPECT().UpdateMemberRoles(gomock.Any(), arg).Return(out, nil).AnyTimes()
|
||||
|
||||
|
||||
@@ -1034,13 +1034,14 @@ func GitSSHKey(t testing.TB, db database.Store, orig database.GitSSHKey) databas
|
||||
|
||||
func Organization(t testing.TB, db database.Store, orig database.Organization) database.Organization {
|
||||
org, err := db.InsertOrganization(genCtx, database.InsertOrganizationParams{
|
||||
ID: takeFirst(orig.ID, uuid.New()),
|
||||
Name: takeFirst(orig.Name, testutil.GetRandomName(t)),
|
||||
DisplayName: takeFirst(orig.Name, testutil.GetRandomName(t)),
|
||||
Description: takeFirst(orig.Description, testutil.GetRandomName(t)),
|
||||
Icon: takeFirst(orig.Icon, ""),
|
||||
CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()),
|
||||
UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()),
|
||||
ID: takeFirst(orig.ID, uuid.New()),
|
||||
Name: takeFirst(orig.Name, testutil.GetRandomName(t)),
|
||||
DisplayName: takeFirst(orig.Name, testutil.GetRandomName(t)),
|
||||
Description: takeFirst(orig.Description, testutil.GetRandomName(t)),
|
||||
Icon: takeFirst(orig.Icon, ""),
|
||||
CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()),
|
||||
UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()),
|
||||
DefaultOrgMemberRoles: takeFirstSlice(orig.DefaultOrgMemberRoles, rbac.DefaultOrgMemberRoles()),
|
||||
})
|
||||
require.NoError(t, err, "insert organization")
|
||||
|
||||
|
||||
Generated
+4
-1
@@ -2375,11 +2375,14 @@ CREATE TABLE organizations (
|
||||
display_name text NOT NULL,
|
||||
icon text DEFAULT ''::text NOT NULL,
|
||||
deleted boolean DEFAULT false NOT NULL,
|
||||
shareable_workspace_owners shareable_workspace_owners DEFAULT 'everyone'::shareable_workspace_owners NOT NULL
|
||||
shareable_workspace_owners shareable_workspace_owners DEFAULT 'everyone'::shareable_workspace_owners NOT NULL,
|
||||
default_org_member_roles text[] NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON COLUMN organizations.shareable_workspace_owners IS 'Controls whose workspaces can be shared: none, everyone, or service_accounts.';
|
||||
|
||||
COMMENT ON COLUMN organizations.default_org_member_roles IS 'Roles granted to every member of this organization at request time. The set is unioned into each member''s effective roles when GetAuthorizationUserRoles runs, so changes propagate to all members on the next request. Deployments can use this column to revoke capabilities that would otherwise be considered normal organization member permissions.';
|
||||
|
||||
CREATE TABLE parameter_schemas (
|
||||
id uuid NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE organizations DROP COLUMN IF EXISTS default_org_member_roles;
|
||||
@@ -0,0 +1,16 @@
|
||||
ALTER TABLE organizations
|
||||
ADD COLUMN default_org_member_roles text[];
|
||||
|
||||
UPDATE organizations
|
||||
SET default_org_member_roles = ARRAY['organization-workspace-access']::text[];
|
||||
|
||||
ALTER TABLE organizations
|
||||
ALTER COLUMN default_org_member_roles SET NOT NULL;
|
||||
|
||||
COMMENT ON COLUMN organizations.default_org_member_roles IS
|
||||
'Roles granted to every member of this organization at request time. '
|
||||
'The set is unioned into each member''s effective roles when '
|
||||
'GetAuthorizationUserRoles runs, so changes propagate to all members '
|
||||
'on the next request. Deployments can use this column to revoke '
|
||||
'capabilities that would otherwise be considered normal organization '
|
||||
'member permissions.';
|
||||
Generated
+2
@@ -5202,6 +5202,8 @@ type Organization struct {
|
||||
Deleted bool `db:"deleted" json:"deleted"`
|
||||
// Controls whose workspaces can be shared: none, everyone, or service_accounts.
|
||||
ShareableWorkspaceOwners ShareableWorkspaceOwners `db:"shareable_workspace_owners" json:"shareable_workspace_owners"`
|
||||
// Roles granted to every member of this organization at request time. The set is unioned into each member's effective roles when GetAuthorizationUserRoles runs, so changes propagate to all members on the next request. Deployments can use this column to revoke capabilities that would otherwise be considered normal organization member permissions.
|
||||
DefaultOrgMemberRoles []string `db:"default_org_member_roles" json:"default_org_member_roles"`
|
||||
}
|
||||
|
||||
type OrganizationMember struct {
|
||||
|
||||
@@ -3036,6 +3036,62 @@ func TestGetAuthorizationUserRolesImpliedOrgRole(t *testing.T) {
|
||||
require.NotContains(t, saRoles.Roles, wantMember)
|
||||
}
|
||||
|
||||
// TestGetAuthorizationUserRolesUnionsDefaultOrgMemberRoles verifies the
|
||||
// resolve-at-read semantics for organizations.default_org_member_roles:
|
||||
// every member's effective roles include the org's defaults, and changes
|
||||
// to the column propagate on the next request. The union applies to
|
||||
// regular users and to service accounts; the SQL array_cats the column
|
||||
// for both code paths.
|
||||
func TestGetAuthorizationUserRolesUnionsDefaultOrgMemberRoles(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
org := dbgen.Organization(t, db, database.Organization{})
|
||||
user := dbgen.User(t, db, database.User{})
|
||||
saUser := dbgen.User(t, db, database.User{IsServiceAccount: true})
|
||||
dbgen.OrganizationMember(t, db, database.OrganizationMember{
|
||||
OrganizationID: org.ID,
|
||||
UserID: user.ID,
|
||||
})
|
||||
dbgen.OrganizationMember(t, db, database.OrganizationMember{
|
||||
OrganizationID: org.ID,
|
||||
UserID: saUser.ID,
|
||||
})
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
|
||||
// New orgs default to organization-workspace-access; both the regular
|
||||
// user's and the service account's effective roles must include the
|
||||
// scoped form.
|
||||
wantWorkspaceAccess := rbac.RoleOrgWorkspaceAccess() + ":" + org.ID.String()
|
||||
initial, err := db.GetAuthorizationUserRoles(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, initial.Roles, wantWorkspaceAccess)
|
||||
initialSA, err := db.GetAuthorizationUserRoles(ctx, saUser.ID)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, initialSA.Roles, wantWorkspaceAccess)
|
||||
|
||||
// Shrinking the org default to empty must immediately drop the role
|
||||
// from both effective sets.
|
||||
_, err = db.UpdateOrganization(ctx, database.UpdateOrganizationParams{
|
||||
ID: org.ID,
|
||||
UpdatedAt: dbtime.Now(),
|
||||
Name: org.Name,
|
||||
DisplayName: org.DisplayName,
|
||||
Description: org.Description,
|
||||
Icon: org.Icon,
|
||||
DefaultOrgMemberRoles: []string{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
shrunk, err := db.GetAuthorizationUserRoles(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotContains(t, shrunk.Roles, wantWorkspaceAccess)
|
||||
shrunkSA, err := db.GetAuthorizationUserRoles(ctx, saUser.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotContains(t, shrunkSA.Roles, wantWorkspaceAccess)
|
||||
}
|
||||
|
||||
func TestUpdateOrganizationWorkspaceSharingSettings(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Generated
+54
-34
@@ -18319,7 +18319,7 @@ func (q *sqlQuerier) UpdateMemberRoles(ctx context.Context, arg UpdateMemberRole
|
||||
|
||||
const getDefaultOrganization = `-- name: GetDefaultOrganization :one
|
||||
SELECT
|
||||
id, name, description, created_at, updated_at, is_default, display_name, icon, deleted, shareable_workspace_owners
|
||||
id, name, description, created_at, updated_at, is_default, display_name, icon, deleted, shareable_workspace_owners, default_org_member_roles
|
||||
FROM
|
||||
organizations
|
||||
WHERE
|
||||
@@ -18342,13 +18342,14 @@ func (q *sqlQuerier) GetDefaultOrganization(ctx context.Context) (Organization,
|
||||
&i.Icon,
|
||||
&i.Deleted,
|
||||
&i.ShareableWorkspaceOwners,
|
||||
pq.Array(&i.DefaultOrgMemberRoles),
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getOrganizationByID = `-- name: GetOrganizationByID :one
|
||||
SELECT
|
||||
id, name, description, created_at, updated_at, is_default, display_name, icon, deleted, shareable_workspace_owners
|
||||
id, name, description, created_at, updated_at, is_default, display_name, icon, deleted, shareable_workspace_owners, default_org_member_roles
|
||||
FROM
|
||||
organizations
|
||||
WHERE
|
||||
@@ -18369,13 +18370,14 @@ func (q *sqlQuerier) GetOrganizationByID(ctx context.Context, id uuid.UUID) (Org
|
||||
&i.Icon,
|
||||
&i.Deleted,
|
||||
&i.ShareableWorkspaceOwners,
|
||||
pq.Array(&i.DefaultOrgMemberRoles),
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getOrganizationByName = `-- name: GetOrganizationByName :one
|
||||
SELECT
|
||||
id, name, description, created_at, updated_at, is_default, display_name, icon, deleted, shareable_workspace_owners
|
||||
id, name, description, created_at, updated_at, is_default, display_name, icon, deleted, shareable_workspace_owners, default_org_member_roles
|
||||
FROM
|
||||
organizations
|
||||
WHERE
|
||||
@@ -18405,6 +18407,7 @@ func (q *sqlQuerier) GetOrganizationByName(ctx context.Context, arg GetOrganizat
|
||||
&i.Icon,
|
||||
&i.Deleted,
|
||||
&i.ShareableWorkspaceOwners,
|
||||
pq.Array(&i.DefaultOrgMemberRoles),
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -18475,7 +18478,7 @@ func (q *sqlQuerier) GetOrganizationResourceCountByID(ctx context.Context, organ
|
||||
|
||||
const getOrganizations = `-- name: GetOrganizations :many
|
||||
SELECT
|
||||
id, name, description, created_at, updated_at, is_default, display_name, icon, deleted, shareable_workspace_owners
|
||||
id, name, description, created_at, updated_at, is_default, display_name, icon, deleted, shareable_workspace_owners, default_org_member_roles
|
||||
FROM
|
||||
organizations
|
||||
WHERE
|
||||
@@ -18520,6 +18523,7 @@ func (q *sqlQuerier) GetOrganizations(ctx context.Context, arg GetOrganizationsP
|
||||
&i.Icon,
|
||||
&i.Deleted,
|
||||
&i.ShareableWorkspaceOwners,
|
||||
pq.Array(&i.DefaultOrgMemberRoles),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -18536,7 +18540,7 @@ func (q *sqlQuerier) GetOrganizations(ctx context.Context, arg GetOrganizationsP
|
||||
|
||||
const getOrganizationsByUserID = `-- name: GetOrganizationsByUserID :many
|
||||
SELECT
|
||||
id, name, description, created_at, updated_at, is_default, display_name, icon, deleted, shareable_workspace_owners
|
||||
id, name, description, created_at, updated_at, is_default, display_name, icon, deleted, shareable_workspace_owners, default_org_member_roles
|
||||
FROM
|
||||
organizations
|
||||
WHERE
|
||||
@@ -18582,6 +18586,7 @@ func (q *sqlQuerier) GetOrganizationsByUserID(ctx context.Context, arg GetOrgani
|
||||
&i.Icon,
|
||||
&i.Deleted,
|
||||
&i.ShareableWorkspaceOwners,
|
||||
pq.Array(&i.DefaultOrgMemberRoles),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -18598,20 +18603,21 @@ func (q *sqlQuerier) GetOrganizationsByUserID(ctx context.Context, arg GetOrgani
|
||||
|
||||
const insertOrganization = `-- name: InsertOrganization :one
|
||||
INSERT INTO
|
||||
organizations (id, "name", display_name, description, icon, created_at, updated_at, is_default)
|
||||
organizations (id, "name", display_name, description, icon, created_at, updated_at, is_default, default_org_member_roles)
|
||||
VALUES
|
||||
-- If no organizations exist, and this is the first, make it the default.
|
||||
($1, $2, $3, $4, $5, $6, $7, (SELECT TRUE FROM organizations LIMIT 1) IS NULL) RETURNING id, name, description, created_at, updated_at, is_default, display_name, icon, deleted, shareable_workspace_owners
|
||||
($1, $2, $3, $4, $5, $6, $7, (SELECT TRUE FROM organizations LIMIT 1) IS NULL, $8) RETURNING id, name, description, created_at, updated_at, is_default, display_name, icon, deleted, shareable_workspace_owners, default_org_member_roles
|
||||
`
|
||||
|
||||
type InsertOrganizationParams struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
DisplayName string `db:"display_name" json:"display_name"`
|
||||
Description string `db:"description" json:"description"`
|
||||
Icon string `db:"icon" json:"icon"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
DisplayName string `db:"display_name" json:"display_name"`
|
||||
Description string `db:"description" json:"description"`
|
||||
Icon string `db:"icon" json:"icon"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
DefaultOrgMemberRoles []string `db:"default_org_member_roles" json:"default_org_member_roles"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertOrganization(ctx context.Context, arg InsertOrganizationParams) (Organization, error) {
|
||||
@@ -18623,6 +18629,7 @@ func (q *sqlQuerier) InsertOrganization(ctx context.Context, arg InsertOrganizat
|
||||
arg.Icon,
|
||||
arg.CreatedAt,
|
||||
arg.UpdatedAt,
|
||||
pq.Array(arg.DefaultOrgMemberRoles),
|
||||
)
|
||||
var i Organization
|
||||
err := row.Scan(
|
||||
@@ -18636,6 +18643,7 @@ func (q *sqlQuerier) InsertOrganization(ctx context.Context, arg InsertOrganizat
|
||||
&i.Icon,
|
||||
&i.Deleted,
|
||||
&i.ShareableWorkspaceOwners,
|
||||
pq.Array(&i.DefaultOrgMemberRoles),
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -18648,19 +18656,21 @@ SET
|
||||
name = $2,
|
||||
display_name = $3,
|
||||
description = $4,
|
||||
icon = $5
|
||||
icon = $5,
|
||||
default_org_member_roles = $6
|
||||
WHERE
|
||||
id = $6
|
||||
RETURNING id, name, description, created_at, updated_at, is_default, display_name, icon, deleted, shareable_workspace_owners
|
||||
id = $7
|
||||
RETURNING id, name, description, created_at, updated_at, is_default, display_name, icon, deleted, shareable_workspace_owners, default_org_member_roles
|
||||
`
|
||||
|
||||
type UpdateOrganizationParams struct {
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
Name string `db:"name" json:"name"`
|
||||
DisplayName string `db:"display_name" json:"display_name"`
|
||||
Description string `db:"description" json:"description"`
|
||||
Icon string `db:"icon" json:"icon"`
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
Name string `db:"name" json:"name"`
|
||||
DisplayName string `db:"display_name" json:"display_name"`
|
||||
Description string `db:"description" json:"description"`
|
||||
Icon string `db:"icon" json:"icon"`
|
||||
DefaultOrgMemberRoles []string `db:"default_org_member_roles" json:"default_org_member_roles"`
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) UpdateOrganization(ctx context.Context, arg UpdateOrganizationParams) (Organization, error) {
|
||||
@@ -18670,6 +18680,7 @@ func (q *sqlQuerier) UpdateOrganization(ctx context.Context, arg UpdateOrganizat
|
||||
arg.DisplayName,
|
||||
arg.Description,
|
||||
arg.Icon,
|
||||
pq.Array(arg.DefaultOrgMemberRoles),
|
||||
arg.ID,
|
||||
)
|
||||
var i Organization
|
||||
@@ -18684,6 +18695,7 @@ func (q *sqlQuerier) UpdateOrganization(ctx context.Context, arg UpdateOrganizat
|
||||
&i.Icon,
|
||||
&i.Deleted,
|
||||
&i.ShareableWorkspaceOwners,
|
||||
pq.Array(&i.DefaultOrgMemberRoles),
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -18716,7 +18728,7 @@ SET
|
||||
updated_at = $2
|
||||
WHERE
|
||||
id = $3
|
||||
RETURNING id, name, description, created_at, updated_at, is_default, display_name, icon, deleted, shareable_workspace_owners
|
||||
RETURNING id, name, description, created_at, updated_at, is_default, display_name, icon, deleted, shareable_workspace_owners, default_org_member_roles
|
||||
`
|
||||
|
||||
type UpdateOrganizationWorkspaceSharingSettingsParams struct {
|
||||
@@ -18739,6 +18751,7 @@ func (q *sqlQuerier) UpdateOrganizationWorkspaceSharingSettings(ctx context.Cont
|
||||
&i.Icon,
|
||||
&i.Deleted,
|
||||
&i.ShareableWorkspaceOwners,
|
||||
pq.Array(&i.DefaultOrgMemberRoles),
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -27898,21 +27911,28 @@ SELECT
|
||||
-- Concatenating the organization id scopes the organization roles.
|
||||
array_agg(org_roles || ':' || organization_members.organization_id::text)
|
||||
FROM
|
||||
organization_members,
|
||||
organization_members
|
||||
JOIN organizations ON organizations.id = organization_members.organization_id,
|
||||
-- All org members get an implied role for their orgs. Most members
|
||||
-- get organization-member, but service accounts will get
|
||||
-- organization-service-account instead. They're largely the same,
|
||||
-- but having them be distinct means we can allow configuring
|
||||
-- service-accounts to have slightly broader permissions–such as
|
||||
-- service-accounts to have slightly broader permissions, such as
|
||||
-- for workspace sharing.
|
||||
--
|
||||
-- organizations.default_org_member_roles is unioned in so changes
|
||||
-- to org defaults propagate to every member on the next request.
|
||||
unnest(
|
||||
array_append(
|
||||
roles,
|
||||
CASE WHEN users.is_service_account THEN
|
||||
'organization-service-account'
|
||||
ELSE
|
||||
'organization-member'
|
||||
END
|
||||
array_cat(
|
||||
array_append(
|
||||
roles,
|
||||
CASE WHEN users.is_service_account THEN
|
||||
'organization-service-account'
|
||||
ELSE
|
||||
'organization-member'
|
||||
END
|
||||
),
|
||||
organizations.default_org_member_roles
|
||||
)
|
||||
) AS org_roles
|
||||
WHERE
|
||||
@@ -27933,7 +27953,7 @@ SELECT
|
||||
FROM
|
||||
users
|
||||
WHERE
|
||||
id = $1
|
||||
users.id = $1
|
||||
`
|
||||
|
||||
type GetAuthorizationUserRolesRow struct {
|
||||
|
||||
@@ -116,10 +116,10 @@ SELECT
|
||||
|
||||
-- name: InsertOrganization :one
|
||||
INSERT INTO
|
||||
organizations (id, "name", display_name, description, icon, created_at, updated_at, is_default)
|
||||
organizations (id, "name", display_name, description, icon, created_at, updated_at, is_default, default_org_member_roles)
|
||||
VALUES
|
||||
-- If no organizations exist, and this is the first, make it the default.
|
||||
(@id, @name, @display_name, @description, @icon, @created_at, @updated_at, (SELECT TRUE FROM organizations LIMIT 1) IS NULL) RETURNING *;
|
||||
(@id, @name, @display_name, @description, @icon, @created_at, @updated_at, (SELECT TRUE FROM organizations LIMIT 1) IS NULL, @default_org_member_roles) RETURNING *;
|
||||
|
||||
-- name: UpdateOrganization :one
|
||||
UPDATE
|
||||
@@ -129,7 +129,8 @@ SET
|
||||
name = @name,
|
||||
display_name = @display_name,
|
||||
description = @description,
|
||||
icon = @icon
|
||||
icon = @icon,
|
||||
default_org_member_roles = @default_org_member_roles
|
||||
WHERE
|
||||
id = @id
|
||||
RETURNING *;
|
||||
|
||||
@@ -609,21 +609,28 @@ SELECT
|
||||
-- Concatenating the organization id scopes the organization roles.
|
||||
array_agg(org_roles || ':' || organization_members.organization_id::text)
|
||||
FROM
|
||||
organization_members,
|
||||
organization_members
|
||||
JOIN organizations ON organizations.id = organization_members.organization_id,
|
||||
-- All org members get an implied role for their orgs. Most members
|
||||
-- get organization-member, but service accounts will get
|
||||
-- organization-service-account instead. They're largely the same,
|
||||
-- but having them be distinct means we can allow configuring
|
||||
-- service-accounts to have slightly broader permissions–such as
|
||||
-- service-accounts to have slightly broader permissions, such as
|
||||
-- for workspace sharing.
|
||||
--
|
||||
-- organizations.default_org_member_roles is unioned in so changes
|
||||
-- to org defaults propagate to every member on the next request.
|
||||
unnest(
|
||||
array_append(
|
||||
roles,
|
||||
CASE WHEN users.is_service_account THEN
|
||||
'organization-service-account'
|
||||
ELSE
|
||||
'organization-member'
|
||||
END
|
||||
array_cat(
|
||||
array_append(
|
||||
roles,
|
||||
CASE WHEN users.is_service_account THEN
|
||||
'organization-service-account'
|
||||
ELSE
|
||||
'organization-member'
|
||||
END
|
||||
),
|
||||
organizations.default_org_member_roles
|
||||
)
|
||||
) AS org_roles
|
||||
WHERE
|
||||
@@ -644,7 +651,7 @@ SELECT
|
||||
FROM
|
||||
users
|
||||
WHERE
|
||||
id = @user_id;
|
||||
users.id = @user_id;
|
||||
|
||||
-- name: UpdateUserQuietHoursSchedule :one
|
||||
UPDATE
|
||||
|
||||
Reference in New Issue
Block a user