mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 01:27:32 +00:00
feat: user service v2 create, update and remove (#6996)
* feat: user service v2 remove user * feat: user service v2 add user human * feat: user service v2 change user human * feat: user service v2 change user human unit tests * feat: user service v2 reactivate, deactivate, lock, unlock user * feat: user service v2 integration tests * fix: merge back origin/main * lint: linter corrections * fix: move permission check for isVerfied and password change * fix: add deprecated notices and other review comments * fix: consistent naming in proto * fix: errors package renaming * fix: remove / delete user renaming in integration test * fix: machine user status changes through user v2 api * fix: linting changes * fix: linting changes * fix: changes from review * fix: changes from review * fix: changes from review * fix: changes from review * fix: changes from review --------- Co-authored-by: Tim Möhlmann <tim+github@zitadel.com> Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
@@ -28,7 +28,7 @@ func (s *Server) AddHumanUser(ctx context.Context, req *user.AddHumanUserRequest
|
||||
return nil, err
|
||||
}
|
||||
orgID := authz.GetCtxData(ctx).OrgID
|
||||
if err = s.command.AddHuman(ctx, orgID, human, false); err != nil {
|
||||
if err = s.command.AddUserHuman(ctx, orgID, human, false, s.userCodeAlg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user.AddHumanUserResponse{
|
||||
@@ -113,6 +113,172 @@ func genderToDomain(gender user.Gender) domain.Gender {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) UpdateHumanUser(ctx context.Context, req *user.UpdateHumanUserRequest) (_ *user.UpdateHumanUserResponse, err error) {
|
||||
human, err := UpdateUserRequestToChangeHuman(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = s.command.ChangeUserHuman(ctx, human, s.userCodeAlg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user.UpdateHumanUserResponse{
|
||||
Details: object.DomainToDetailsPb(human.Details),
|
||||
EmailCode: human.EmailCode,
|
||||
PhoneCode: human.PhoneCode,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) LockUser(ctx context.Context, req *user.LockUserRequest) (_ *user.LockUserResponse, err error) {
|
||||
details, err := s.command.LockUserV2(ctx, req.UserId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user.LockUserResponse{
|
||||
Details: object.DomainToDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UnlockUser(ctx context.Context, req *user.UnlockUserRequest) (_ *user.UnlockUserResponse, err error) {
|
||||
details, err := s.command.UnlockUserV2(ctx, req.UserId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user.UnlockUserResponse{
|
||||
Details: object.DomainToDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) DeactivateUser(ctx context.Context, req *user.DeactivateUserRequest) (_ *user.DeactivateUserResponse, err error) {
|
||||
details, err := s.command.DeactivateUserV2(ctx, req.UserId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user.DeactivateUserResponse{
|
||||
Details: object.DomainToDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ReactivateUser(ctx context.Context, req *user.ReactivateUserRequest) (_ *user.ReactivateUserResponse, err error) {
|
||||
details, err := s.command.ReactivateUserV2(ctx, req.UserId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user.ReactivateUserResponse{
|
||||
Details: object.DomainToDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ifNotNilPtr[v, p any](value *v, conv func(v) p) *p {
|
||||
var pNil *p
|
||||
if value == nil {
|
||||
return pNil
|
||||
}
|
||||
pVal := conv(*value)
|
||||
return &pVal
|
||||
}
|
||||
|
||||
func UpdateUserRequestToChangeHuman(req *user.UpdateHumanUserRequest) (*command.ChangeHuman, error) {
|
||||
email, err := SetHumanEmailToEmail(req.Email, req.GetUserId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &command.ChangeHuman{
|
||||
ID: req.GetUserId(),
|
||||
Username: req.Username,
|
||||
Profile: SetHumanProfileToProfile(req.Profile),
|
||||
Email: email,
|
||||
Phone: SetHumanPhoneToPhone(req.Phone),
|
||||
Password: SetHumanPasswordToPassword(req.Password),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func SetHumanProfileToProfile(profile *user.SetHumanProfile) *command.Profile {
|
||||
if profile == nil {
|
||||
return nil
|
||||
}
|
||||
var firstName *string
|
||||
if profile.GivenName != "" {
|
||||
firstName = &profile.GivenName
|
||||
}
|
||||
var lastName *string
|
||||
if profile.FamilyName != "" {
|
||||
lastName = &profile.FamilyName
|
||||
}
|
||||
return &command.Profile{
|
||||
FirstName: firstName,
|
||||
LastName: lastName,
|
||||
NickName: profile.NickName,
|
||||
DisplayName: profile.DisplayName,
|
||||
PreferredLanguage: ifNotNilPtr(profile.PreferredLanguage, language.Make),
|
||||
Gender: ifNotNilPtr(profile.Gender, genderToDomain),
|
||||
}
|
||||
}
|
||||
|
||||
func SetHumanEmailToEmail(email *user.SetHumanEmail, userID string) (*command.Email, error) {
|
||||
if email == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var urlTemplate string
|
||||
if email.GetSendCode() != nil && email.GetSendCode().UrlTemplate != nil {
|
||||
urlTemplate = *email.GetSendCode().UrlTemplate
|
||||
if err := domain.RenderConfirmURLTemplate(io.Discard, urlTemplate, userID, "code", "orgID"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &command.Email{
|
||||
Address: domain.EmailAddress(email.Email),
|
||||
Verified: email.GetIsVerified(),
|
||||
ReturnCode: email.GetReturnCode() != nil,
|
||||
URLTemplate: urlTemplate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func SetHumanPhoneToPhone(phone *user.SetHumanPhone) *command.Phone {
|
||||
if phone == nil {
|
||||
return nil
|
||||
}
|
||||
return &command.Phone{
|
||||
Number: domain.PhoneNumber(phone.GetPhone()),
|
||||
Verified: phone.GetIsVerified(),
|
||||
ReturnCode: phone.GetReturnCode() != nil,
|
||||
}
|
||||
}
|
||||
|
||||
func SetHumanPasswordToPassword(password *user.SetPassword) *command.Password {
|
||||
if password == nil {
|
||||
return nil
|
||||
}
|
||||
var changeRequired bool
|
||||
var passwordStr *string
|
||||
if password.GetPassword() != nil {
|
||||
passwordStr = &password.GetPassword().Password
|
||||
changeRequired = password.GetPassword().GetChangeRequired()
|
||||
}
|
||||
var hash *string
|
||||
if password.GetHashedPassword() != nil {
|
||||
hash = &password.GetHashedPassword().Hash
|
||||
changeRequired = password.GetHashedPassword().GetChangeRequired()
|
||||
}
|
||||
var code *string
|
||||
if password.GetVerificationCode() != "" {
|
||||
codeT := password.GetVerificationCode()
|
||||
code = &codeT
|
||||
}
|
||||
var oldPassword *string
|
||||
if password.GetCurrentPassword() != "" {
|
||||
oldPasswordT := password.GetCurrentPassword()
|
||||
oldPassword = &oldPasswordT
|
||||
}
|
||||
return &command.Password{
|
||||
PasswordCode: code,
|
||||
OldPassword: oldPassword,
|
||||
Password: passwordStr,
|
||||
EncodedPasswordHash: hash,
|
||||
ChangeRequired: changeRequired,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) AddIDPLink(ctx context.Context, req *user.AddIDPLinkRequest) (_ *user.AddIDPLinkResponse, err error) {
|
||||
orgID := authz.GetCtxData(ctx).OrgID
|
||||
details, err := s.command.AddUserIDPLink(ctx, req.UserId, orgID, &command.AddLink{
|
||||
@@ -128,6 +294,92 @@ func (s *Server) AddIDPLink(ctx context.Context, req *user.AddIDPLinkRequest) (_
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteUser(ctx context.Context, req *user.DeleteUserRequest) (_ *user.DeleteUserResponse, err error) {
|
||||
memberships, grants, err := s.removeUserDependencies(ctx, req.GetUserId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
details, err := s.command.RemoveUserV2(ctx, req.UserId, memberships, grants...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user.DeleteUserResponse{
|
||||
Details: object.DomainToDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) removeUserDependencies(ctx context.Context, userID string) ([]*command.CascadingMembership, []string, error) {
|
||||
userGrantUserQuery, err := query.NewUserGrantUserIDSearchQuery(userID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
grants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{userGrantUserQuery},
|
||||
}, true, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
membershipsUserQuery, err := query.NewMembershipUserIDQuery(userID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
memberships, err := s.query.Memberships(ctx, &query.MembershipSearchQuery{
|
||||
Queries: []query.SearchQuery{membershipsUserQuery},
|
||||
}, false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return cascadingMemberships(memberships.Memberships), userGrantsToIDs(grants.UserGrants), nil
|
||||
}
|
||||
|
||||
func cascadingMemberships(memberships []*query.Membership) []*command.CascadingMembership {
|
||||
cascades := make([]*command.CascadingMembership, len(memberships))
|
||||
for i, membership := range memberships {
|
||||
cascades[i] = &command.CascadingMembership{
|
||||
UserID: membership.UserID,
|
||||
ResourceOwner: membership.ResourceOwner,
|
||||
IAM: cascadingIAMMembership(membership.IAM),
|
||||
Org: cascadingOrgMembership(membership.Org),
|
||||
Project: cascadingProjectMembership(membership.Project),
|
||||
ProjectGrant: cascadingProjectGrantMembership(membership.ProjectGrant),
|
||||
}
|
||||
}
|
||||
return cascades
|
||||
}
|
||||
|
||||
func cascadingIAMMembership(membership *query.IAMMembership) *command.CascadingIAMMembership {
|
||||
if membership == nil {
|
||||
return nil
|
||||
}
|
||||
return &command.CascadingIAMMembership{IAMID: membership.IAMID}
|
||||
}
|
||||
func cascadingOrgMembership(membership *query.OrgMembership) *command.CascadingOrgMembership {
|
||||
if membership == nil {
|
||||
return nil
|
||||
}
|
||||
return &command.CascadingOrgMembership{OrgID: membership.OrgID}
|
||||
}
|
||||
func cascadingProjectMembership(membership *query.ProjectMembership) *command.CascadingProjectMembership {
|
||||
if membership == nil {
|
||||
return nil
|
||||
}
|
||||
return &command.CascadingProjectMembership{ProjectID: membership.ProjectID}
|
||||
}
|
||||
func cascadingProjectGrantMembership(membership *query.ProjectGrantMembership) *command.CascadingProjectGrantMembership {
|
||||
if membership == nil {
|
||||
return nil
|
||||
}
|
||||
return &command.CascadingProjectGrantMembership{ProjectID: membership.ProjectID, GrantID: membership.GrantID}
|
||||
}
|
||||
|
||||
func userGrantsToIDs(userGrants []*query.UserGrant) []string {
|
||||
converted := make([]string, len(userGrants))
|
||||
for i, grant := range userGrants {
|
||||
converted[i] = grant.ID
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func (s *Server) StartIdentityProviderIntent(ctx context.Context, req *user.StartIdentityProviderIntentRequest) (_ *user.StartIdentityProviderIntentResponse, err error) {
|
||||
switch t := req.GetContent().(type) {
|
||||
case *user.StartIdentityProviderIntentRequest_Urls:
|
||||
|
Reference in New Issue
Block a user