2025-06-04 09:17:23 +02:00
|
|
|
package user
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"io"
|
|
|
|
|
feat: exchange gRPC server implementation to connectRPC (#10145)
# Which Problems Are Solved
The current maintained gRPC server in combination with a REST (grpc)
gateway is getting harder and harder to maintain. Additionally, there
have been and still are issues with supporting / displaying `oneOf`s
correctly.
We therefore decided to exchange the server implementation to
connectRPC, which apart from supporting connect as protocol, also also
"standard" gRCP clients as well as HTTP/1.1 / rest like clients, e.g.
curl directly call the server without any additional gateway.
# How the Problems Are Solved
- All v2 services are moved to connectRPC implementation. (v1 services
are still served as pure grpc servers)
- All gRPC server interceptors were migrated / copied to a corresponding
connectRPC interceptor.
- API.ListGrpcServices and API. ListGrpcMethods were changed to include
the connect services and endpoints.
- gRPC server reflection was changed to a `StaticReflector` using the
`ListGrpcServices` list.
- The `grpc.Server` interfaces was split into different combinations to
be able to handle the different cases (grpc server and prefixed gateway,
connect server with grpc gateway, connect server only, ...)
- Docs of services serving connectRPC only with no additional gateway
(instance, webkey, project, app, org v2 beta) are changed to expose that
- since the plugin is not yet available on buf, we download it using
`postinstall` hook of the docs
# Additional Changes
- WebKey service is added as v2 service (in addition to the current
v2beta)
# Additional Context
closes #9483
---------
Co-authored-by: Elio Bischof <elio@zitadel.com>
2025-07-04 10:06:20 -04:00
|
|
|
"connectrpc.com/connect"
|
2025-06-04 09:17:23 +02:00
|
|
|
"golang.org/x/text/language"
|
|
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
|
|
|
|
|
|
"github.com/zitadel/zitadel/internal/command"
|
|
|
|
"github.com/zitadel/zitadel/internal/domain"
|
|
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
|
|
|
legacyobject "github.com/zitadel/zitadel/pkg/grpc/object/v2"
|
|
|
|
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
|
|
|
)
|
|
|
|
|
feat: exchange gRPC server implementation to connectRPC (#10145)
# Which Problems Are Solved
The current maintained gRPC server in combination with a REST (grpc)
gateway is getting harder and harder to maintain. Additionally, there
have been and still are issues with supporting / displaying `oneOf`s
correctly.
We therefore decided to exchange the server implementation to
connectRPC, which apart from supporting connect as protocol, also also
"standard" gRCP clients as well as HTTP/1.1 / rest like clients, e.g.
curl directly call the server without any additional gateway.
# How the Problems Are Solved
- All v2 services are moved to connectRPC implementation. (v1 services
are still served as pure grpc servers)
- All gRPC server interceptors were migrated / copied to a corresponding
connectRPC interceptor.
- API.ListGrpcServices and API. ListGrpcMethods were changed to include
the connect services and endpoints.
- gRPC server reflection was changed to a `StaticReflector` using the
`ListGrpcServices` list.
- The `grpc.Server` interfaces was split into different combinations to
be able to handle the different cases (grpc server and prefixed gateway,
connect server with grpc gateway, connect server only, ...)
- Docs of services serving connectRPC only with no additional gateway
(instance, webkey, project, app, org v2 beta) are changed to expose that
- since the plugin is not yet available on buf, we download it using
`postinstall` hook of the docs
# Additional Changes
- WebKey service is added as v2 service (in addition to the current
v2beta)
# Additional Context
closes #9483
---------
Co-authored-by: Elio Bischof <elio@zitadel.com>
2025-07-04 10:06:20 -04:00
|
|
|
func (s *Server) createUserTypeHuman(ctx context.Context, humanPb *user.CreateUserRequest_Human, orgId string, userName, userId *string) (*connect.Response[user.CreateUserResponse], error) {
|
2025-07-04 18:12:59 +02:00
|
|
|
metadataEntries := make([]*user.SetMetadataEntry, len(humanPb.Metadata))
|
|
|
|
for i, metadataEntry := range humanPb.Metadata {
|
|
|
|
metadataEntries[i] = &user.SetMetadataEntry{
|
|
|
|
Key: metadataEntry.GetKey(),
|
|
|
|
Value: metadataEntry.GetValue(),
|
|
|
|
}
|
|
|
|
}
|
2025-06-04 09:17:23 +02:00
|
|
|
addHumanPb := &user.AddHumanUserRequest{
|
|
|
|
Username: userName,
|
|
|
|
UserId: userId,
|
|
|
|
Organization: &legacyobject.Organization{
|
|
|
|
Org: &legacyobject.Organization_OrgId{OrgId: orgId},
|
|
|
|
},
|
|
|
|
Profile: humanPb.Profile,
|
|
|
|
Email: humanPb.Email,
|
|
|
|
Phone: humanPb.Phone,
|
|
|
|
IdpLinks: humanPb.IdpLinks,
|
|
|
|
TotpSecret: humanPb.TotpSecret,
|
2025-07-04 18:12:59 +02:00
|
|
|
Metadata: metadataEntries,
|
2025-06-04 09:17:23 +02:00
|
|
|
}
|
|
|
|
switch pwType := humanPb.GetPasswordType().(type) {
|
|
|
|
case *user.CreateUserRequest_Human_HashedPassword:
|
|
|
|
addHumanPb.PasswordType = &user.AddHumanUserRequest_HashedPassword{
|
|
|
|
HashedPassword: pwType.HashedPassword,
|
|
|
|
}
|
|
|
|
case *user.CreateUserRequest_Human_Password:
|
|
|
|
addHumanPb.PasswordType = &user.AddHumanUserRequest_Password{
|
|
|
|
Password: pwType.Password,
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
// optional password is not set
|
|
|
|
}
|
|
|
|
newHuman, err := AddUserRequestToAddHuman(addHumanPb)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err = s.command.AddUserHuman(
|
|
|
|
ctx,
|
|
|
|
orgId,
|
|
|
|
newHuman,
|
|
|
|
false,
|
|
|
|
s.userCodeAlg,
|
|
|
|
); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
feat: exchange gRPC server implementation to connectRPC (#10145)
# Which Problems Are Solved
The current maintained gRPC server in combination with a REST (grpc)
gateway is getting harder and harder to maintain. Additionally, there
have been and still are issues with supporting / displaying `oneOf`s
correctly.
We therefore decided to exchange the server implementation to
connectRPC, which apart from supporting connect as protocol, also also
"standard" gRCP clients as well as HTTP/1.1 / rest like clients, e.g.
curl directly call the server without any additional gateway.
# How the Problems Are Solved
- All v2 services are moved to connectRPC implementation. (v1 services
are still served as pure grpc servers)
- All gRPC server interceptors were migrated / copied to a corresponding
connectRPC interceptor.
- API.ListGrpcServices and API. ListGrpcMethods were changed to include
the connect services and endpoints.
- gRPC server reflection was changed to a `StaticReflector` using the
`ListGrpcServices` list.
- The `grpc.Server` interfaces was split into different combinations to
be able to handle the different cases (grpc server and prefixed gateway,
connect server with grpc gateway, connect server only, ...)
- Docs of services serving connectRPC only with no additional gateway
(instance, webkey, project, app, org v2 beta) are changed to expose that
- since the plugin is not yet available on buf, we download it using
`postinstall` hook of the docs
# Additional Changes
- WebKey service is added as v2 service (in addition to the current
v2beta)
# Additional Context
closes #9483
---------
Co-authored-by: Elio Bischof <elio@zitadel.com>
2025-07-04 10:06:20 -04:00
|
|
|
return connect.NewResponse(&user.CreateUserResponse{
|
2025-06-04 09:17:23 +02:00
|
|
|
Id: newHuman.ID,
|
|
|
|
CreationDate: timestamppb.New(newHuman.Details.EventDate),
|
|
|
|
EmailCode: newHuman.EmailCode,
|
|
|
|
PhoneCode: newHuman.PhoneCode,
|
feat: exchange gRPC server implementation to connectRPC (#10145)
# Which Problems Are Solved
The current maintained gRPC server in combination with a REST (grpc)
gateway is getting harder and harder to maintain. Additionally, there
have been and still are issues with supporting / displaying `oneOf`s
correctly.
We therefore decided to exchange the server implementation to
connectRPC, which apart from supporting connect as protocol, also also
"standard" gRCP clients as well as HTTP/1.1 / rest like clients, e.g.
curl directly call the server without any additional gateway.
# How the Problems Are Solved
- All v2 services are moved to connectRPC implementation. (v1 services
are still served as pure grpc servers)
- All gRPC server interceptors were migrated / copied to a corresponding
connectRPC interceptor.
- API.ListGrpcServices and API. ListGrpcMethods were changed to include
the connect services and endpoints.
- gRPC server reflection was changed to a `StaticReflector` using the
`ListGrpcServices` list.
- The `grpc.Server` interfaces was split into different combinations to
be able to handle the different cases (grpc server and prefixed gateway,
connect server with grpc gateway, connect server only, ...)
- Docs of services serving connectRPC only with no additional gateway
(instance, webkey, project, app, org v2 beta) are changed to expose that
- since the plugin is not yet available on buf, we download it using
`postinstall` hook of the docs
# Additional Changes
- WebKey service is added as v2 service (in addition to the current
v2beta)
# Additional Context
closes #9483
---------
Co-authored-by: Elio Bischof <elio@zitadel.com>
2025-07-04 10:06:20 -04:00
|
|
|
}), nil
|
2025-06-04 09:17:23 +02:00
|
|
|
}
|
|
|
|
|
feat: exchange gRPC server implementation to connectRPC (#10145)
# Which Problems Are Solved
The current maintained gRPC server in combination with a REST (grpc)
gateway is getting harder and harder to maintain. Additionally, there
have been and still are issues with supporting / displaying `oneOf`s
correctly.
We therefore decided to exchange the server implementation to
connectRPC, which apart from supporting connect as protocol, also also
"standard" gRCP clients as well as HTTP/1.1 / rest like clients, e.g.
curl directly call the server without any additional gateway.
# How the Problems Are Solved
- All v2 services are moved to connectRPC implementation. (v1 services
are still served as pure grpc servers)
- All gRPC server interceptors were migrated / copied to a corresponding
connectRPC interceptor.
- API.ListGrpcServices and API. ListGrpcMethods were changed to include
the connect services and endpoints.
- gRPC server reflection was changed to a `StaticReflector` using the
`ListGrpcServices` list.
- The `grpc.Server` interfaces was split into different combinations to
be able to handle the different cases (grpc server and prefixed gateway,
connect server with grpc gateway, connect server only, ...)
- Docs of services serving connectRPC only with no additional gateway
(instance, webkey, project, app, org v2 beta) are changed to expose that
- since the plugin is not yet available on buf, we download it using
`postinstall` hook of the docs
# Additional Changes
- WebKey service is added as v2 service (in addition to the current
v2beta)
# Additional Context
closes #9483
---------
Co-authored-by: Elio Bischof <elio@zitadel.com>
2025-07-04 10:06:20 -04:00
|
|
|
func (s *Server) updateUserTypeHuman(ctx context.Context, humanPb *user.UpdateUserRequest_Human, userId string, userName *string) (*connect.Response[user.UpdateUserResponse], error) {
|
2025-06-04 09:17:23 +02:00
|
|
|
cmd, err := updateHumanUserToCommand(userId, userName, humanPb)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err = s.command.ChangeUserHuman(ctx, cmd, s.userCodeAlg); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
feat: exchange gRPC server implementation to connectRPC (#10145)
# Which Problems Are Solved
The current maintained gRPC server in combination with a REST (grpc)
gateway is getting harder and harder to maintain. Additionally, there
have been and still are issues with supporting / displaying `oneOf`s
correctly.
We therefore decided to exchange the server implementation to
connectRPC, which apart from supporting connect as protocol, also also
"standard" gRCP clients as well as HTTP/1.1 / rest like clients, e.g.
curl directly call the server without any additional gateway.
# How the Problems Are Solved
- All v2 services are moved to connectRPC implementation. (v1 services
are still served as pure grpc servers)
- All gRPC server interceptors were migrated / copied to a corresponding
connectRPC interceptor.
- API.ListGrpcServices and API. ListGrpcMethods were changed to include
the connect services and endpoints.
- gRPC server reflection was changed to a `StaticReflector` using the
`ListGrpcServices` list.
- The `grpc.Server` interfaces was split into different combinations to
be able to handle the different cases (grpc server and prefixed gateway,
connect server with grpc gateway, connect server only, ...)
- Docs of services serving connectRPC only with no additional gateway
(instance, webkey, project, app, org v2 beta) are changed to expose that
- since the plugin is not yet available on buf, we download it using
`postinstall` hook of the docs
# Additional Changes
- WebKey service is added as v2 service (in addition to the current
v2beta)
# Additional Context
closes #9483
---------
Co-authored-by: Elio Bischof <elio@zitadel.com>
2025-07-04 10:06:20 -04:00
|
|
|
return connect.NewResponse(&user.UpdateUserResponse{
|
2025-06-04 09:17:23 +02:00
|
|
|
ChangeDate: timestamppb.New(cmd.Details.EventDate),
|
|
|
|
EmailCode: cmd.EmailCode,
|
|
|
|
PhoneCode: cmd.PhoneCode,
|
feat: exchange gRPC server implementation to connectRPC (#10145)
# Which Problems Are Solved
The current maintained gRPC server in combination with a REST (grpc)
gateway is getting harder and harder to maintain. Additionally, there
have been and still are issues with supporting / displaying `oneOf`s
correctly.
We therefore decided to exchange the server implementation to
connectRPC, which apart from supporting connect as protocol, also also
"standard" gRCP clients as well as HTTP/1.1 / rest like clients, e.g.
curl directly call the server without any additional gateway.
# How the Problems Are Solved
- All v2 services are moved to connectRPC implementation. (v1 services
are still served as pure grpc servers)
- All gRPC server interceptors were migrated / copied to a corresponding
connectRPC interceptor.
- API.ListGrpcServices and API. ListGrpcMethods were changed to include
the connect services and endpoints.
- gRPC server reflection was changed to a `StaticReflector` using the
`ListGrpcServices` list.
- The `grpc.Server` interfaces was split into different combinations to
be able to handle the different cases (grpc server and prefixed gateway,
connect server with grpc gateway, connect server only, ...)
- Docs of services serving connectRPC only with no additional gateway
(instance, webkey, project, app, org v2 beta) are changed to expose that
- since the plugin is not yet available on buf, we download it using
`postinstall` hook of the docs
# Additional Changes
- WebKey service is added as v2 service (in addition to the current
v2beta)
# Additional Context
closes #9483
---------
Co-authored-by: Elio Bischof <elio@zitadel.com>
2025-07-04 10:06:20 -04:00
|
|
|
}), nil
|
2025-06-04 09:17:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func updateHumanUserToCommand(userId string, userName *string, human *user.UpdateUserRequest_Human) (*command.ChangeHuman, error) {
|
|
|
|
phone := human.GetPhone()
|
|
|
|
if phone != nil && phone.Phone == "" && phone.GetVerification() != nil {
|
|
|
|
return nil, zerrors.ThrowInvalidArgument(nil, "USERv2-4f3d6", "Errors.User.Phone.VerifyingRemovalIsNotSupported")
|
|
|
|
}
|
|
|
|
email, err := setHumanEmailToEmail(human.Email, userId)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &command.ChangeHuman{
|
|
|
|
ID: userId,
|
|
|
|
Username: userName,
|
|
|
|
Profile: SetHumanProfileToProfile(human.Profile),
|
|
|
|
Email: email,
|
|
|
|
Phone: setHumanPhoneToPhone(human.Phone, true),
|
|
|
|
Password: setHumanPasswordToPassword(human.Password),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func updateHumanUserRequestToChangeHuman(req *user.UpdateHumanUserRequest) (*command.ChangeHuman, error) {
|
|
|
|
email, err := setHumanEmailToEmail(req.Email, req.GetUserId())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
changeHuman := &command.ChangeHuman{
|
|
|
|
ID: req.GetUserId(),
|
|
|
|
Username: req.Username,
|
|
|
|
Email: email,
|
|
|
|
Phone: setHumanPhoneToPhone(req.Phone, false),
|
|
|
|
Password: setHumanPasswordToPassword(req.Password),
|
|
|
|
}
|
|
|
|
if profile := req.GetProfile(); profile != nil {
|
|
|
|
var firstName *string
|
|
|
|
if profile.GivenName != "" {
|
|
|
|
firstName = &profile.GivenName
|
|
|
|
}
|
|
|
|
var lastName *string
|
|
|
|
if profile.FamilyName != "" {
|
|
|
|
lastName = &profile.FamilyName
|
|
|
|
}
|
|
|
|
changeHuman.Profile = SetHumanProfileToProfile(&user.UpdateUserRequest_Human_Profile{
|
|
|
|
GivenName: firstName,
|
|
|
|
FamilyName: lastName,
|
|
|
|
NickName: profile.NickName,
|
|
|
|
DisplayName: profile.DisplayName,
|
|
|
|
PreferredLanguage: profile.PreferredLanguage,
|
|
|
|
Gender: profile.Gender,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return changeHuman, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func SetHumanProfileToProfile(profile *user.UpdateUserRequest_Human_Profile) *command.Profile {
|
|
|
|
if profile == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return &command.Profile{
|
|
|
|
FirstName: profile.GivenName,
|
|
|
|
LastName: profile.FamilyName,
|
|
|
|
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, withRemove bool) *command.Phone {
|
|
|
|
if phone == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
number := phone.GetPhone()
|
|
|
|
return &command.Phone{
|
|
|
|
Number: domain.PhoneNumber(number),
|
|
|
|
Verified: phone.GetIsVerified(),
|
|
|
|
ReturnCode: phone.GetReturnCode() != nil,
|
|
|
|
Remove: withRemove && number == "",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func setHumanPasswordToPassword(password *user.SetPassword) *command.Password {
|
|
|
|
if password == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return &command.Password{
|
|
|
|
PasswordCode: password.GetVerificationCode(),
|
|
|
|
OldPassword: password.GetCurrentPassword(),
|
|
|
|
Password: password.GetPassword().GetPassword(),
|
|
|
|
EncodedPasswordHash: password.GetHashedPassword().GetHash(),
|
|
|
|
ChangeRequired: password.GetPassword().GetChangeRequired() || password.GetHashedPassword().GetChangeRequired(),
|
|
|
|
}
|
|
|
|
}
|