feat(group): group service to create, update, and delete groups (#10455)
# Which Problems Are Solved
This PR adds API definition and backend implementation for GroupService
to manage user groups.
# How the Problems Are Solved
* API definition to create, update, retrieve, and delete groups is added
* Command-side implementation to create, update, and delete user groups
as part of the GroupV2 API is added
# Additional Changes
N/A
# Additional Context
- Related to #10089, #9702 (parent ticket)
- User contribution: https://github.com/zitadel/zitadel/pull/9428/files
- Additional functionalities to list/search user groups, add
permissions, manage users in groups, group scopes will be added in
subsequent PRs.
- Also needs documentation, which will be added once the entire feature
is available
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
2025-10-06 11:23:15 +02:00
|
|
|
package group
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
|
|
|
|
|
"connectrpc.com/connect"
|
|
|
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
|
|
|
|
2025-10-07 14:53:25 +02:00
|
|
|
"github.com/zitadel/zitadel/internal/api/grpc/filter/v2"
|
|
|
|
|
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
feat(group): group service to create, update, and delete groups (#10455)
# Which Problems Are Solved
This PR adds API definition and backend implementation for GroupService
to manage user groups.
# How the Problems Are Solved
* API definition to create, update, retrieve, and delete groups is added
* Command-side implementation to create, update, and delete user groups
as part of the GroupV2 API is added
# Additional Changes
N/A
# Additional Context
- Related to #10089, #9702 (parent ticket)
- User contribution: https://github.com/zitadel/zitadel/pull/9428/files
- Additional functionalities to list/search user groups, add
permissions, manage users in groups, group scopes will be added in
subsequent PRs.
- Also needs documentation, which will be added once the entire feature
is available
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
2025-10-06 11:23:15 +02:00
|
|
|
"github.com/zitadel/zitadel/internal/query"
|
|
|
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
2025-10-28 14:23:54 +01:00
|
|
|
authorization_v2beta "github.com/zitadel/zitadel/pkg/grpc/authorization/v2beta"
|
2025-10-07 14:53:25 +02:00
|
|
|
group_v2 "github.com/zitadel/zitadel/pkg/grpc/group/v2"
|
feat(group): group service to create, update, and delete groups (#10455)
# Which Problems Are Solved
This PR adds API definition and backend implementation for GroupService
to manage user groups.
# How the Problems Are Solved
* API definition to create, update, retrieve, and delete groups is added
* Command-side implementation to create, update, and delete user groups
as part of the GroupV2 API is added
# Additional Changes
N/A
# Additional Context
- Related to #10089, #9702 (parent ticket)
- User contribution: https://github.com/zitadel/zitadel/pull/9428/files
- Additional functionalities to list/search user groups, add
permissions, manage users in groups, group scopes will be added in
subsequent PRs.
- Also needs documentation, which will be added once the entire feature
is available
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
2025-10-06 11:23:15 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// GetGroup returns a group that matches the group ID in the request
|
2025-10-07 14:53:25 +02:00
|
|
|
func (s *Server) GetGroup(ctx context.Context, req *connect.Request[group_v2.GetGroupRequest]) (*connect.Response[group_v2.GetGroupResponse], error) {
|
2025-10-21 11:18:21 +02:00
|
|
|
group, err := s.query.GetGroupByID(ctx, req.Msg.GetId(), s.checkPermission)
|
2025-10-07 14:53:25 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return connect.NewResponse(&group_v2.GetGroupResponse{
|
|
|
|
|
Group: groupToPb(group),
|
|
|
|
|
}), nil
|
feat(group): group service to create, update, and delete groups (#10455)
# Which Problems Are Solved
This PR adds API definition and backend implementation for GroupService
to manage user groups.
# How the Problems Are Solved
* API definition to create, update, retrieve, and delete groups is added
* Command-side implementation to create, update, and delete user groups
as part of the GroupV2 API is added
# Additional Changes
N/A
# Additional Context
- Related to #10089, #9702 (parent ticket)
- User contribution: https://github.com/zitadel/zitadel/pull/9428/files
- Additional functionalities to list/search user groups, add
permissions, manage users in groups, group scopes will be added in
subsequent PRs.
- Also needs documentation, which will be added once the entire feature
is available
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
2025-10-06 11:23:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ListGroups returns a list of groups that match the search criteria
|
2025-10-07 14:53:25 +02:00
|
|
|
func (s *Server) ListGroups(ctx context.Context, req *connect.Request[group_v2.ListGroupsRequest]) (*connect.Response[group_v2.ListGroupsResponse], error) {
|
|
|
|
|
queries, err := listGroupsRequestToModel(req.Msg, s.systemDefaults)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2025-10-21 11:18:21 +02:00
|
|
|
resp, err := s.query.SearchGroups(ctx, queries, s.checkPermission)
|
feat(group): group service to create, update, and delete groups (#10455)
# Which Problems Are Solved
This PR adds API definition and backend implementation for GroupService
to manage user groups.
# How the Problems Are Solved
* API definition to create, update, retrieve, and delete groups is added
* Command-side implementation to create, update, and delete user groups
as part of the GroupV2 API is added
# Additional Changes
N/A
# Additional Context
- Related to #10089, #9702 (parent ticket)
- User contribution: https://github.com/zitadel/zitadel/pull/9428/files
- Additional functionalities to list/search user groups, add
permissions, manage users in groups, group scopes will be added in
subsequent PRs.
- Also needs documentation, which will be added once the entire feature
is available
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
2025-10-06 11:23:15 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2025-10-07 14:53:25 +02:00
|
|
|
return connect.NewResponse(&group_v2.ListGroupsResponse{
|
|
|
|
|
Groups: groupsToPb(resp.Groups),
|
|
|
|
|
Pagination: filter.QueryToPaginationPb(queries.SearchRequest, resp.SearchResponse),
|
feat(group): group service to create, update, and delete groups (#10455)
# Which Problems Are Solved
This PR adds API definition and backend implementation for GroupService
to manage user groups.
# How the Problems Are Solved
* API definition to create, update, retrieve, and delete groups is added
* Command-side implementation to create, update, and delete user groups
as part of the GroupV2 API is added
# Additional Changes
N/A
# Additional Context
- Related to #10089, #9702 (parent ticket)
- User contribution: https://github.com/zitadel/zitadel/pull/9428/files
- Additional functionalities to list/search user groups, add
permissions, manage users in groups, group scopes will be added in
subsequent PRs.
- Also needs documentation, which will be added once the entire feature
is available
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
2025-10-06 11:23:15 +02:00
|
|
|
}), nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-28 14:23:54 +01:00
|
|
|
// ListGroupUsers returns a list of users from user groupUsers that match the search criteria
|
|
|
|
|
func (s *Server) ListGroupUsers(ctx context.Context, req *connect.Request[group_v2.ListGroupUsersRequest]) (*connect.Response[group_v2.ListGroupUsersResponse], error) {
|
|
|
|
|
queries, err := listGroupUsersRequestToModel(req.Msg, s.systemDefaults)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
resp, err := s.query.SearchGroupUsers(ctx, queries, s.checkPermission)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return connect.NewResponse(&group_v2.ListGroupUsersResponse{
|
|
|
|
|
GroupUsers: groupUsersToPb(resp.GroupUsers),
|
|
|
|
|
Pagination: filter.QueryToPaginationPb(queries.SearchRequest, resp.SearchResponse),
|
|
|
|
|
}), nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-07 14:53:25 +02:00
|
|
|
func listGroupsRequestToModel(req *group_v2.ListGroupsRequest, systemDefaults systemdefaults.SystemDefaults) (*query.GroupSearchQuery, error) {
|
|
|
|
|
offset, limit, asc, err := filter.PaginationPbToQuery(systemDefaults, req.GetPagination())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
queries, err := groupSearchFiltersToQuery(req.GetFilters())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return &query.GroupSearchQuery{
|
|
|
|
|
SearchRequest: query.SearchRequest{
|
|
|
|
|
Offset: offset,
|
|
|
|
|
Limit: limit,
|
|
|
|
|
Asc: asc,
|
|
|
|
|
SortingColumn: groupFieldNameToSortingColumn(req.SortingColumn),
|
|
|
|
|
},
|
|
|
|
|
Queries: queries,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func groupSearchFiltersToQuery(filters []*group_v2.GroupsSearchFilter) (_ []query.SearchQuery, err error) {
|
|
|
|
|
q := make([]query.SearchQuery, len(filters))
|
|
|
|
|
for i, f := range filters {
|
|
|
|
|
q[i], err = groupFilterToQuery(f)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return q, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func groupFilterToQuery(f *group_v2.GroupsSearchFilter) (query.SearchQuery, error) {
|
|
|
|
|
switch q := f.Filter.(type) {
|
|
|
|
|
case *group_v2.GroupsSearchFilter_GroupIds:
|
|
|
|
|
return query.NewGroupIDsSearchQuery(q.GroupIds.GetIds())
|
|
|
|
|
case *group_v2.GroupsSearchFilter_NameFilter:
|
|
|
|
|
return query.NewGroupNameSearchQuery(q.NameFilter.GetName(), filter.TextMethodPbToQuery(q.NameFilter.GetMethod()))
|
|
|
|
|
case *group_v2.GroupsSearchFilter_OrganizationId:
|
|
|
|
|
return query.NewGroupOrganizationIdSearchQuery(q.OrganizationId.GetId())
|
|
|
|
|
default:
|
|
|
|
|
return nil, zerrors.ThrowInvalidArgument(nil, "GRPC-g3f4g", "List.Query.Invalid")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func groupFieldNameToSortingColumn(field *group_v2.FieldName) query.Column {
|
|
|
|
|
if field == nil {
|
|
|
|
|
return query.GroupColumnCreationDate
|
|
|
|
|
}
|
|
|
|
|
switch *field {
|
|
|
|
|
case group_v2.FieldName_FIELD_NAME_CREATION_DATE, group_v2.FieldName_FIELD_NAME_UNSPECIFIED:
|
|
|
|
|
return query.GroupColumnCreationDate
|
|
|
|
|
case group_v2.FieldName_FIELD_NAME_ID:
|
|
|
|
|
return query.GroupColumnID
|
|
|
|
|
case group_v2.FieldName_FIELD_NAME_NAME:
|
|
|
|
|
return query.GroupColumnName
|
|
|
|
|
case group_v2.FieldName_FIELD_NAME_CHANGE_DATE:
|
|
|
|
|
return query.GroupColumnChangeDate
|
|
|
|
|
default:
|
|
|
|
|
return query.GroupColumnCreationDate
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func groupsToPb(groups []*query.Group) []*group_v2.Group {
|
|
|
|
|
pbGroups := make([]*group_v2.Group, len(groups))
|
feat(group): group service to create, update, and delete groups (#10455)
# Which Problems Are Solved
This PR adds API definition and backend implementation for GroupService
to manage user groups.
# How the Problems Are Solved
* API definition to create, update, retrieve, and delete groups is added
* Command-side implementation to create, update, and delete user groups
as part of the GroupV2 API is added
# Additional Changes
N/A
# Additional Context
- Related to #10089, #9702 (parent ticket)
- User contribution: https://github.com/zitadel/zitadel/pull/9428/files
- Additional functionalities to list/search user groups, add
permissions, manage users in groups, group scopes will be added in
subsequent PRs.
- Also needs documentation, which will be added once the entire feature
is available
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
2025-10-06 11:23:15 +02:00
|
|
|
for i, g := range groups {
|
|
|
|
|
pbGroups[i] = groupToPb(g)
|
|
|
|
|
}
|
|
|
|
|
return pbGroups
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-07 14:53:25 +02:00
|
|
|
func groupToPb(g *query.Group) *group_v2.Group {
|
|
|
|
|
return &group_v2.Group{
|
|
|
|
|
Id: g.ID,
|
|
|
|
|
Name: g.Name,
|
|
|
|
|
Description: g.Description,
|
|
|
|
|
OrganizationId: g.ResourceOwner,
|
|
|
|
|
CreationDate: timestamppb.New(g.CreationDate),
|
|
|
|
|
ChangeDate: timestamppb.New(g.ChangeDate),
|
feat(group): group service to create, update, and delete groups (#10455)
# Which Problems Are Solved
This PR adds API definition and backend implementation for GroupService
to manage user groups.
# How the Problems Are Solved
* API definition to create, update, retrieve, and delete groups is added
* Command-side implementation to create, update, and delete user groups
as part of the GroupV2 API is added
# Additional Changes
N/A
# Additional Context
- Related to #10089, #9702 (parent ticket)
- User contribution: https://github.com/zitadel/zitadel/pull/9428/files
- Additional functionalities to list/search user groups, add
permissions, manage users in groups, group scopes will be added in
subsequent PRs.
- Also needs documentation, which will be added once the entire feature
is available
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
2025-10-06 11:23:15 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-10-28 14:23:54 +01:00
|
|
|
|
|
|
|
|
func listGroupUsersRequestToModel(req *group_v2.ListGroupUsersRequest, systemDefaults systemdefaults.SystemDefaults) (*query.GroupUsersSearchQuery, error) {
|
|
|
|
|
offset, limit, asc, err := filter.PaginationPbToQuery(systemDefaults, req.GetPagination())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
queries, err := groupUsersSearchFiltersToQuery(req.GetFilters())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return &query.GroupUsersSearchQuery{
|
|
|
|
|
SearchRequest: query.SearchRequest{
|
|
|
|
|
Offset: offset,
|
|
|
|
|
Limit: limit,
|
|
|
|
|
Asc: asc,
|
|
|
|
|
SortingColumn: groupUsersFieldNameToSortingColumn(req.SortingColumn),
|
|
|
|
|
},
|
|
|
|
|
Queries: queries,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func groupUsersSearchFiltersToQuery(filters []*group_v2.GroupUsersSearchFilter) (_ []query.SearchQuery, err error) {
|
|
|
|
|
q := make([]query.SearchQuery, len(filters))
|
|
|
|
|
for i, f := range filters {
|
|
|
|
|
q[i], err = groupUsersFilterToQuery(f)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return q, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func groupUsersFilterToQuery(f *group_v2.GroupUsersSearchFilter) (query.SearchQuery, error) {
|
|
|
|
|
switch q := f.Filter.(type) {
|
|
|
|
|
case *group_v2.GroupUsersSearchFilter_UserIds:
|
|
|
|
|
return query.NewGroupUsersUserIDsSearchQuery(q.UserIds.GetIds())
|
|
|
|
|
case *group_v2.GroupUsersSearchFilter_GroupIds:
|
|
|
|
|
return query.NewGroupUsersGroupIDsSearchQuery(q.GroupIds.GetIds())
|
|
|
|
|
default:
|
|
|
|
|
return nil, zerrors.ThrowInvalidArgument(nil, "GRPC-osMHhx", "List.Query.Invalid")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func groupUsersFieldNameToSortingColumn(field *group_v2.GroupUserFieldName) query.Column {
|
|
|
|
|
if field == nil {
|
|
|
|
|
return query.GroupUsersColumnCreationDate
|
|
|
|
|
}
|
|
|
|
|
switch *field {
|
|
|
|
|
case group_v2.GroupUserFieldName_GROUP_USER_FIELD_NAME_CREATION_DATE,
|
|
|
|
|
group_v2.GroupUserFieldName_GROUP_USER_FIELD_NAME_UNSPECIFIED:
|
|
|
|
|
return query.GroupUsersColumnCreationDate
|
|
|
|
|
case group_v2.GroupUserFieldName_GROUP_USER_FIELD_NAME_USER_ID:
|
|
|
|
|
return query.GroupUsersColumnUserID
|
|
|
|
|
case group_v2.GroupUserFieldName_GROUP_USER_FIELD_NAME_GROUP_ID:
|
|
|
|
|
return query.GroupUsersColumnGroupID
|
|
|
|
|
default:
|
|
|
|
|
return query.GroupUsersColumnCreationDate
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func groupUsersToPb(groupUsers []*query.GroupUser) []*group_v2.GroupUser {
|
|
|
|
|
pbGroupUsers := make([]*group_v2.GroupUser, len(groupUsers))
|
|
|
|
|
for i, gu := range groupUsers {
|
|
|
|
|
pbGroupUsers[i] = groupUserToPb(gu)
|
|
|
|
|
}
|
|
|
|
|
return pbGroupUsers
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func groupUserToPb(gu *query.GroupUser) *group_v2.GroupUser {
|
|
|
|
|
return &group_v2.GroupUser{
|
|
|
|
|
GroupId: gu.GroupID,
|
|
|
|
|
OrganizationId: gu.ResourceOwner,
|
|
|
|
|
User: &authorization_v2beta.User{
|
|
|
|
|
Id: gu.UserID,
|
|
|
|
|
PreferredLoginName: gu.PreferredLoginName,
|
|
|
|
|
DisplayName: gu.DisplayName,
|
|
|
|
|
AvatarUrl: gu.AvatarUrl,
|
|
|
|
|
OrganizationId: gu.ResourceOwner,
|
|
|
|
|
},
|
|
|
|
|
CreationDate: timestamppb.New(gu.CreationDate),
|
|
|
|
|
}
|
|
|
|
|
}
|