mirror of
https://github.com/coder/coder.git
synced 2025-07-18 14:17:22 +00:00
feat: add owner groups to workspace data (#12841)
This commit is contained in:
@ -174,6 +174,7 @@ var (
|
||||
// When org scoped provisioner credentials are implemented,
|
||||
// this can be reduced to read a specific org.
|
||||
rbac.ResourceOrganization.Type: {rbac.ActionRead},
|
||||
rbac.ResourceGroup.Type: {rbac.ActionRead},
|
||||
}),
|
||||
Org: map[string][]rbac.Permission{},
|
||||
User: []rbac.Permission{},
|
||||
@ -1141,6 +1142,10 @@ func (q *querier) GetGroupMembers(ctx context.Context, id uuid.UUID) ([]database
|
||||
return q.db.GetGroupMembers(ctx, id)
|
||||
}
|
||||
|
||||
func (q *querier) GetGroupsByOrganizationAndUserID(ctx context.Context, arg database.GetGroupsByOrganizationAndUserIDParams) ([]database.Group, error) {
|
||||
return fetchWithPostFilter(q.auth, q.db.GetGroupsByOrganizationAndUserID)(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) GetGroupsByOrganizationID(ctx context.Context, organizationID uuid.UUID) ([]database.Group, error) {
|
||||
return fetchWithPostFilter(q.auth, q.db.GetGroupsByOrganizationID)(ctx, organizationID)
|
||||
}
|
||||
|
@ -314,6 +314,14 @@ func (s *MethodTestSuite) TestGroup() {
|
||||
_ = dbgen.GroupMember(s.T(), db, database.GroupMember{})
|
||||
check.Args(g.ID).Asserts(g, rbac.ActionRead)
|
||||
}))
|
||||
s.Run("GetGroupsByOrganizationAndUserID", s.Subtest(func(db database.Store, check *expects) {
|
||||
g := dbgen.Group(s.T(), db, database.Group{})
|
||||
gm := dbgen.GroupMember(s.T(), db, database.GroupMember{GroupID: g.ID})
|
||||
check.Args(database.GetGroupsByOrganizationAndUserIDParams{
|
||||
OrganizationID: g.OrganizationID,
|
||||
UserID: gm.UserID,
|
||||
}).Asserts(g, rbac.ActionRead)
|
||||
}))
|
||||
s.Run("InsertAllUsersGroup", s.Subtest(func(db database.Store, check *expects) {
|
||||
o := dbgen.Organization(s.T(), db, database.Organization{})
|
||||
check.Args(o.ID).Asserts(rbac.ResourceGroup.InOrg(o.ID), rbac.ActionCreate)
|
||||
|
@ -2250,6 +2250,30 @@ func (q *FakeQuerier) GetGroupMembers(_ context.Context, id uuid.UUID) ([]databa
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetGroupsByOrganizationAndUserID(_ context.Context, arg database.GetGroupsByOrganizationAndUserIDParams) ([]database.Group, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
var groupIds []uuid.UUID
|
||||
for _, member := range q.groupMembers {
|
||||
if member.UserID == arg.UserID {
|
||||
groupIds = append(groupIds, member.GroupID)
|
||||
}
|
||||
}
|
||||
groups := []database.Group{}
|
||||
for _, group := range q.groups {
|
||||
if slices.Contains(groupIds, group.ID) && group.OrganizationID == arg.OrganizationID {
|
||||
groups = append(groups, group)
|
||||
}
|
||||
}
|
||||
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetGroupsByOrganizationID(_ context.Context, id uuid.UUID) ([]database.Group, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
@ -559,6 +559,13 @@ func (m metricsStore) GetGroupMembers(ctx context.Context, groupID uuid.UUID) ([
|
||||
return users, err
|
||||
}
|
||||
|
||||
func (m metricsStore) GetGroupsByOrganizationAndUserID(ctx context.Context, arg database.GetGroupsByOrganizationAndUserIDParams) ([]database.Group, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetGroupsByOrganizationAndUserID(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("GetGroupsByOrganizationAndUserID").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetGroupsByOrganizationID(ctx context.Context, organizationID uuid.UUID) ([]database.Group, error) {
|
||||
start := time.Now()
|
||||
groups, err := m.s.GetGroupsByOrganizationID(ctx, organizationID)
|
||||
|
@ -1095,6 +1095,21 @@ func (mr *MockStoreMockRecorder) GetGroupMembers(arg0, arg1 any) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGroupMembers", reflect.TypeOf((*MockStore)(nil).GetGroupMembers), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetGroupsByOrganizationAndUserID mocks base method.
|
||||
func (m *MockStore) GetGroupsByOrganizationAndUserID(arg0 context.Context, arg1 database.GetGroupsByOrganizationAndUserIDParams) ([]database.Group, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetGroupsByOrganizationAndUserID", arg0, arg1)
|
||||
ret0, _ := ret[0].([]database.Group)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetGroupsByOrganizationAndUserID indicates an expected call of GetGroupsByOrganizationAndUserID.
|
||||
func (mr *MockStoreMockRecorder) GetGroupsByOrganizationAndUserID(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGroupsByOrganizationAndUserID", reflect.TypeOf((*MockStore)(nil).GetGroupsByOrganizationAndUserID), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetGroupsByOrganizationID mocks base method.
|
||||
func (m *MockStore) GetGroupsByOrganizationID(arg0 context.Context, arg1 uuid.UUID) ([]database.Group, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -123,6 +123,7 @@ type sqlcQuerier interface {
|
||||
// If the group is a user made group, then we need to check the group_members table.
|
||||
// If it is the "Everyone" group, then we need to check the organization_members table.
|
||||
GetGroupMembers(ctx context.Context, groupID uuid.UUID) ([]User, error)
|
||||
GetGroupsByOrganizationAndUserID(ctx context.Context, arg GetGroupsByOrganizationAndUserIDParams) ([]Group, error)
|
||||
GetGroupsByOrganizationID(ctx context.Context, organizationID uuid.UUID) ([]Group, error)
|
||||
GetHealthSettings(ctx context.Context) (string, error)
|
||||
GetHungProvisionerJobs(ctx context.Context, updatedAt time.Time) ([]ProvisionerJob, error)
|
||||
|
@ -1484,6 +1484,67 @@ func (q *sqlQuerier) GetGroupByOrgAndName(ctx context.Context, arg GetGroupByOrg
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getGroupsByOrganizationAndUserID = `-- name: GetGroupsByOrganizationAndUserID :many
|
||||
SELECT
|
||||
groups.id, groups.name, groups.organization_id, groups.avatar_url, groups.quota_allowance, groups.display_name, groups.source
|
||||
FROM
|
||||
groups
|
||||
-- If the group is a user made group, then we need to check the group_members table.
|
||||
LEFT JOIN
|
||||
group_members
|
||||
ON
|
||||
group_members.group_id = groups.id AND
|
||||
group_members.user_id = $1
|
||||
-- If it is the "Everyone" group, then we need to check the organization_members table.
|
||||
LEFT JOIN
|
||||
organization_members
|
||||
ON
|
||||
organization_members.organization_id = groups.id AND
|
||||
organization_members.user_id = $1
|
||||
WHERE
|
||||
-- In either case, the group_id will only match an org or a group.
|
||||
(group_members.user_id = $1 OR organization_members.user_id = $1)
|
||||
AND
|
||||
-- Ensure the group or organization is the specified organization.
|
||||
groups.organization_id = $2
|
||||
`
|
||||
|
||||
type GetGroupsByOrganizationAndUserIDParams struct {
|
||||
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
||||
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) GetGroupsByOrganizationAndUserID(ctx context.Context, arg GetGroupsByOrganizationAndUserIDParams) ([]Group, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getGroupsByOrganizationAndUserID, arg.UserID, arg.OrganizationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Group
|
||||
for rows.Next() {
|
||||
var i Group
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Name,
|
||||
&i.OrganizationID,
|
||||
&i.AvatarURL,
|
||||
&i.QuotaAllowance,
|
||||
&i.DisplayName,
|
||||
&i.Source,
|
||||
); 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 getGroupsByOrganizationID = `-- name: GetGroupsByOrganizationID :many
|
||||
SELECT
|
||||
id, name, organization_id, avatar_url, quota_allowance, display_name, source
|
||||
|
@ -28,6 +28,31 @@ FROM
|
||||
WHERE
|
||||
organization_id = $1;
|
||||
|
||||
-- name: GetGroupsByOrganizationAndUserID :many
|
||||
SELECT
|
||||
groups.*
|
||||
FROM
|
||||
groups
|
||||
-- If the group is a user made group, then we need to check the group_members table.
|
||||
LEFT JOIN
|
||||
group_members
|
||||
ON
|
||||
group_members.group_id = groups.id AND
|
||||
group_members.user_id = @user_id
|
||||
-- If it is the "Everyone" group, then we need to check the organization_members table.
|
||||
LEFT JOIN
|
||||
organization_members
|
||||
ON
|
||||
organization_members.organization_id = groups.id AND
|
||||
organization_members.user_id = @user_id
|
||||
WHERE
|
||||
-- In either case, the group_id will only match an org or a group.
|
||||
(group_members.user_id = @user_id OR organization_members.user_id = @user_id)
|
||||
AND
|
||||
-- Ensure the group or organization is the specified organization.
|
||||
groups.organization_id = @organization_id;
|
||||
|
||||
|
||||
-- name: InsertGroup :one
|
||||
INSERT INTO groups (
|
||||
id,
|
||||
|
Reference in New Issue
Block a user