feat: new user auth api (#1168)

* fix: correct selectors for extended writemodel

* fix: no previous checks in eventstore

* start check previous

* feat: auth user commands

* feat: auth user commands

* feat: auth user commands

* feat: otp

* feat: corrections from pr merge

* feat: webauthn

* feat: comment old webauthn

* feat: refactor user, human, machine

* feat: webauth command side

* feat: command and query side in login

* feat: fix user writemodel append events

* fix: remove creation dates on command side

* fix: remove previous sequence

* previous sequence

* fix: external idps

* Update internal/api/grpc/management/user.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* Update internal/v2/command/user_human_email.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* fix: pr changes

* fix: phone verification

Co-authored-by: adlerhurst <silvan.reusser@gmail.com>
Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
Fabi
2021-01-15 09:32:59 +01:00
committed by GitHub
parent e5731b0d3b
commit 959530ddad
74 changed files with 1554 additions and 1519 deletions

View File

@@ -2,6 +2,7 @@ package admin
import (
"context"
"github.com/caos/zitadel/internal/errors"
"github.com/golang/protobuf/ptypes/empty"
@@ -31,10 +32,11 @@ func (s *Server) IsOrgUnique(ctx context.Context, request *admin.UniqueOrgReques
}
func (s *Server) SetUpOrg(ctx context.Context, orgSetUp *admin.OrgSetUpRequest) (_ *empty.Empty, err error) {
err = s.command.SetUpOrg(ctx, orgCreateRequestToDomain(orgSetUp.Org), userCreateRequestToDomain(orgSetUp.User))
if err != nil {
return nil, err
human, _ := userCreateRequestToDomain(orgSetUp.User)
if human == nil {
return &empty.Empty{}, errors.ThrowPreconditionFailed(nil, "ADMIN-4nd9f", "Errors.User.NotHuman")
}
err = s.command.SetUpOrg(ctx, orgCreateRequestToDomain(orgSetUp.Org), human)
return &empty.Empty{}, nil
}

View File

@@ -9,22 +9,18 @@ import (
"golang.org/x/text/language"
)
func userCreateRequestToDomain(user *admin.CreateUserRequest) *domain.User {
var human *domain.Human
var machine *domain.Machine
func userCreateRequestToDomain(user *admin.CreateUserRequest) (*domain.Human, *domain.Machine) {
if h := user.GetHuman(); h != nil {
human = humanCreateToDomain(h)
human := humanCreateToDomain(h)
human.Username = user.UserName
return human, nil
}
if m := user.GetMachine(); m != nil {
machine = machineCreateToDomain(m)
}
return &domain.User{
UserName: user.UserName,
Human: human,
Machine: machine,
machine := machineCreateToDomain(m)
machine.Username = user.UserName
return nil, machine
}
return nil, nil
}
func humanCreateToDomain(u *admin.CreateHumanRequest) *domain.Human {

View File

@@ -42,7 +42,8 @@ func (s *Server) GetMyUserPhone(ctx context.Context, _ *empty.Empty) (*auth.User
}
func (s *Server) RemoveMyUserPhone(ctx context.Context, _ *empty.Empty) (*empty.Empty, error) {
err := s.repo.RemoveMyPhone(ctx)
ctxData := authz.GetCtxData(ctx)
err := s.command.RemoveHumanPhone(ctx, ctxData.UserID, ctxData.ResourceOwner)
return &empty.Empty{}, err
}
@@ -84,12 +85,14 @@ func (s *Server) ChangeMyUserEmail(ctx context.Context, request *auth.UpdateUser
}
func (s *Server) VerifyMyUserEmail(ctx context.Context, request *auth.VerifyMyUserEmailRequest) (*empty.Empty, error) {
err := s.repo.VerifyMyEmail(ctx, request.Code)
ctxData := authz.GetCtxData(ctx)
err := s.command.VerifyHumanEmail(ctx, ctxData.UserID, request.Code, ctxData.OrgID)
return &empty.Empty{}, err
}
func (s *Server) ResendMyEmailVerificationMail(ctx context.Context, _ *empty.Empty) (*empty.Empty, error) {
err := s.repo.ResendMyEmailVerificationMail(ctx)
ctxData := authz.GetCtxData(ctx)
err := s.command.CreateHumanEmailVerificationCode(ctx, ctxData.UserID, ctxData.ResourceOwner)
return &empty.Empty{}, err
}
@@ -102,25 +105,28 @@ func (s *Server) ChangeMyUserPhone(ctx context.Context, request *auth.UpdateUser
}
func (s *Server) VerifyMyUserPhone(ctx context.Context, request *auth.VerifyUserPhoneRequest) (*empty.Empty, error) {
err := s.repo.VerifyMyPhone(ctx, request.Code)
ctxData := authz.GetCtxData(ctx)
err := s.command.VerifyHumanPhone(ctx, ctxData.UserID, request.Code, ctxData.ResourceOwner)
return &empty.Empty{}, err
}
func (s *Server) ResendMyPhoneVerificationCode(ctx context.Context, _ *empty.Empty) (*empty.Empty, error) {
err := s.repo.ResendMyPhoneVerificationCode(ctx)
ctxData := authz.GetCtxData(ctx)
err := s.command.CreateHumanPhoneVerificationCode(ctx, ctxData.UserID, ctxData.ResourceOwner)
return &empty.Empty{}, err
}
func (s *Server) UpdateMyUserAddress(ctx context.Context, request *auth.UpdateUserAddressRequest) (*auth.UserAddress, error) {
address, err := s.repo.ChangeMyAddress(ctx, updateAddressToModel(ctx, request))
address, err := s.command.ChangeHumanAddress(ctx, updateAddressToDomain(ctx, request))
if err != nil {
return nil, err
}
return addressFromModel(address), nil
return addressFromDomain(address), nil
}
func (s *Server) ChangeMyPassword(ctx context.Context, request *auth.PasswordChange) (*empty.Empty, error) {
err := s.repo.ChangeMyPassword(ctx, request.OldPassword, request.NewPassword)
ctxData := authz.GetCtxData(ctx)
err := s.command.ChangePassword(ctx, ctxData.OrgID, ctxData.UserID, request.OldPassword, request.NewPassword, "")
return &empty.Empty{}, err
}
@@ -133,7 +139,7 @@ func (s *Server) SearchMyExternalIDPs(ctx context.Context, request *auth.Externa
}
func (s *Server) RemoveMyExternalIDP(ctx context.Context, request *auth.ExternalIDPRemoveRequest) (*empty.Empty, error) {
err := s.repo.RemoveMyExternalIDP(ctx, externalIDPRemoveToModel(ctx, request))
err := s.command.RemoveHumanExternalIDP(ctx, externalIDPRemoveToDomain(ctx, request))
return &empty.Empty{}, err
}
@@ -146,38 +152,44 @@ func (s *Server) GetMyPasswordComplexityPolicy(ctx context.Context, _ *empty.Emp
}
func (s *Server) AddMfaOTP(ctx context.Context, _ *empty.Empty) (_ *auth.MfaOtpResponse, err error) {
otp, err := s.repo.AddMyMFAOTP(ctx)
ctxData := authz.GetCtxData(ctx)
otp, err := s.command.AddHumanOTP(ctx, ctxData.UserID, ctxData.OrgID)
if err != nil {
return nil, err
}
return otpFromModel(otp), nil
return otpFromDomain(otp), nil
}
func (s *Server) VerifyMfaOTP(ctx context.Context, request *auth.VerifyMfaOtp) (*empty.Empty, error) {
err := s.repo.VerifyMyMFAOTPSetup(ctx, request.Code)
ctxData := authz.GetCtxData(ctx)
err := s.command.CheckMFAOTPSetup(ctx, ctxData.UserID, request.Code, "", ctxData.ResourceOwner)
return &empty.Empty{}, err
}
func (s *Server) RemoveMfaOTP(ctx context.Context, _ *empty.Empty) (_ *empty.Empty, err error) {
err = s.repo.RemoveMyMFAOTP(ctx)
ctxData := authz.GetCtxData(ctx)
err = s.command.RemoveHumanOTP(ctx, ctxData.UserID, ctxData.OrgID)
return &empty.Empty{}, err
}
func (s *Server) AddMyMfaU2F(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNResponse, err error) {
u2f, err := s.repo.AddMyMFAU2F(ctx)
ctxData := authz.GetCtxData(ctx)
u2f, err := s.command.AddHumanU2F(ctx, ctxData.UserID, ctxData.ResourceOwner, false)
if err != nil {
return nil, err
}
return verifyWebAuthNFromModel(u2f), err
return verifyWebAuthNFromDomain(u2f), err
}
func (s *Server) VerifyMyMfaU2F(ctx context.Context, request *auth.VerifyWebAuthN) (*empty.Empty, error) {
err := s.repo.VerifyMyMFAU2FSetup(ctx, request.TokenName, request.PublicKeyCredential)
ctxData := authz.GetCtxData(ctx)
err := s.command.VerifyHumanU2F(ctx, ctxData.UserID, ctxData.OrgID, request.TokenName, "", request.PublicKeyCredential)
return &empty.Empty{}, err
}
func (s *Server) RemoveMyMfaU2F(ctx context.Context, id *auth.WebAuthNTokenID) (*empty.Empty, error) {
err := s.repo.RemoveMyMFAU2F(ctx, id.Id)
ctxData := authz.GetCtxData(ctx)
err := s.command.RemoveHumanU2F(ctx, ctxData.UserID, id.Id, ctxData.OrgID)
return &empty.Empty{}, err
}
@@ -190,20 +202,23 @@ func (s *Server) GetMyPasswordless(ctx context.Context, _ *empty.Empty) (_ *auth
}
func (s *Server) AddMyPasswordless(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNResponse, err error) {
u2f, err := s.repo.AddMyPasswordless(ctx)
ctxData := authz.GetCtxData(ctx)
u2f, err := s.command.AddHumanPasswordless(ctx, ctxData.UserID, ctxData.ResourceOwner, false)
if err != nil {
return nil, err
}
return verifyWebAuthNFromModel(u2f), err
return verifyWebAuthNFromDomain(u2f), err
}
func (s *Server) VerifyMyPasswordless(ctx context.Context, request *auth.VerifyWebAuthN) (*empty.Empty, error) {
err := s.repo.VerifyMyPasswordlessSetup(ctx, request.TokenName, request.PublicKeyCredential)
ctxData := authz.GetCtxData(ctx)
err := s.command.VerifyHumanPasswordless(ctx, ctxData.UserID, ctxData.OrgID, request.TokenName, "", request.PublicKeyCredential)
return &empty.Empty{}, err
}
func (s *Server) RemoveMyPasswordless(ctx context.Context, id *auth.WebAuthNTokenID) (*empty.Empty, error) {
err := s.repo.RemoveMyPasswordless(ctx, id.Id)
ctxData := authz.GetCtxData(ctx)
err := s.command.RemoveHumanPasswordless(ctx, ctxData.UserID, id.Id, ctxData.ResourceOwner)
return &empty.Empty{}, err
}

View File

@@ -194,7 +194,7 @@ func updatePhoneToDomain(ctx context.Context, e *auth.UpdateUserPhoneRequest) *d
}
}
func addressFromModel(address *usr_model.Address) *auth.UserAddress {
func addressFromDomain(address *domain.Address) *auth.UserAddress {
creationDate, err := ptypes.TimestampProto(address.CreationDate)
logging.Log("GRPC-65FRs").OnError(err).Debug("unable to parse timestamp")
@@ -234,8 +234,8 @@ func addressViewFromModel(address *usr_model.Address) *auth.UserAddressView {
}
}
func updateAddressToModel(ctx context.Context, address *auth.UpdateUserAddressRequest) *usr_model.Address {
return &usr_model.Address{
func updateAddressToDomain(ctx context.Context, address *auth.UpdateUserAddressRequest) *domain.Address {
return &domain.Address{
ObjectRoot: ctxToObjectRoot(ctx),
Country: address.Country,
StreetAddress: address.StreetAddress,
@@ -252,11 +252,11 @@ func externalIDPSearchRequestToModel(request *auth.ExternalIDPSearchRequest) *us
}
}
func externalIDPRemoveToModel(ctx context.Context, idp *auth.ExternalIDPRemoveRequest) *usr_model.ExternalIDP {
return &usr_model.ExternalIDP{
ObjectRoot: ctxToObjectRoot(ctx),
IDPConfigID: idp.IdpConfigId,
UserID: idp.ExternalUserId,
func externalIDPRemoveToDomain(ctx context.Context, idp *auth.ExternalIDPRemoveRequest) *domain.ExternalIDP {
return &domain.ExternalIDP{
ObjectRoot: ctxToObjectRoot(ctx),
IDPConfigID: idp.IdpConfigId,
ExternalUserID: idp.ExternalUserId,
}
}
@@ -308,12 +308,12 @@ func externalIDPViewFromModel(externalIDP *usr_model.ExternalIDPView) *auth.Exte
}
}
func otpFromModel(otp *usr_model.OTP) *auth.MfaOtpResponse {
func otpFromDomain(otp *domain.OTP) *auth.MfaOtpResponse {
return &auth.MfaOtpResponse{
UserId: otp.AggregateID,
Url: otp.Url,
Secret: otp.SecretString,
State: mfaStateFromModel(otp.State),
State: mfaStateFromDomain(otp.State),
}
}
@@ -360,11 +360,11 @@ func genderToDomain(gender auth.Gender) domain.Gender {
}
}
func mfaStateFromModel(state usr_model.MFAState) auth.MFAState {
func mfaStateFromDomain(state domain.MFAState) auth.MFAState {
switch state {
case usr_model.MFAStateReady:
case domain.MFAStateReady:
return auth.MFAState_MFASTATE_READY
case usr_model.MFAStateNotReady:
case domain.MFAStateNotReady:
return auth.MFAState_MFASTATE_NOT_READY
default:
return auth.MFAState_MFASTATE_UNSPECIFIED
@@ -381,7 +381,7 @@ func mfasFromModel(mfas []*usr_model.MultiFactor) []*auth.MultiFactor {
func mfaFromModel(mfa *usr_model.MultiFactor) *auth.MultiFactor {
return &auth.MultiFactor{
State: mfaStateFromModel(mfa.State),
State: auth.MFAState(mfa.State),
Type: mfaTypeFromModel(mfa.Type),
Attribute: mfa.Attribute,
Id: mfa.ID,
@@ -431,11 +431,11 @@ func userChangesToAPI(changes *usr_model.UserChanges) (_ []*auth.Change) {
return result
}
func verifyWebAuthNFromModel(u2f *usr_model.WebAuthNToken) *auth.WebAuthNResponse {
func verifyWebAuthNFromDomain(u2f *domain.WebAuthNToken) *auth.WebAuthNResponse {
return &auth.WebAuthNResponse{
Id: u2f.WebAuthNTokenID,
PublicKey: u2f.CredentialCreationData,
State: mfaStateFromModel(u2f.State),
State: mfaStateFromDomain(u2f.State),
}
}
@@ -451,7 +451,7 @@ func webAuthNTokenFromModel(token *usr_model.WebAuthNToken) *auth.WebAuthNToken
return &auth.WebAuthNToken{
Id: token.WebAuthNTokenID,
Name: token.WebAuthNTokenName,
State: mfaStateFromModel(token.State),
State: auth.MFAState(token.State),
}
}

View File

@@ -17,20 +17,16 @@ import (
)
func appFromModel(app *proj_model.Application) *management.Application {
creationDate, err := ptypes.TimestampProto(app.CreationDate)
logging.Log("GRPC-iejs3").OnError(err).Debug("unable to parse timestamp")
changeDate, err := ptypes.TimestampProto(app.ChangeDate)
logging.Log("GRPC-di7rw").OnError(err).Debug("unable to parse timestamp")
return &management.Application{
Id: app.AppID,
State: appStateFromModel(app.State),
CreationDate: creationDate,
ChangeDate: changeDate,
Name: app.Name,
Sequence: app.Sequence,
AppConfig: appConfigFromModel(app),
Id: app.AppID,
State: appStateFromModel(app.State),
ChangeDate: changeDate,
Name: app.Name,
Sequence: app.Sequence,
AppConfig: appConfigFromModel(app),
}
}

View File

@@ -52,43 +52,39 @@ func (s *Server) IsUserUnique(ctx context.Context, request *management.UniqueUse
}
func (s *Server) CreateUser(ctx context.Context, in *management.CreateUserRequest) (*management.UserResponse, error) {
user, err := s.command.AddUser(ctx, authz.GetCtxData(ctx).OrgID, userCreateToDomain(in))
human, machine := userCreateToDomain(in)
if human != nil {
h, err := s.command.AddHuman(ctx, authz.GetCtxData(ctx).OrgID, human)
if err != nil {
return nil, err
}
return userHumanFromDomain(h), nil
}
m, err := s.command.AddMachine(ctx, authz.GetCtxData(ctx).OrgID, machine)
if err != nil {
return nil, err
}
return userFromDomain(user), nil
return userMachineFromDomain(m), nil
}
func (s *Server) DeactivateUser(ctx context.Context, in *management.UserID) (*management.UserResponse, error) {
user, err := s.command.DeactivateUser(ctx, in.Id, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return userFromDomain(user), nil
func (s *Server) DeactivateUser(ctx context.Context, in *management.UserID) (*empty.Empty, error) {
err := s.command.DeactivateUser(ctx, in.Id, authz.GetCtxData(ctx).OrgID)
return &empty.Empty{}, err
}
func (s *Server) ReactivateUser(ctx context.Context, in *management.UserID) (*management.UserResponse, error) {
user, err := s.command.ReactivateUser(ctx, in.Id, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return userFromDomain(user), nil
func (s *Server) ReactivateUser(ctx context.Context, in *management.UserID) (*empty.Empty, error) {
err := s.command.ReactivateUser(ctx, in.Id, authz.GetCtxData(ctx).OrgID)
return &empty.Empty{}, err
}
func (s *Server) LockUser(ctx context.Context, in *management.UserID) (*management.UserResponse, error) {
user, err := s.command.LockUser(ctx, in.Id, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return userFromDomain(user), nil
func (s *Server) LockUser(ctx context.Context, in *management.UserID) (*empty.Empty, error) {
err := s.command.LockUser(ctx, in.Id, authz.GetCtxData(ctx).OrgID)
return &empty.Empty{}, err
}
func (s *Server) UnlockUser(ctx context.Context, in *management.UserID) (*management.UserResponse, error) {
user, err := s.command.UnlockUser(ctx, in.Id, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return userFromDomain(user), nil
func (s *Server) UnlockUser(ctx context.Context, in *management.UserID) (*empty.Empty, error) {
err := s.command.UnlockUser(ctx, in.Id, authz.GetCtxData(ctx).OrgID)
return &empty.Empty{}, err
}
func (s *Server) DeleteUser(ctx context.Context, in *management.UserID) (*empty.Empty, error) {

View File

@@ -19,48 +19,48 @@ import (
"github.com/caos/zitadel/pkg/grpc/message"
)
func userFromDomain(user *domain.User) *management.UserResponse {
creationDate, err := ptypes.TimestampProto(user.CreationDate)
logging.Log("GRPC-8duwe").OnError(err).Debug("unable to parse timestamp")
changeDate, err := ptypes.TimestampProto(user.ChangeDate)
func userMachineFromDomain(machine *domain.Machine) *management.UserResponse {
changeDate, err := ptypes.TimestampProto(machine.ChangeDate)
logging.Log("GRPC-ckoe3d").OnError(err).Debug("unable to parse timestamp")
userResp := &management.UserResponse{
Id: user.AggregateID,
State: userStateFromDomain(user.State),
CreationDate: creationDate,
ChangeDate: changeDate,
Sequence: user.Sequence,
UserName: user.UserName,
Id: machine.AggregateID,
State: userStateFromDomain(machine.GetState()),
ChangeDate: changeDate,
Sequence: machine.Sequence,
UserName: machine.GetUsername(),
}
if user.Machine != nil {
userResp.User = &management.UserResponse_Machine{Machine: machineFromDomain(user.Machine)}
}
if user.Human != nil {
userResp.User = &management.UserResponse_Human{Human: humanFromDomain(user.Human)}
}
userResp.User = &management.UserResponse_Machine{Machine: machineFromDomain(machine)}
return userResp
}
func userCreateToDomain(user *management.CreateUserRequest) *domain.User {
var human *domain.Human
var machine *domain.Machine
func userHumanFromDomain(human *domain.Human) *management.UserResponse {
changeDate, err := ptypes.TimestampProto(human.ChangeDate)
logging.Log("GRPC-ckoe3d").OnError(err).Debug("unable to parse timestamp")
userResp := &management.UserResponse{
Id: human.AggregateID,
State: userStateFromDomain(human.GetState()),
ChangeDate: changeDate,
Sequence: human.Sequence,
UserName: human.GetUsername(),
}
userResp.User = &management.UserResponse_Human{Human: humanFromDomain(human)}
return userResp
}
func userCreateToDomain(user *management.CreateUserRequest) (*domain.Human, *domain.Machine) {
if h := user.GetHuman(); h != nil {
human = humanCreateToDomain(h)
human := humanCreateToDomain(h)
human.Username = user.UserName
return human, nil
}
if m := user.GetMachine(); m != nil {
machine = machineCreateToDomain(m)
}
return &domain.User{
UserName: user.UserName,
Human: human,
Machine: machine,
machine := machineCreateToDomain(m)
machine.Username = user.UserName
return nil, machine
}
return nil, nil
}
func passwordRequestToModel(r *management.PasswordRequest) *usr_model.Password {
@@ -212,15 +212,11 @@ func userMembershipSearchKeyToModel(key management.UserMembershipSearchKey) usr_
}
func profileFromDomain(profile *domain.Profile) *management.UserProfile {
creationDate, err := ptypes.TimestampProto(profile.CreationDate)
logging.Log("GRPC-dkso3").OnError(err).Debug("unable to parse timestamp")
changeDate, err := ptypes.TimestampProto(profile.ChangeDate)
logging.Log("GRPC-ski8d").OnError(err).Debug("unable to parse timestamp")
return &management.UserProfile{
Id: profile.AggregateID,
CreationDate: creationDate,
ChangeDate: changeDate,
Sequence: profile.Sequence,
FirstName: profile.FirstName,
@@ -270,15 +266,11 @@ func updateProfileToDomain(u *management.UpdateUserProfileRequest) *domain.Profi
}
func emailFromDomain(email *domain.Email) *management.UserEmail {
creationDate, err := ptypes.TimestampProto(email.CreationDate)
logging.Log("GRPC-d9ow2").OnError(err).Debug("unable to parse timestamp")
changeDate, err := ptypes.TimestampProto(email.ChangeDate)
logging.Log("GRPC-s0dkw").OnError(err).Debug("unable to parse timestamp")
return &management.UserEmail{
Id: email.AggregateID,
CreationDate: creationDate,
ChangeDate: changeDate,
Sequence: email.Sequence,
Email: email.EmailAddress,
@@ -312,15 +304,11 @@ func updateEmailToDomain(e *management.UpdateUserEmailRequest) *domain.Email {
}
func phoneFromDomain(phone *domain.Phone) *management.UserPhone {
creationDate, err := ptypes.TimestampProto(phone.CreationDate)
logging.Log("GRPC-ps9ws").OnError(err).Debug("unable to parse timestamp")
changeDate, err := ptypes.TimestampProto(phone.ChangeDate)
logging.Log("GRPC-09ewq").OnError(err).Debug("unable to parse timestamp")
return &management.UserPhone{
Id: phone.AggregateID,
CreationDate: creationDate,
ChangeDate: changeDate,
Sequence: phone.Sequence,
Phone: phone.PhoneNumber,
@@ -353,15 +341,11 @@ func updatePhoneToDomain(e *management.UpdateUserPhoneRequest) *domain.Phone {
}
func addressFromDomain(address *domain.Address) *management.UserAddress {
creationDate, err := ptypes.TimestampProto(address.CreationDate)
logging.Log("GRPC-ud8w7").OnError(err).Debug("unable to parse timestamp")
changeDate, err := ptypes.TimestampProto(address.ChangeDate)
logging.Log("GRPC-si9ws").OnError(err).Debug("unable to parse timestamp")
return &management.UserAddress{
Id: address.AggregateID,
CreationDate: creationDate,
ChangeDate: changeDate,
Sequence: address.Sequence,
Country: address.Country,