mirror of
				https://github.com/zitadel/zitadel.git
				synced 2025-10-31 09:40:17 +00:00 
			
		
		
		
	 e4a4b7cfbe
			
		
	
	e4a4b7cfbe
	
	
	
		
			
			* chore(proto): update versions
* change protoc plugin
* some cleanups
* define api for setting emails in new api
* implement user.SetEmail
* move SetEmail buisiness logic into command
* resuse newCryptoCode
* command: add ChangeEmail unit tests
Not complete, was not able to mock the generator.
* Revert "resuse newCryptoCode"
This reverts commit c89e90ae35.
* undo change to crypto code generators
* command: use a generator so we can test properly
* command: reorganise ChangeEmail
improve test coverage
* implement VerifyEmail
including unit tests
* add URL template tests
* begin user creation
* change protos
* implement metadata and move context
* merge commands
* proto: change context to object
* remove old auth option
* remove old auth option
* fix linting errors
run gci on modified files
* add permission checks and fix some errors
* comments
* comments
* update email requests
* rename proto requests
* cleanup and docs
* simplify
* simplify
* fix setup
* remove unused proto messages / fields
---------
Co-authored-by: adlerhurst <silvan.reusser@gmail.com>
Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
		
	
		
			
				
	
	
		
			108 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			108 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package user
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"io"
 | |
| 
 | |
| 	"golang.org/x/text/language"
 | |
| 
 | |
| 	"github.com/zitadel/zitadel/internal/api/grpc/object/v2"
 | |
| 	"github.com/zitadel/zitadel/internal/command"
 | |
| 	"github.com/zitadel/zitadel/internal/domain"
 | |
| 	"github.com/zitadel/zitadel/internal/errors"
 | |
| 	"github.com/zitadel/zitadel/pkg/grpc/user/v2alpha"
 | |
| )
 | |
| 
 | |
| func (s *Server) AddHumanUser(ctx context.Context, req *user.AddHumanUserRequest) (_ *user.AddHumanUserResponse, err error) {
 | |
| 	human, err := addUserRequestToAddHuman(req)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	err = s.command.AddHuman(ctx, req.GetOrganisation().GetOrgId(), human, false)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return &user.AddHumanUserResponse{
 | |
| 		UserId:    human.ID,
 | |
| 		Details:   object.DomainToDetailsPb(human.Details),
 | |
| 		EmailCode: human.EmailCode,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func addUserRequestToAddHuman(req *user.AddHumanUserRequest) (*command.AddHuman, error) {
 | |
| 	username := req.GetUsername()
 | |
| 	if username == "" {
 | |
| 		username = req.GetEmail().GetEmail()
 | |
| 	}
 | |
| 	var urlTemplate string
 | |
| 	if req.GetEmail().GetSendCode() != nil {
 | |
| 		urlTemplate = req.GetEmail().GetSendCode().GetUrlTemplate()
 | |
| 		// test the template execution so the async notification will not fail because of it and the user won't realize
 | |
| 		if err := domain.RenderConfirmURLTemplate(io.Discard, urlTemplate, req.GetUserId(), "code", "orgID"); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 	bcryptedPassword, err := hashedPasswordToCommand(req.GetHashedPassword())
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	passwordChangeRequired := req.GetPassword().GetChangeRequired() || req.GetHashedPassword().GetChangeRequired()
 | |
| 	metadata := make([]*command.AddMetadataEntry, len(req.Metadata))
 | |
| 	for i, metadataEntry := range req.Metadata {
 | |
| 		metadata[i] = &command.AddMetadataEntry{
 | |
| 			Key:   metadataEntry.GetKey(),
 | |
| 			Value: metadataEntry.GetValue(),
 | |
| 		}
 | |
| 	}
 | |
| 	return &command.AddHuman{
 | |
| 		ID:          req.GetUserId(),
 | |
| 		Username:    username,
 | |
| 		FirstName:   req.GetProfile().GetFirstName(),
 | |
| 		LastName:    req.GetProfile().GetLastName(),
 | |
| 		NickName:    req.GetProfile().GetNickName(),
 | |
| 		DisplayName: req.GetProfile().GetDisplayName(),
 | |
| 		Email: command.Email{
 | |
| 			Address:     domain.EmailAddress(req.GetEmail().GetEmail()),
 | |
| 			Verified:    req.GetEmail().GetIsVerified(),
 | |
| 			ReturnCode:  req.GetEmail().GetReturnCode() != nil,
 | |
| 			URLTemplate: urlTemplate,
 | |
| 		},
 | |
| 		PreferredLanguage:      language.Make(req.GetProfile().GetPreferredLanguage()),
 | |
| 		Gender:                 genderToDomain(req.GetProfile().GetGender()),
 | |
| 		Phone:                  command.Phone{}, // TODO: add as soon as possible
 | |
| 		Password:               req.GetPassword().GetPassword(),
 | |
| 		BcryptedPassword:       bcryptedPassword,
 | |
| 		PasswordChangeRequired: passwordChangeRequired,
 | |
| 		Passwordless:           false,
 | |
| 		ExternalIDP:            false,
 | |
| 		Register:               false,
 | |
| 		Metadata:               metadata,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func genderToDomain(gender user.Gender) domain.Gender {
 | |
| 	switch gender {
 | |
| 	case user.Gender_GENDER_UNSPECIFIED:
 | |
| 		return domain.GenderUnspecified
 | |
| 	case user.Gender_GENDER_FEMALE:
 | |
| 		return domain.GenderFemale
 | |
| 	case user.Gender_GENDER_MALE:
 | |
| 		return domain.GenderMale
 | |
| 	case user.Gender_GENDER_DIVERSE:
 | |
| 		return domain.GenderDiverse
 | |
| 	default:
 | |
| 		return domain.GenderUnspecified
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func hashedPasswordToCommand(hashed *user.HashedPassword) (string, error) {
 | |
| 	if hashed == nil {
 | |
| 		return "", nil
 | |
| 	}
 | |
| 	// we currently only handle bcrypt
 | |
| 	if hashed.GetAlgorithm() != "bcrypt" {
 | |
| 		return "", errors.ThrowInvalidArgument(nil, "USER-JDk4t", "Errors.InvalidArgument")
 | |
| 	}
 | |
| 	return hashed.GetHash(), nil
 | |
| }
 |