mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-06 14:22:15 +00:00
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 <[email protected]>
This commit is contained in:
co-authored by
Livio Spring
parent
ee3654fe6f
commit
b81dedcaea
@@ -0,0 +1,160 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
repo "github.com/zitadel/zitadel/internal/repository/group"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type CreateGroup struct {
|
||||
models.ObjectRoot
|
||||
|
||||
Name string
|
||||
Description string
|
||||
}
|
||||
|
||||
func (g *CreateGroup) IsValid() error {
|
||||
if strings.TrimSpace(g.Name) == "" {
|
||||
return zerrors.ThrowInvalidArgument(nil, "GROUP-m177lN", "Errors.Group.InvalidName")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateGroup creates a new user group in an organization
|
||||
func (c *Commands) CreateGroup(ctx context.Context, group *CreateGroup) (details *domain.ObjectDetails, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
// todo: check permissions
|
||||
|
||||
if err = group.IsValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if group.ResourceOwner == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "CMDGRP-msc0Tt", "Errors.Group.MissingOrganizationID")
|
||||
}
|
||||
// check whether the organization where the group should be created exists
|
||||
err = c.checkOrgExists(ctx, group.ResourceOwner)
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "CMDGRP-j1mH8l", "Errors.Org.NotFound")
|
||||
}
|
||||
|
||||
// create a unique group ID if not provided
|
||||
if group.AggregateID == "" {
|
||||
group.AggregateID, err = c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// check if a group with the same ID already exists
|
||||
groupWriteModel, err := c.getGroupWriteModelByID(ctx, group.AggregateID, group.ResourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if groupWriteModel.State.Exists() {
|
||||
return nil, zerrors.ThrowAlreadyExists(nil, "CMDGRP-shRut3", "Errors.Group.AlreadyExists")
|
||||
}
|
||||
|
||||
err = c.pushAppendAndReduce(ctx,
|
||||
groupWriteModel,
|
||||
repo.NewGroupAddedEvent(ctx,
|
||||
GroupAggregateFromWriteModel(ctx, &groupWriteModel.WriteModel),
|
||||
group.Name,
|
||||
group.Description,
|
||||
))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&groupWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
type UpdateGroup struct {
|
||||
models.ObjectRoot
|
||||
|
||||
Name *string
|
||||
Description *string
|
||||
}
|
||||
|
||||
func (g *UpdateGroup) IsValid() error {
|
||||
if g.Name != nil && strings.TrimSpace(*g.Name) == "" {
|
||||
return zerrors.ThrowInvalidArgument(nil, "GROUP-dUNd3r", "Errors.Group.InvalidName")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateGroup updates a user group in an organization
|
||||
func (c *Commands) UpdateGroup(ctx context.Context, groupUpdate *UpdateGroup) (details *domain.ObjectDetails, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
if err = groupUpdate.IsValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// todo: check permissions
|
||||
existingGroup, err := c.getGroupWriteModelByID(ctx, groupUpdate.AggregateID, groupUpdate.ResourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !existingGroup.State.Exists() {
|
||||
return nil, zerrors.ThrowNotFound(nil, "CMDGRP-b33zly", "Errors.Group.NotFound")
|
||||
}
|
||||
|
||||
changedEvent := existingGroup.NewChangedEvent(
|
||||
ctx,
|
||||
GroupAggregateFromWriteModel(ctx, &existingGroup.WriteModel),
|
||||
groupUpdate.Name,
|
||||
groupUpdate.Description,
|
||||
)
|
||||
if changedEvent == nil {
|
||||
return writeModelToObjectDetails(&existingGroup.WriteModel), nil
|
||||
}
|
||||
|
||||
err = c.pushAppendAndReduce(ctx,
|
||||
existingGroup,
|
||||
changedEvent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existingGroup.WriteModel), nil
|
||||
}
|
||||
|
||||
// DeleteGroup deletes a user group from an organization
|
||||
func (c *Commands) DeleteGroup(ctx context.Context, groupID string) (details *domain.ObjectDetails, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
existingGroup, err := c.getGroupWriteModelByID(ctx, groupID, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !existingGroup.State.Exists() {
|
||||
return writeModelToObjectDetails(&existingGroup.WriteModel), nil
|
||||
}
|
||||
|
||||
err = c.pushAppendAndReduce(ctx,
|
||||
existingGroup,
|
||||
repo.NewGroupRemovedEvent(ctx,
|
||||
GroupAggregateFromWriteModel(ctx, &existingGroup.WriteModel),
|
||||
existingGroup.Name,
|
||||
))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existingGroup.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) getGroupWriteModelByID(ctx context.Context, id, orgID string) (*GroupWriteModel, error) {
|
||||
groupWriteModel := NewGroupWriteModel(id, orgID)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, groupWriteModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return groupWriteModel, nil
|
||||
}
|
||||
Reference in New Issue
Block a user