-
Notifications
You must be signed in to change notification settings - Fork 437
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(api): add user creation to user service (#5745)
* 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 c89e90a. * 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>
- Loading branch information
1 parent
19f2f83
commit e4a4b7c
Showing
24 changed files
with
1,168 additions
and
219 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package user | ||
|
||
import ( | ||
"errors" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
|
||
caos_errs "github.com/zitadel/zitadel/internal/errors" | ||
user "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha" | ||
) | ||
|
||
func Test_hashedPasswordToCommand(t *testing.T) { | ||
type args struct { | ||
hashed *user.HashedPassword | ||
} | ||
type res struct { | ||
want string | ||
err func(error) bool | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
res res | ||
}{ | ||
{ | ||
"not hashed", | ||
args{ | ||
hashed: nil, | ||
}, | ||
res{ | ||
"", | ||
nil, | ||
}, | ||
}, | ||
{ | ||
"hashed, not bcrypt", | ||
args{ | ||
hashed: &user.HashedPassword{ | ||
Hash: "hash", | ||
Algorithm: "custom", | ||
}, | ||
}, | ||
res{ | ||
"", | ||
func(err error) bool { | ||
return errors.Is(err, caos_errs.ThrowInvalidArgument(nil, "USER-JDk4t", "Errors.InvalidArgument")) | ||
}, | ||
}, | ||
}, | ||
{ | ||
"hashed, bcrypt", | ||
args{ | ||
hashed: &user.HashedPassword{ | ||
Hash: "hash", | ||
Algorithm: "bcrypt", | ||
}, | ||
}, | ||
res{ | ||
"hash", | ||
nil, | ||
}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
got, err := hashedPasswordToCommand(tt.args.hashed) | ||
if tt.res.err == nil { | ||
require.NoError(t, err) | ||
} | ||
if tt.res.err != nil && !tt.res.err(err) { | ||
t.Errorf("got wrong err: %v ", err) | ||
} | ||
if tt.res.err == nil { | ||
assert.Equal(t, tt.res.want, got) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
e4a4b7c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
docs – ./
zitadel-docs.vercel.app
docs-git-main-zitadel.vercel.app
docs-zitadel.vercel.app