mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 17:37:32 +00:00
chore: move the go code into a subfolder
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type Address struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
Country string `json:"country,omitempty"`
|
||||
Locality string `json:"locality,omitempty"`
|
||||
PostalCode string `json:"postalCode,omitempty"`
|
||||
Region string `json:"region,omitempty"`
|
||||
StreetAddress string `json:"streetAddress,omitempty"`
|
||||
}
|
||||
|
||||
func (a *Address) Changes(changed *Address) map[string]interface{} {
|
||||
changes := make(map[string]interface{}, 1)
|
||||
if a.Country != changed.Country {
|
||||
changes["country"] = changed.Country
|
||||
}
|
||||
if a.Locality != changed.Locality {
|
||||
changes["locality"] = changed.Locality
|
||||
}
|
||||
if a.PostalCode != changed.PostalCode {
|
||||
changes["postalCode"] = changed.PostalCode
|
||||
}
|
||||
if a.Region != changed.Region {
|
||||
changes["region"] = changed.Region
|
||||
}
|
||||
if a.StreetAddress != changed.StreetAddress {
|
||||
changes["streetAddress"] = changed.StreetAddress
|
||||
}
|
||||
return changes
|
||||
}
|
||||
|
||||
func (u *Human) appendUserAddressChangedEvent(event *es_models.Event) error {
|
||||
if u.Address == nil {
|
||||
u.Address = new(Address)
|
||||
}
|
||||
return u.Address.setData(event)
|
||||
}
|
||||
|
||||
func (a *Address) setData(event *es_models.Event) error {
|
||||
a.ObjectRoot.AppendEvent(event)
|
||||
if err := json.Unmarshal(event.Data, a); err != nil {
|
||||
logging.Log("EVEN-clos0").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-so92s", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
)
|
||||
|
||||
func TestAddressChanges(t *testing.T) {
|
||||
type args struct {
|
||||
existingAddress *Address
|
||||
newAddress *Address
|
||||
}
|
||||
type res struct {
|
||||
changesLen int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "all fields changed",
|
||||
args: args{
|
||||
existingAddress: &Address{Country: "Country", Locality: "Locality", PostalCode: "PostalCode", Region: "Region", StreetAddress: "StreetAddress"},
|
||||
newAddress: &Address{Country: "CountryChanged", Locality: "LocalityChanged", PostalCode: "PostalCodeChanged", Region: "RegionChanged", StreetAddress: "StreetAddressChanged"},
|
||||
},
|
||||
res: res{
|
||||
changesLen: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no fields changed",
|
||||
args: args{
|
||||
existingAddress: &Address{Country: "Country", Locality: "Locality", PostalCode: "PostalCode", Region: "Region", StreetAddress: "StreetAddress"},
|
||||
newAddress: &Address{Country: "Country", Locality: "Locality", PostalCode: "PostalCode", Region: "Region", StreetAddress: "StreetAddress"},
|
||||
},
|
||||
res: res{
|
||||
changesLen: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
changes := tt.args.existingAddress.Changes(tt.args.newAddress)
|
||||
if len(changes) != tt.res.changesLen {
|
||||
t.Errorf("got wrong changes len: expected: %v, actual: %v ", tt.res.changesLen, len(changes))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendUserAddressChangedEvent(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
address *Address
|
||||
event *es_models.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Human
|
||||
}{
|
||||
{
|
||||
name: "append user address event",
|
||||
args: args{
|
||||
user: &Human{Address: &Address{Locality: "Locality", Country: "Country"}},
|
||||
address: &Address{Locality: "LocalityChanged", PostalCode: "PostalCode"},
|
||||
event: &es_models.Event{},
|
||||
},
|
||||
result: &Human{Address: &Address{Locality: "LocalityChanged", Country: "Country", PostalCode: "PostalCode"}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.address != nil {
|
||||
data, _ := json.Marshal(tt.args.address)
|
||||
tt.args.event.Data = data
|
||||
}
|
||||
tt.args.user.appendUserAddressChangedEvent(tt.args.event)
|
||||
if tt.args.user.Address.Locality != tt.result.Address.Locality {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.user)
|
||||
}
|
||||
if tt.args.user.Address.Country != tt.result.Address.Country {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.user)
|
||||
}
|
||||
if tt.args.user.Address.PostalCode != tt.result.Address.PostalCode {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.user)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type AuthRequest struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
UserAgentID string `json:"userAgentID,omitempty"`
|
||||
SelectedIDPConfigID string `json:"selectedIDPConfigID,omitempty"`
|
||||
*BrowserInfo
|
||||
}
|
||||
|
||||
type BrowserInfo struct {
|
||||
UserAgent string `json:"userAgent,omitempty"`
|
||||
AcceptLanguage string `json:"acceptLanguage,omitempty"`
|
||||
RemoteIP net.IP `json:"remoteIP,omitempty"`
|
||||
}
|
||||
|
||||
func (a *AuthRequest) SetData(event eventstore.Event) error {
|
||||
if err := event.Unmarshal(a); err != nil {
|
||||
logging.Log("EVEN-T5df6").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-yGmhh", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type Email struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
EmailAddress string `json:"email,omitempty"`
|
||||
IsEmailVerified bool `json:"-"`
|
||||
}
|
||||
|
||||
type EmailCode struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
Code *crypto.CryptoValue `json:"code,omitempty"`
|
||||
Expiry time.Duration `json:"expiry,omitempty"`
|
||||
}
|
||||
|
||||
func (e *Email) Changes(changed *Email) map[string]interface{} {
|
||||
changes := make(map[string]interface{}, 1)
|
||||
if changed.EmailAddress != "" && e.EmailAddress != changed.EmailAddress {
|
||||
changes["email"] = changed.EmailAddress
|
||||
}
|
||||
return changes
|
||||
}
|
||||
|
||||
func (u *Human) appendUserEmailChangedEvent(event *es_models.Event) error {
|
||||
u.Email = new(Email)
|
||||
return u.Email.setData(event)
|
||||
}
|
||||
|
||||
func (u *Human) appendUserEmailCodeAddedEvent(event *es_models.Event) error {
|
||||
u.EmailCode = new(EmailCode)
|
||||
return u.EmailCode.SetData(event)
|
||||
}
|
||||
|
||||
func (u *Human) appendUserEmailVerifiedEvent() {
|
||||
u.IsEmailVerified = true
|
||||
}
|
||||
|
||||
func (a *Email) setData(event *es_models.Event) error {
|
||||
a.ObjectRoot.AppendEvent(event)
|
||||
if err := json.Unmarshal(event.Data, a); err != nil {
|
||||
logging.Log("EVEN-dlo9s").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-sl9xw", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *EmailCode) SetData(event *es_models.Event) error {
|
||||
a.ObjectRoot.AppendEvent(event)
|
||||
a.CreationDate = event.CreationDate
|
||||
if err := json.Unmarshal(event.Data, a); err != nil {
|
||||
logging.Log("EVEN-lo9s").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-s8uws", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -0,0 +1,153 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
)
|
||||
|
||||
func TestEmailChanges(t *testing.T) {
|
||||
type args struct {
|
||||
existingEmail *Email
|
||||
new *Email
|
||||
}
|
||||
type res struct {
|
||||
changesLen int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "all fields changed",
|
||||
args: args{
|
||||
existingEmail: &Email{EmailAddress: "Email", IsEmailVerified: true},
|
||||
new: &Email{EmailAddress: "EmailChanged", IsEmailVerified: false},
|
||||
},
|
||||
res: res{
|
||||
changesLen: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no fields changed",
|
||||
args: args{
|
||||
existingEmail: &Email{EmailAddress: "Email", IsEmailVerified: true},
|
||||
new: &Email{EmailAddress: "Email", IsEmailVerified: false},
|
||||
},
|
||||
res: res{
|
||||
changesLen: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
changes := tt.args.existingEmail.Changes(tt.args.new)
|
||||
if len(changes) != tt.res.changesLen {
|
||||
t.Errorf("got wrong changes len: expected: %v, actual: %v ", tt.res.changesLen, len(changes))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendUserEmailChangedEvent(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
email *Email
|
||||
event *es_models.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Human
|
||||
}{
|
||||
{
|
||||
name: "append user email event",
|
||||
args: args{
|
||||
user: &Human{Email: &Email{EmailAddress: "EmailAddress"}},
|
||||
email: &Email{EmailAddress: "EmailAddressChanged"},
|
||||
event: &es_models.Event{},
|
||||
},
|
||||
result: &Human{Email: &Email{EmailAddress: "EmailAddressChanged"}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.email != nil {
|
||||
data, _ := json.Marshal(tt.args.email)
|
||||
tt.args.event.Data = data
|
||||
}
|
||||
tt.args.user.appendUserEmailChangedEvent(tt.args.event)
|
||||
if tt.args.user.Email.EmailAddress != tt.result.Email.EmailAddress {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.user)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendUserEmailCodeAddedEvent(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
code *EmailCode
|
||||
event *es_models.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Human
|
||||
}{
|
||||
{
|
||||
name: "append user email code added event",
|
||||
args: args{
|
||||
user: &Human{Email: &Email{EmailAddress: "EmailAddress"}},
|
||||
code: &EmailCode{Expiry: time.Hour * 1},
|
||||
event: &es_models.Event{},
|
||||
},
|
||||
result: &Human{EmailCode: &EmailCode{Expiry: time.Hour * 1}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.code != nil {
|
||||
data, _ := json.Marshal(tt.args.code)
|
||||
tt.args.event.Data = data
|
||||
}
|
||||
tt.args.user.appendUserEmailCodeAddedEvent(tt.args.event)
|
||||
if tt.args.user.EmailCode.Expiry != tt.result.EmailCode.Expiry {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.user)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendUserEmailVerifiedEvent(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
event *es_models.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Human
|
||||
}{
|
||||
{
|
||||
name: "append user email event",
|
||||
args: args{
|
||||
user: &Human{Email: &Email{EmailAddress: "EmailAddress"}},
|
||||
event: &es_models.Event{},
|
||||
},
|
||||
result: &Human{Email: &Email{EmailAddress: "EmailAddress", IsEmailVerified: true}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
tt.args.user.appendUserEmailVerifiedEvent()
|
||||
if tt.args.user.Email.IsEmailVerified != tt.result.Email.IsEmailVerified {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.user)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type ExternalIDP struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
IDPConfigID string `json:"idpConfigId,omitempty"`
|
||||
UserID string `json:"userId,omitempty"`
|
||||
DisplayName string `json:"displayName,omitempty"`
|
||||
}
|
||||
|
||||
func GetExternalIDP(idps []*ExternalIDP, id string) (int, *ExternalIDP) {
|
||||
for i, idp := range idps {
|
||||
if idp.UserID == id {
|
||||
return i, idp
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
func (u *Human) appendExternalIDPAddedEvent(event *es_models.Event) error {
|
||||
idp := new(ExternalIDP)
|
||||
err := idp.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
idp.ObjectRoot.CreationDate = event.CreationDate
|
||||
u.ExternalIDPs = append(u.ExternalIDPs, idp)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Human) appendExternalIDPRemovedEvent(event *es_models.Event) error {
|
||||
idp := new(ExternalIDP)
|
||||
err := idp.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if i, externalIdp := GetExternalIDP(u.ExternalIDPs, idp.UserID); externalIdp != nil {
|
||||
u.ExternalIDPs[i] = u.ExternalIDPs[len(u.ExternalIDPs)-1]
|
||||
u.ExternalIDPs[len(u.ExternalIDPs)-1] = nil
|
||||
u.ExternalIDPs = u.ExternalIDPs[:len(u.ExternalIDPs)-1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pw *ExternalIDP) setData(event *es_models.Event) error {
|
||||
pw.ObjectRoot.AppendEvent(event)
|
||||
if err := json.Unmarshal(event.Data, pw); err != nil {
|
||||
logging.Log("EVEN-Msi9d").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-A9osf", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -0,0 +1,90 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
)
|
||||
|
||||
func TestAppendExternalIDPAddedEvent(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
externalIDP *ExternalIDP
|
||||
event *es_models.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Human
|
||||
}{
|
||||
{
|
||||
name: "append external idp added event",
|
||||
args: args{
|
||||
user: &Human{},
|
||||
externalIDP: &ExternalIDP{IDPConfigID: "IDPConfigID", UserID: "UserID", DisplayName: "DisplayName"},
|
||||
event: &es_models.Event{},
|
||||
},
|
||||
result: &Human{ExternalIDPs: []*ExternalIDP{{IDPConfigID: "IDPConfigID", UserID: "UserID", DisplayName: "DisplayName"}}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.externalIDP != nil {
|
||||
data, _ := json.Marshal(tt.args.externalIDP)
|
||||
tt.args.event.Data = data
|
||||
}
|
||||
tt.args.user.appendExternalIDPAddedEvent(tt.args.event)
|
||||
if len(tt.args.user.ExternalIDPs) == 0 {
|
||||
t.Error("got wrong result expected external idps on user ")
|
||||
}
|
||||
if tt.args.user.ExternalIDPs[0].UserID != tt.result.ExternalIDPs[0].UserID {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.ExternalIDPs[0].UserID, tt.args.user.ExternalIDPs[0].UserID)
|
||||
}
|
||||
if tt.args.user.ExternalIDPs[0].IDPConfigID != tt.result.ExternalIDPs[0].IDPConfigID {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.ExternalIDPs[0].IDPConfigID, tt.args.user.ExternalIDPs[0].IDPConfigID)
|
||||
}
|
||||
if tt.args.user.ExternalIDPs[0].DisplayName != tt.result.ExternalIDPs[0].DisplayName {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.ExternalIDPs[0].DisplayName, tt.args.user.ExternalIDPs[0].IDPConfigID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendExternalIDPRemovedEvent(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
externalIDP *ExternalIDP
|
||||
event *es_models.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Human
|
||||
}{
|
||||
{
|
||||
name: "append external idp removed event",
|
||||
args: args{
|
||||
user: &Human{
|
||||
ExternalIDPs: []*ExternalIDP{
|
||||
{IDPConfigID: "IDPConfigID", UserID: "UserID", DisplayName: "DisplayName"},
|
||||
}},
|
||||
externalIDP: &ExternalIDP{UserID: "UserID"},
|
||||
event: &es_models.Event{},
|
||||
},
|
||||
result: &Human{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.externalIDP != nil {
|
||||
data, _ := json.Marshal(tt.args.externalIDP)
|
||||
tt.args.event.Data = data
|
||||
}
|
||||
tt.args.user.appendExternalIDPRemovedEvent(tt.args.event)
|
||||
if len(tt.args.user.ExternalIDPs) != 0 {
|
||||
t.Error("got wrong result expected 0 external idps on user ")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
54
apps/api/internal/user/repository/eventsourcing/model/otp.go
Normal file
54
apps/api/internal/user/repository/eventsourcing/model/otp.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/user/model"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type OTP struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
Secret *crypto.CryptoValue `json:"otpSecret,omitempty"`
|
||||
State int32 `json:"-"`
|
||||
}
|
||||
|
||||
type OTPVerified struct {
|
||||
UserAgentID string `json:"userAgentID,omitempty"`
|
||||
}
|
||||
|
||||
func (u *Human) appendOTPAddedEvent(event eventstore.Event) error {
|
||||
u.OTP = &OTP{
|
||||
State: int32(model.MFAStateNotReady),
|
||||
}
|
||||
return u.OTP.setData(event)
|
||||
}
|
||||
|
||||
func (u *Human) appendOTPVerifiedEvent() {
|
||||
u.OTP.State = int32(model.MFAStateReady)
|
||||
}
|
||||
|
||||
func (u *Human) appendOTPRemovedEvent() {
|
||||
u.OTP = nil
|
||||
}
|
||||
|
||||
func (o *OTP) setData(event eventstore.Event) error {
|
||||
o.ObjectRoot.AppendEvent(event)
|
||||
if err := event.Unmarshal(o); err != nil {
|
||||
logging.Log("EVEN-d9soe").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-lo023", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *OTPVerified) SetData(event eventstore.Event) error {
|
||||
if err := event.Unmarshal(o); err != nil {
|
||||
logging.Log("EVEN-BF421").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-GB6hj", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -0,0 +1,110 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/user/model"
|
||||
)
|
||||
|
||||
func TestAppendMFAOTPAddedEvent(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
otp *OTP
|
||||
event *es_models.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Human
|
||||
}{
|
||||
{
|
||||
name: "append user otp event",
|
||||
args: args{
|
||||
user: &Human{},
|
||||
otp: &OTP{Secret: &crypto.CryptoValue{KeyID: "KeyID"}},
|
||||
event: &es_models.Event{},
|
||||
},
|
||||
result: &Human{OTP: &OTP{Secret: &crypto.CryptoValue{KeyID: "KeyID"}, State: int32(model.MFAStateNotReady)}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.otp != nil {
|
||||
data, _ := json.Marshal(tt.args.otp)
|
||||
tt.args.event.Data = data
|
||||
}
|
||||
tt.args.user.appendOTPAddedEvent(tt.args.event)
|
||||
if tt.args.user.OTP.State != tt.result.OTP.State {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.OTP.State, tt.args.user.OTP.State)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendMFAOTPVerifyEvent(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
otp *OTP
|
||||
event *es_models.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Human
|
||||
}{
|
||||
{
|
||||
name: "append otp verify event",
|
||||
args: args{
|
||||
user: &Human{OTP: &OTP{Secret: &crypto.CryptoValue{KeyID: "KeyID"}}},
|
||||
otp: &OTP{Secret: &crypto.CryptoValue{KeyID: "KeyID"}},
|
||||
event: &es_models.Event{},
|
||||
},
|
||||
result: &Human{OTP: &OTP{Secret: &crypto.CryptoValue{KeyID: "KeyID"}, State: int32(model.MFAStateReady)}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.otp != nil {
|
||||
data, _ := json.Marshal(tt.args.otp)
|
||||
tt.args.event.Data = data
|
||||
}
|
||||
tt.args.user.appendOTPVerifiedEvent()
|
||||
if tt.args.user.OTP.State != tt.result.OTP.State {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.OTP.State, tt.args.user.OTP.State)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendMFAOTPRemoveEvent(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
otp *OTP
|
||||
event *es_models.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Human
|
||||
}{
|
||||
{
|
||||
name: "append otp verify event",
|
||||
args: args{
|
||||
user: &Human{OTP: &OTP{Secret: &crypto.CryptoValue{KeyID: "KeyID"}}},
|
||||
event: &es_models.Event{},
|
||||
},
|
||||
result: &Human{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.args.user.appendOTPRemovedEvent()
|
||||
if tt.args.user.OTP != nil {
|
||||
t.Errorf("got wrong result: actual: %v ", tt.result.OTP)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type Password struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
Secret *crypto.CryptoValue `json:"secret,omitempty"`
|
||||
EncodedHash string `json:"encodedHash,omitempty"`
|
||||
ChangeRequired bool `json:"changeRequired,omitempty"`
|
||||
}
|
||||
|
||||
type PasswordCode struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
Code *crypto.CryptoValue `json:"code,omitempty"`
|
||||
Expiry time.Duration `json:"expiry,omitempty"`
|
||||
NotificationType int32 `json:"notificationType,omitempty"`
|
||||
}
|
||||
|
||||
type PasswordChange struct {
|
||||
Password
|
||||
UserAgentID string `json:"userAgentID,omitempty"`
|
||||
}
|
||||
|
||||
func (u *Human) appendUserPasswordChangedEvent(event eventstore.Event) error {
|
||||
u.Password = new(Password)
|
||||
err := u.Password.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.Password.ObjectRoot.CreationDate = event.CreatedAt()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Human) appendPasswordSetRequestedEvent(event eventstore.Event) error {
|
||||
u.PasswordCode = new(PasswordCode)
|
||||
return u.PasswordCode.SetData(event)
|
||||
}
|
||||
|
||||
func (pw *Password) setData(event eventstore.Event) error {
|
||||
pw.ObjectRoot.AppendEvent(event)
|
||||
if err := event.Unmarshal(pw); err != nil {
|
||||
logging.Log("EVEN-dks93").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-sl9xlo2rsw", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *PasswordCode) SetData(event eventstore.Event) error {
|
||||
c.ObjectRoot.AppendEvent(event)
|
||||
c.CreationDate = event.CreatedAt()
|
||||
if err := event.Unmarshal(c); err != nil {
|
||||
logging.Log("EVEN-lo0y2").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-q21dr", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pw *PasswordChange) SetData(event eventstore.Event) error {
|
||||
if err := event.Unmarshal(pw); err != nil {
|
||||
logging.Log("EVEN-ADs31").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-BDd32", "could not unmarshal event")
|
||||
}
|
||||
pw.ObjectRoot.AppendEvent(event)
|
||||
return nil
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
)
|
||||
|
||||
func TestAppendUserPasswordChangedEvent(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
pw *Password
|
||||
event *es_models.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Human
|
||||
}{
|
||||
{
|
||||
name: "append init user code event",
|
||||
args: args{
|
||||
user: &Human{},
|
||||
pw: &Password{ChangeRequired: true},
|
||||
event: &es_models.Event{},
|
||||
},
|
||||
result: &Human{Password: &Password{ChangeRequired: true}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.pw != nil {
|
||||
data, _ := json.Marshal(tt.args.pw)
|
||||
tt.args.event.Data = data
|
||||
}
|
||||
tt.args.user.appendUserPasswordChangedEvent(tt.args.event)
|
||||
if tt.args.user.Password.ChangeRequired != tt.result.Password.ChangeRequired {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.user)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendPasswordSetRequestedEvent(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
code *PasswordCode
|
||||
event *es_models.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Human
|
||||
}{
|
||||
{
|
||||
name: "append user phone code added event",
|
||||
args: args{
|
||||
user: &Human{Phone: &Phone{PhoneNumber: "PhoneNumber"}},
|
||||
code: &PasswordCode{Expiry: time.Hour * 1},
|
||||
event: &es_models.Event{},
|
||||
},
|
||||
result: &Human{PasswordCode: &PasswordCode{Expiry: time.Hour * 1}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.code != nil {
|
||||
data, _ := json.Marshal(tt.args.code)
|
||||
tt.args.event.Data = data
|
||||
}
|
||||
tt.args.user.appendPasswordSetRequestedEvent(tt.args.event)
|
||||
if tt.args.user.PasswordCode.Expiry != tt.result.PasswordCode.Expiry {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.user)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type Phone struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
PhoneNumber string `json:"phone,omitempty"`
|
||||
IsPhoneVerified bool `json:"-"`
|
||||
}
|
||||
|
||||
type PhoneCode struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
Code *crypto.CryptoValue `json:"code,omitempty"`
|
||||
Expiry time.Duration `json:"expiry,omitempty"`
|
||||
}
|
||||
|
||||
func (p *Phone) Changes(changed *Phone) map[string]interface{} {
|
||||
changes := make(map[string]interface{}, 1)
|
||||
if changed.PhoneNumber != "" && p.PhoneNumber != changed.PhoneNumber {
|
||||
changes["phone"] = changed.PhoneNumber
|
||||
}
|
||||
return changes
|
||||
}
|
||||
|
||||
func (u *Human) appendUserPhoneChangedEvent(event *es_models.Event) error {
|
||||
u.Phone = new(Phone)
|
||||
return u.Phone.setData(event)
|
||||
}
|
||||
|
||||
func (u *Human) appendUserPhoneCodeAddedEvent(event *es_models.Event) error {
|
||||
u.PhoneCode = new(PhoneCode)
|
||||
return u.PhoneCode.SetData(event)
|
||||
}
|
||||
|
||||
func (u *Human) appendUserPhoneVerifiedEvent() {
|
||||
u.IsPhoneVerified = true
|
||||
}
|
||||
|
||||
func (u *Human) appendUserPhoneRemovedEvent() {
|
||||
u.Phone = nil
|
||||
u.PhoneCode = nil
|
||||
}
|
||||
|
||||
func (p *Phone) setData(event *es_models.Event) error {
|
||||
p.ObjectRoot.AppendEvent(event)
|
||||
if err := json.Unmarshal(event.Data, p); err != nil {
|
||||
logging.Log("EVEN-lco9s").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-lre56", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *PhoneCode) SetData(event *es_models.Event) error {
|
||||
c.ObjectRoot.AppendEvent(event)
|
||||
c.CreationDate = event.CreationDate
|
||||
if err := json.Unmarshal(event.Data, c); err != nil {
|
||||
logging.Log("EVEN-sk8ws").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-7hdj3", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -0,0 +1,153 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
)
|
||||
|
||||
func TestPhoneChanges(t *testing.T) {
|
||||
type args struct {
|
||||
existingPhone *Phone
|
||||
newPhone *Phone
|
||||
}
|
||||
type res struct {
|
||||
changesLen int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "all fields changed",
|
||||
args: args{
|
||||
existingPhone: &Phone{PhoneNumber: "Phone", IsPhoneVerified: true},
|
||||
newPhone: &Phone{PhoneNumber: "PhoneChanged", IsPhoneVerified: false},
|
||||
},
|
||||
res: res{
|
||||
changesLen: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no fields changed",
|
||||
args: args{
|
||||
existingPhone: &Phone{PhoneNumber: "Phone", IsPhoneVerified: true},
|
||||
newPhone: &Phone{PhoneNumber: "Phone", IsPhoneVerified: false},
|
||||
},
|
||||
res: res{
|
||||
changesLen: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
changes := tt.args.existingPhone.Changes(tt.args.newPhone)
|
||||
if len(changes) != tt.res.changesLen {
|
||||
t.Errorf("got wrong changes len: expected: %v, actual: %v ", tt.res.changesLen, len(changes))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendUserPhoneChangedEvent(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
phone *Phone
|
||||
event *es_models.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Human
|
||||
}{
|
||||
{
|
||||
name: "append user phone event",
|
||||
args: args{
|
||||
user: &Human{Phone: &Phone{PhoneNumber: "PhoneNumber"}},
|
||||
phone: &Phone{PhoneNumber: "PhoneNumberChanged"},
|
||||
event: &es_models.Event{},
|
||||
},
|
||||
result: &Human{Phone: &Phone{PhoneNumber: "PhoneNumberChanged"}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.phone != nil {
|
||||
data, _ := json.Marshal(tt.args.phone)
|
||||
tt.args.event.Data = data
|
||||
}
|
||||
tt.args.user.appendUserPhoneChangedEvent(tt.args.event)
|
||||
if tt.args.user.Phone.PhoneNumber != tt.result.Phone.PhoneNumber {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.user)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendUserPhoneCodeAddedEvent(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
code *PhoneCode
|
||||
event *es_models.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Human
|
||||
}{
|
||||
{
|
||||
name: "append user phone code added event",
|
||||
args: args{
|
||||
user: &Human{Phone: &Phone{PhoneNumber: "PhoneNumber"}},
|
||||
code: &PhoneCode{Expiry: time.Hour * 1},
|
||||
event: &es_models.Event{},
|
||||
},
|
||||
result: &Human{PhoneCode: &PhoneCode{Expiry: time.Hour * 1}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.code != nil {
|
||||
data, _ := json.Marshal(tt.args.code)
|
||||
tt.args.event.Data = data
|
||||
}
|
||||
tt.args.user.appendUserPhoneCodeAddedEvent(tt.args.event)
|
||||
if tt.args.user.PhoneCode.Expiry != tt.result.PhoneCode.Expiry {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.user)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendUserPhoneVerifiedEvent(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
event *es_models.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Human
|
||||
}{
|
||||
{
|
||||
name: "append user phone event",
|
||||
args: args{
|
||||
user: &Human{Phone: &Phone{PhoneNumber: "PhoneNumber"}},
|
||||
event: &es_models.Event{},
|
||||
},
|
||||
result: &Human{Phone: &Phone{PhoneNumber: "PhoneNumber", IsPhoneVerified: true}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
tt.args.user.appendUserPhoneVerifiedEvent()
|
||||
if tt.args.user.Phone.IsPhoneVerified != tt.result.Phone.IsPhoneVerified {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.user)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
)
|
||||
|
||||
type Profile struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
FirstName string `json:"firstName,omitempty"`
|
||||
LastName string `json:"lastName,omitempty"`
|
||||
NickName string `json:"nickName,omitempty"`
|
||||
DisplayName string `json:"displayName,omitempty"`
|
||||
PreferredLanguage LanguageTag `json:"preferredLanguage,omitempty"`
|
||||
Gender int32 `json:"gender,omitempty"`
|
||||
}
|
||||
|
||||
func (p *Profile) Changes(changed *Profile) map[string]interface{} {
|
||||
changes := make(map[string]interface{}, 1)
|
||||
if changed.FirstName != "" && p.FirstName != changed.FirstName {
|
||||
changes["firstName"] = changed.FirstName
|
||||
}
|
||||
if changed.LastName != "" && p.LastName != changed.LastName {
|
||||
changes["lastName"] = changed.LastName
|
||||
}
|
||||
if changed.NickName != p.NickName {
|
||||
changes["nickName"] = changed.NickName
|
||||
}
|
||||
if changed.DisplayName != "" && p.DisplayName != changed.DisplayName {
|
||||
changes["displayName"] = changed.DisplayName
|
||||
}
|
||||
if language.Tag(changed.PreferredLanguage) != language.Und && changed.PreferredLanguage != p.PreferredLanguage {
|
||||
changes["preferredLanguage"] = changed.PreferredLanguage
|
||||
}
|
||||
if changed.Gender != p.Gender {
|
||||
changes["gender"] = changed.Gender
|
||||
}
|
||||
return changes
|
||||
}
|
||||
|
||||
type LanguageTag language.Tag
|
||||
|
||||
func (t *LanguageTag) UnmarshalJSON(data []byte) error {
|
||||
var tag string
|
||||
err := json.Unmarshal(data, &tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*t = LanguageTag(language.Make(tag))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t LanguageTag) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(language.Tag(t))
|
||||
}
|
||||
|
||||
func (t *LanguageTag) MarshalBinary() ([]byte, error) {
|
||||
if t == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return []byte(language.Tag(*t).String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary modifies the receiver so it must take a pointer receiver.
|
||||
func (t *LanguageTag) UnmarshalBinary(data []byte) error {
|
||||
*t = LanguageTag(language.Make(string(data)))
|
||||
return nil
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
|
||||
user_model "github.com/zitadel/zitadel/internal/user/model"
|
||||
)
|
||||
|
||||
func TestProfileChanges(t *testing.T) {
|
||||
type args struct {
|
||||
existingProfile *Profile
|
||||
newProfile *Profile
|
||||
}
|
||||
type res struct {
|
||||
changesLen int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "all attributes changed",
|
||||
args: args{
|
||||
existingProfile: &Profile{FirstName: "FirstName", LastName: "LastName", NickName: "NickName", DisplayName: "DisplayName", PreferredLanguage: LanguageTag(language.German), Gender: int32(user_model.GenderFemale)},
|
||||
newProfile: &Profile{FirstName: "FirstNameChanged", LastName: "LastNameChanged", NickName: "NickNameChanged", DisplayName: "DisplayNameChanged", PreferredLanguage: LanguageTag(language.English), Gender: int32(user_model.GenderMale)},
|
||||
},
|
||||
res: res{
|
||||
changesLen: 6,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes",
|
||||
args: args{
|
||||
existingProfile: &Profile{FirstName: "FirstName", LastName: "LastName", NickName: "NickName", DisplayName: "DisplayName", PreferredLanguage: LanguageTag(language.German), Gender: int32(user_model.GenderFemale)},
|
||||
newProfile: &Profile{FirstName: "FirstName", LastName: "LastName", NickName: "NickName", DisplayName: "DisplayName", PreferredLanguage: LanguageTag(language.German), Gender: int32(user_model.GenderFemale)},
|
||||
},
|
||||
res: res{
|
||||
changesLen: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty names",
|
||||
args: args{
|
||||
existingProfile: &Profile{FirstName: "FirstName", LastName: "LastName", NickName: "NickName", DisplayName: "DisplayName", PreferredLanguage: LanguageTag(language.German), Gender: int32(user_model.GenderFemale)},
|
||||
newProfile: &Profile{FirstName: "", LastName: "", NickName: "NickName", DisplayName: "DisplayName", PreferredLanguage: LanguageTag(language.German), Gender: int32(user_model.GenderFemale)},
|
||||
},
|
||||
res: res{
|
||||
changesLen: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
changes := tt.args.existingProfile.Changes(tt.args.newProfile)
|
||||
if len(changes) != tt.res.changesLen {
|
||||
t.Errorf("got wrong changes len: expected: %v, actual: %v ", tt.res.changesLen, len(changes))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
user_repo "github.com/zitadel/zitadel/internal/repository/user"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
TokenID string `json:"tokenId" gorm:"column:token_id"`
|
||||
ApplicationID string `json:"applicationId" gorm:"column:application_id"`
|
||||
UserAgentID string `json:"userAgentId" gorm:"column:user_agent_id"`
|
||||
Audience database.TextArray[string] `json:"audience" gorm:"column:audience"`
|
||||
Scopes database.TextArray[string] `json:"scopes" gorm:"column:scopes"`
|
||||
Expiration time.Time `json:"expiration" gorm:"column:expiration"`
|
||||
PreferredLanguage string `json:"preferredLanguage" gorm:"column:preferred_language"`
|
||||
}
|
||||
|
||||
func (t *Token) AppendEvents(events ...*es_models.Event) error {
|
||||
for _, event := range events {
|
||||
if err := t.AppendEvent(event); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Token) AppendEvent(event *es_models.Event) error {
|
||||
if event.Typ == user_repo.UserTokenAddedType {
|
||||
err := t.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.CreationDate = event.CreationDate
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Token) setData(event *es_models.Event) error {
|
||||
t.ObjectRoot.AppendEvent(event)
|
||||
if err := json.Unmarshal(event.Data, t); err != nil {
|
||||
logging.Log("EVEN-4Fm9s").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-5Gms9", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
111
apps/api/internal/user/repository/eventsourcing/model/user.go
Normal file
111
apps/api/internal/user/repository/eventsourcing/model/user.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/repository/user"
|
||||
"github.com/zitadel/zitadel/internal/user/model"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
const (
|
||||
UserVersion = "v2"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
es_models.ObjectRoot
|
||||
State int32 `json:"-"`
|
||||
UserName string `json:"userName"`
|
||||
|
||||
*Human
|
||||
*Machine
|
||||
}
|
||||
|
||||
func (u *User) AppendEvents(events ...*es_models.Event) error {
|
||||
for _, event := range events {
|
||||
if err := u.AppendEvent(event); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *User) AppendEvent(event *es_models.Event) error {
|
||||
u.ObjectRoot.AppendEvent(event)
|
||||
|
||||
switch event.Type() {
|
||||
case user.UserV1AddedType,
|
||||
user.HumanAddedType,
|
||||
user.MachineAddedEventType,
|
||||
user.UserV1RegisteredType,
|
||||
user.HumanRegisteredType,
|
||||
user.UserV1ProfileChangedType,
|
||||
user.UserDomainClaimedType,
|
||||
user.UserUserNameChangedType:
|
||||
err := u.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case user.UserDeactivatedType:
|
||||
u.appendDeactivatedEvent()
|
||||
case user.UserReactivatedType:
|
||||
u.appendReactivatedEvent()
|
||||
case user.UserLockedType:
|
||||
u.appendLockedEvent()
|
||||
case user.UserUnlockedType:
|
||||
u.appendUnlockedEvent()
|
||||
case user.UserRemovedType:
|
||||
u.appendRemovedEvent()
|
||||
}
|
||||
|
||||
if u.Human != nil {
|
||||
u.Human.user = u
|
||||
return u.Human.AppendEvent(event)
|
||||
} else if u.Machine != nil {
|
||||
u.Machine.user = u
|
||||
return u.Machine.AppendEvent(event)
|
||||
}
|
||||
if strings.HasPrefix(string(event.Typ), "user.human") || event.AggregateVersion == "v1" {
|
||||
u.Human = &Human{user: u}
|
||||
return u.Human.AppendEvent(event)
|
||||
}
|
||||
if strings.HasPrefix(string(event.Typ), "user.machine") {
|
||||
u.Machine = &Machine{user: u}
|
||||
return u.Machine.AppendEvent(event)
|
||||
}
|
||||
|
||||
return zerrors.ThrowNotFound(nil, "MODEL-x9TaX", "Errors.UserType.Undefined")
|
||||
}
|
||||
|
||||
func (u *User) setData(event *es_models.Event) error {
|
||||
if err := json.Unmarshal(event.Data, u); err != nil {
|
||||
logging.Log("EVEN-ZDzQy").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-yGmhh", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *User) appendDeactivatedEvent() {
|
||||
u.State = int32(model.UserStateInactive)
|
||||
}
|
||||
|
||||
func (u *User) appendReactivatedEvent() {
|
||||
u.State = int32(model.UserStateActive)
|
||||
}
|
||||
|
||||
func (u *User) appendLockedEvent() {
|
||||
u.State = int32(model.UserStateLocked)
|
||||
}
|
||||
|
||||
func (u *User) appendUnlockedEvent() {
|
||||
u.State = int32(model.UserStateActive)
|
||||
}
|
||||
|
||||
func (u *User) appendRemovedEvent() {
|
||||
u.State = int32(model.UserStateDeleted)
|
||||
}
|
@@ -0,0 +1,185 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/repository/user"
|
||||
"github.com/zitadel/zitadel/internal/user/model"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type Human struct {
|
||||
user *User `json:"-"`
|
||||
|
||||
*Password
|
||||
*Profile
|
||||
*Email
|
||||
*Phone
|
||||
*Address
|
||||
ExternalIDPs []*ExternalIDP `json:"-"`
|
||||
InitCode *InitUserCode `json:"-"`
|
||||
EmailCode *EmailCode `json:"-"`
|
||||
PhoneCode *PhoneCode `json:"-"`
|
||||
PasswordCode *PasswordCode `json:"-"`
|
||||
OTP *OTP `json:"-"`
|
||||
U2FTokens []*WebAuthNToken `json:"-"`
|
||||
PasswordlessTokens []*WebAuthNToken `json:"-"`
|
||||
U2FLogins []*WebAuthNLogin `json:"-"`
|
||||
PasswordlessLogins []*WebAuthNLogin `json:"-"`
|
||||
}
|
||||
|
||||
type InitUserCode struct {
|
||||
es_models.ObjectRoot
|
||||
Code *crypto.CryptoValue `json:"code,omitempty"`
|
||||
Expiry time.Duration `json:"expiry,omitempty"`
|
||||
}
|
||||
|
||||
func (p *Human) AppendEvents(events ...*es_models.Event) error {
|
||||
for _, event := range events {
|
||||
if err := p.AppendEvent(event); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Human) AppendEvent(event *es_models.Event) (err error) {
|
||||
switch event.Type() {
|
||||
case user.UserV1AddedType,
|
||||
user.UserV1RegisteredType,
|
||||
user.UserV1ProfileChangedType,
|
||||
user.HumanAddedType,
|
||||
user.HumanRegisteredType,
|
||||
user.HumanProfileChangedType:
|
||||
err = h.setData(event)
|
||||
case user.UserV1InitialCodeAddedType,
|
||||
user.HumanInitialCodeAddedType:
|
||||
err = h.appendInitUsercodeCreatedEvent(event)
|
||||
case user.UserV1PasswordChangedType,
|
||||
user.HumanPasswordChangedType:
|
||||
err = h.appendUserPasswordChangedEvent(event)
|
||||
case user.UserV1PasswordCodeAddedType,
|
||||
user.HumanPasswordCodeAddedType:
|
||||
err = h.appendPasswordSetRequestedEvent(event)
|
||||
case user.UserV1EmailChangedType,
|
||||
user.HumanEmailChangedType:
|
||||
err = h.appendUserEmailChangedEvent(event)
|
||||
case user.UserV1EmailCodeAddedType,
|
||||
user.HumanEmailCodeAddedType:
|
||||
err = h.appendUserEmailCodeAddedEvent(event)
|
||||
case user.UserV1EmailVerifiedType,
|
||||
user.HumanEmailVerifiedType:
|
||||
h.appendUserEmailVerifiedEvent()
|
||||
case user.UserV1PhoneChangedType,
|
||||
user.HumanPhoneChangedType:
|
||||
err = h.appendUserPhoneChangedEvent(event)
|
||||
case user.UserV1PhoneCodeAddedType,
|
||||
user.HumanPhoneCodeAddedType:
|
||||
err = h.appendUserPhoneCodeAddedEvent(event)
|
||||
case user.UserV1PhoneVerifiedType,
|
||||
user.HumanPhoneVerifiedType:
|
||||
h.appendUserPhoneVerifiedEvent()
|
||||
case user.UserV1PhoneRemovedType,
|
||||
user.HumanPhoneRemovedType:
|
||||
h.appendUserPhoneRemovedEvent()
|
||||
case user.UserV1AddressChangedType,
|
||||
user.HumanAddressChangedType:
|
||||
err = h.appendUserAddressChangedEvent(event)
|
||||
case user.UserV1MFAOTPAddedType,
|
||||
user.HumanMFAOTPAddedType:
|
||||
err = h.appendOTPAddedEvent(event)
|
||||
case user.UserV1MFAOTPVerifiedType,
|
||||
user.HumanMFAOTPVerifiedType:
|
||||
h.appendOTPVerifiedEvent()
|
||||
case user.UserV1MFAOTPRemovedType,
|
||||
user.HumanMFAOTPRemovedType:
|
||||
h.appendOTPRemovedEvent()
|
||||
case user.UserIDPLinkAddedType:
|
||||
err = h.appendExternalIDPAddedEvent(event)
|
||||
case user.UserIDPLinkRemovedType, user.UserIDPLinkCascadeRemovedType:
|
||||
err = h.appendExternalIDPRemovedEvent(event)
|
||||
case user.HumanU2FTokenAddedType:
|
||||
err = h.appendU2FAddedEvent(event)
|
||||
case user.HumanU2FTokenVerifiedType:
|
||||
err = h.appendU2FVerifiedEvent(event)
|
||||
case user.HumanU2FTokenSignCountChangedType:
|
||||
err = h.appendU2FChangeSignCountEvent(event)
|
||||
case user.HumanU2FTokenRemovedType:
|
||||
err = h.appendU2FRemovedEvent(event)
|
||||
case user.HumanPasswordlessTokenAddedType:
|
||||
err = h.appendPasswordlessAddedEvent(event)
|
||||
case user.HumanPasswordlessTokenVerifiedType:
|
||||
err = h.appendPasswordlessVerifiedEvent(event)
|
||||
case user.HumanPasswordlessTokenSignCountChangedType:
|
||||
err = h.appendPasswordlessChangeSignCountEvent(event)
|
||||
case user.HumanPasswordlessTokenRemovedType:
|
||||
err = h.appendPasswordlessRemovedEvent(event)
|
||||
case user.HumanU2FTokenBeginLoginType:
|
||||
err = h.appendU2FLoginEvent(event)
|
||||
case user.HumanPasswordlessTokenBeginLoginType:
|
||||
err = h.appendPasswordlessLoginEvent(event)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.ComputeObject()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Human) ComputeObject() {
|
||||
if h.user.State == int32(model.UserStateUnspecified) || h.user.State == int32(model.UserStateInitial) {
|
||||
if h.Email != nil && h.IsEmailVerified {
|
||||
h.user.State = int32(model.UserStateActive)
|
||||
} else {
|
||||
h.user.State = int32(model.UserStateInitial)
|
||||
}
|
||||
}
|
||||
if h.Password != nil && h.Password.ObjectRoot.IsZero() {
|
||||
h.Password.ObjectRoot = h.user.ObjectRoot
|
||||
}
|
||||
if h.Profile != nil && h.Profile.ObjectRoot.IsZero() {
|
||||
h.Profile.ObjectRoot = h.user.ObjectRoot
|
||||
}
|
||||
if h.Email != nil && h.Email.ObjectRoot.IsZero() {
|
||||
h.Email.ObjectRoot = h.user.ObjectRoot
|
||||
}
|
||||
if h.Phone != nil && h.Phone.ObjectRoot.IsZero() {
|
||||
h.Phone.ObjectRoot = h.user.ObjectRoot
|
||||
}
|
||||
if h.Address != nil && h.Address.ObjectRoot.IsZero() {
|
||||
h.Address.ObjectRoot = h.user.ObjectRoot
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Human) setData(event *es_models.Event) error {
|
||||
if err := json.Unmarshal(event.Data, u); err != nil {
|
||||
logging.Log("EVEN-8ujgd").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-sj4jd", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Human) appendInitUsercodeCreatedEvent(event *es_models.Event) error {
|
||||
initCode := new(InitUserCode)
|
||||
err := initCode.SetData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
initCode.ObjectRoot.CreationDate = event.CreationDate
|
||||
u.InitCode = initCode
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *InitUserCode) SetData(event *es_models.Event) error {
|
||||
c.ObjectRoot.AppendEvent(event)
|
||||
if err := json.Unmarshal(event.Data, c); err != nil {
|
||||
logging.Log("EVEN-7duwe").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-lo34s", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
user_repo "github.com/zitadel/zitadel/internal/repository/user"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type Machine struct {
|
||||
user *User `json:"-"`
|
||||
|
||||
Name string `json:"name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
func (sa *Machine) AppendEvents(events ...*es_models.Event) error {
|
||||
for _, event := range events {
|
||||
if err := sa.AppendEvent(event); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sa *Machine) AppendEvent(event *es_models.Event) (err error) {
|
||||
switch event.Type() {
|
||||
case user_repo.MachineAddedEventType, user_repo.MachineChangedEventType:
|
||||
err = sa.setData(event)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (sa *Machine) setData(event *es_models.Event) error {
|
||||
if err := json.Unmarshal(event.Data, sa); err != nil {
|
||||
logging.Log("EVEN-8ujgd").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-GwjY9", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type MachineKey struct {
|
||||
es_models.ObjectRoot `json:"-"`
|
||||
KeyID string `json:"keyId,omitempty"`
|
||||
Type int32 `json:"type,omitempty"`
|
||||
ExpirationDate time.Time `json:"expirationDate,omitempty"`
|
||||
PublicKey []byte `json:"publicKey,omitempty"`
|
||||
privateKey []byte
|
||||
}
|
||||
|
||||
func (key *MachineKey) AppendEvents(events ...*es_models.Event) error {
|
||||
for _, event := range events {
|
||||
err := key.AppendEvent(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (key *MachineKey) AppendEvent(event *es_models.Event) (err error) {
|
||||
key.ObjectRoot.AppendEvent(event)
|
||||
switch event.Type() {
|
||||
case user_repo.MachineKeyAddedEventType:
|
||||
err = json.Unmarshal(event.Data, key)
|
||||
if err != nil {
|
||||
return zerrors.ThrowInternal(err, "MODEL-SjI4S", "Errors.Internal")
|
||||
}
|
||||
case user_repo.MachineKeyRemovedEventType:
|
||||
key.ExpirationDate = event.CreationDate
|
||||
}
|
||||
return err
|
||||
}
|
@@ -0,0 +1,257 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/user/model"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type WebAuthNToken struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
WebauthNTokenID string `json:"webAuthNTokenId"`
|
||||
Challenge string `json:"challenge"`
|
||||
State int32 `json:"-"`
|
||||
|
||||
KeyID []byte `json:"keyId"`
|
||||
PublicKey []byte `json:"publicKey"`
|
||||
AttestationType string `json:"attestationType"`
|
||||
AAGUID []byte `json:"aaguid"`
|
||||
SignCount uint32 `json:"signCount"`
|
||||
WebAuthNTokenName string `json:"webAuthNTokenName"`
|
||||
}
|
||||
|
||||
type WebAuthNVerify struct {
|
||||
WebAuthNTokenID string `json:"webAuthNTokenId"`
|
||||
KeyID []byte `json:"keyId"`
|
||||
PublicKey []byte `json:"publicKey"`
|
||||
AttestationType string `json:"attestationType"`
|
||||
AAGUID []byte `json:"aaguid"`
|
||||
SignCount uint32 `json:"signCount"`
|
||||
WebAuthNTokenName string `json:"webAuthNTokenName"`
|
||||
UserAgentID string `json:"userAgentID,omitempty"`
|
||||
}
|
||||
|
||||
type WebAuthNSignCount struct {
|
||||
WebauthNTokenID string `json:"webAuthNTokenId"`
|
||||
SignCount uint32 `json:"signCount"`
|
||||
}
|
||||
|
||||
type WebAuthNTokenID struct {
|
||||
WebauthNTokenID string `json:"webAuthNTokenId"`
|
||||
}
|
||||
|
||||
type WebAuthNLogin struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
WebauthNTokenID string `json:"webAuthNTokenId"`
|
||||
Challenge string `json:"challenge"`
|
||||
*AuthRequest
|
||||
}
|
||||
|
||||
func GetWebauthn(webauthnTokens []*WebAuthNToken, id string) (int, *WebAuthNToken) {
|
||||
for i, webauthn := range webauthnTokens {
|
||||
if webauthn.WebauthNTokenID == id {
|
||||
return i, webauthn
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (w *WebAuthNVerify) SetData(event eventstore.Event) error {
|
||||
if err := event.Unmarshal(w); err != nil {
|
||||
logging.Log("EVEN-G342rf").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-B6641", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Human) appendU2FAddedEvent(event eventstore.Event) error {
|
||||
webauthn := new(WebAuthNToken)
|
||||
err := webauthn.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
webauthn.ObjectRoot.CreationDate = event.CreatedAt()
|
||||
webauthn.State = int32(model.MFAStateNotReady)
|
||||
for i, token := range u.U2FTokens {
|
||||
if token.State == int32(model.MFAStateNotReady) {
|
||||
u.U2FTokens[i] = webauthn
|
||||
return nil
|
||||
}
|
||||
}
|
||||
u.U2FTokens = append(u.U2FTokens, webauthn)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Human) appendU2FVerifiedEvent(event eventstore.Event) error {
|
||||
webauthn := new(WebAuthNToken)
|
||||
err := webauthn.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, token := GetWebauthn(u.U2FTokens, webauthn.WebauthNTokenID); token != nil {
|
||||
err := token.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
token.State = int32(model.MFAStateReady)
|
||||
return nil
|
||||
}
|
||||
return zerrors.ThrowPreconditionFailed(nil, "MODEL-4hu9s", "Errors.Users.MFA.U2F.NotExisting")
|
||||
}
|
||||
|
||||
func (u *Human) appendU2FChangeSignCountEvent(event eventstore.Event) error {
|
||||
webauthn := new(WebAuthNToken)
|
||||
err := webauthn.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, token := GetWebauthn(u.U2FTokens, webauthn.WebauthNTokenID); token != nil {
|
||||
token.setData(event)
|
||||
return nil
|
||||
}
|
||||
return zerrors.ThrowPreconditionFailed(nil, "MODEL-5Ms8h", "Errors.Users.MFA.U2F.NotExisting")
|
||||
}
|
||||
|
||||
func (u *Human) appendU2FRemovedEvent(event eventstore.Event) error {
|
||||
webauthn := new(WebAuthNToken)
|
||||
err := webauthn.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := len(u.U2FTokens) - 1; i >= 0; i-- {
|
||||
if u.U2FTokens[i].WebauthNTokenID == webauthn.WebauthNTokenID {
|
||||
copy(u.U2FTokens[i:], u.U2FTokens[i+1:])
|
||||
u.U2FTokens[len(u.U2FTokens)-1] = nil
|
||||
u.U2FTokens = u.U2FTokens[:len(u.U2FTokens)-1]
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Human) appendPasswordlessAddedEvent(event eventstore.Event) error {
|
||||
webauthn := new(WebAuthNToken)
|
||||
err := webauthn.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
webauthn.ObjectRoot.CreationDate = event.CreatedAt()
|
||||
webauthn.State = int32(model.MFAStateNotReady)
|
||||
for i, token := range u.PasswordlessTokens {
|
||||
if token.State == int32(model.MFAStateNotReady) {
|
||||
u.PasswordlessTokens[i] = webauthn
|
||||
return nil
|
||||
}
|
||||
}
|
||||
u.PasswordlessTokens = append(u.PasswordlessTokens, webauthn)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Human) appendPasswordlessVerifiedEvent(event eventstore.Event) error {
|
||||
webauthn := new(WebAuthNToken)
|
||||
err := webauthn.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, token := GetWebauthn(u.PasswordlessTokens, webauthn.WebauthNTokenID); token != nil {
|
||||
err := token.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
token.State = int32(model.MFAStateReady)
|
||||
return nil
|
||||
}
|
||||
return zerrors.ThrowPreconditionFailed(nil, "MODEL-mKns8", "Errors.Users.MFA.Passwordless.NotExisting")
|
||||
}
|
||||
|
||||
func (u *Human) appendPasswordlessChangeSignCountEvent(event eventstore.Event) error {
|
||||
webauthn := new(WebAuthNToken)
|
||||
err := webauthn.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, token := GetWebauthn(u.PasswordlessTokens, webauthn.WebauthNTokenID); token != nil {
|
||||
err := token.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return zerrors.ThrowPreconditionFailed(nil, "MODEL-2Mv9s", "Errors.Users.MFA.Passwordless.NotExisting")
|
||||
}
|
||||
|
||||
func (u *Human) appendPasswordlessRemovedEvent(event eventstore.Event) error {
|
||||
webauthn := new(WebAuthNToken)
|
||||
err := webauthn.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := len(u.PasswordlessTokens) - 1; i >= 0; i-- {
|
||||
if u.PasswordlessTokens[i].WebauthNTokenID == webauthn.WebauthNTokenID {
|
||||
copy(u.PasswordlessTokens[i:], u.PasswordlessTokens[i+1:])
|
||||
u.PasswordlessTokens[len(u.PasswordlessTokens)-1] = nil
|
||||
u.PasswordlessTokens = u.PasswordlessTokens[:len(u.PasswordlessTokens)-1]
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WebAuthNToken) setData(event eventstore.Event) error {
|
||||
w.ObjectRoot.AppendEvent(event)
|
||||
if err := event.Unmarshal(w); err != nil {
|
||||
logging.Log("EVEN-4M9is").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-lo023", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Human) appendU2FLoginEvent(event eventstore.Event) error {
|
||||
webauthn := new(WebAuthNLogin)
|
||||
webauthn.ObjectRoot.AppendEvent(event)
|
||||
err := webauthn.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
webauthn.ObjectRoot.CreationDate = event.CreatedAt()
|
||||
for i, token := range u.U2FLogins {
|
||||
if token.AuthRequest.ID == webauthn.AuthRequest.ID {
|
||||
u.U2FLogins[i] = webauthn
|
||||
return nil
|
||||
}
|
||||
}
|
||||
u.U2FLogins = append(u.U2FLogins, webauthn)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Human) appendPasswordlessLoginEvent(event eventstore.Event) error {
|
||||
webauthn := new(WebAuthNLogin)
|
||||
webauthn.ObjectRoot.AppendEvent(event)
|
||||
err := webauthn.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
webauthn.ObjectRoot.CreationDate = event.CreatedAt()
|
||||
for i, token := range u.PasswordlessLogins {
|
||||
if token.AuthRequest.ID == webauthn.AuthRequest.ID {
|
||||
u.PasswordlessLogins[i] = webauthn
|
||||
return nil
|
||||
}
|
||||
}
|
||||
u.PasswordlessLogins = append(u.PasswordlessLogins, webauthn)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WebAuthNLogin) setData(event eventstore.Event) error {
|
||||
w.ObjectRoot.AppendEvent(event)
|
||||
if err := event.Unmarshal(w); err != nil {
|
||||
logging.Log("EVEN-hmSlo").WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-lo023", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -0,0 +1,151 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/user"
|
||||
)
|
||||
|
||||
func TestAppendMFAU2FAddedEvent(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
u2f *WebAuthNToken
|
||||
event *es_models.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Human
|
||||
}{
|
||||
{
|
||||
name: "append user u2f event",
|
||||
args: args{
|
||||
user: &Human{},
|
||||
u2f: &WebAuthNToken{WebauthNTokenID: "WebauthNTokenID", Challenge: "Challenge"},
|
||||
event: &es_models.Event{},
|
||||
},
|
||||
result: &Human{
|
||||
U2FTokens: []*WebAuthNToken{
|
||||
{WebauthNTokenID: "WebauthNTokenID", Challenge: "Challenge", State: int32(user.AuthFactorState_AUTH_FACTOR_STATE_NOT_READY)},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.u2f != nil {
|
||||
data, _ := json.Marshal(tt.args.u2f)
|
||||
tt.args.event.Data = data
|
||||
}
|
||||
tt.args.user.appendU2FAddedEvent(tt.args.event)
|
||||
if tt.args.user.U2FTokens[0].State != tt.result.U2FTokens[0].State {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.U2FTokens[0].State, tt.args.user.U2FTokens[0].State)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendMFAU2FVerifyEvent(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
u2f *WebAuthNVerify
|
||||
event *es_models.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Human
|
||||
}{
|
||||
{
|
||||
name: "append u2f verify event",
|
||||
args: args{
|
||||
user: &Human{
|
||||
U2FTokens: []*WebAuthNToken{
|
||||
{WebauthNTokenID: "WebauthNTokenID", Challenge: "Challenge", State: int32(user.AuthFactorState_AUTH_FACTOR_STATE_NOT_READY)},
|
||||
},
|
||||
},
|
||||
u2f: &WebAuthNVerify{WebAuthNTokenID: "WebauthNTokenID", KeyID: []byte("KeyID"), PublicKey: []byte("PublicKey"), AttestationType: "AttestationType", AAGUID: []byte("AAGUID"), SignCount: 1},
|
||||
event: &es_models.Event{},
|
||||
},
|
||||
result: &Human{
|
||||
U2FTokens: []*WebAuthNToken{
|
||||
{
|
||||
WebauthNTokenID: "WebauthNTokenID",
|
||||
Challenge: "Challenge",
|
||||
State: int32(user.AuthFactorState_AUTH_FACTOR_STATE_READY),
|
||||
KeyID: []byte("KeyID"),
|
||||
PublicKey: []byte("PublicKey"),
|
||||
AttestationType: "AttestationType",
|
||||
AAGUID: []byte("AAGUID"),
|
||||
SignCount: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.u2f != nil {
|
||||
data, _ := json.Marshal(tt.args.u2f)
|
||||
tt.args.event.Data = data
|
||||
}
|
||||
tt.args.user.appendU2FVerifiedEvent(tt.args.event)
|
||||
if tt.args.user.U2FTokens[0].State != tt.result.U2FTokens[0].State {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.U2FTokens[0].State, tt.args.user.U2FTokens[0].State)
|
||||
}
|
||||
if tt.args.user.U2FTokens[0].AttestationType != tt.result.U2FTokens[0].AttestationType {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.U2FTokens[0].AttestationType, tt.args.user.U2FTokens[0].AttestationType)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendMFAU2FRemoveEvent(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
u2f *WebAuthNTokenID
|
||||
event *es_models.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Human
|
||||
}{
|
||||
{
|
||||
name: "append u2f remove event",
|
||||
args: args{
|
||||
user: &Human{
|
||||
U2FTokens: []*WebAuthNToken{
|
||||
{
|
||||
WebauthNTokenID: "WebauthNTokenID",
|
||||
Challenge: "Challenge",
|
||||
State: int32(user.AuthFactorState_AUTH_FACTOR_STATE_NOT_READY),
|
||||
KeyID: []byte("KeyID"),
|
||||
PublicKey: []byte("PublicKey"),
|
||||
AttestationType: "AttestationType",
|
||||
AAGUID: []byte("AAGUID"),
|
||||
SignCount: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
u2f: &WebAuthNTokenID{WebauthNTokenID: "WebauthNTokenID"},
|
||||
event: &es_models.Event{},
|
||||
},
|
||||
result: &Human{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.u2f != nil {
|
||||
data, _ := json.Marshal(tt.args.u2f)
|
||||
tt.args.event.Data = data
|
||||
}
|
||||
tt.args.user.appendU2FRemovedEvent(tt.args.event)
|
||||
if len(tt.args.user.U2FTokens) != 0 {
|
||||
t.Errorf("got wrong result: actual: %v ", tt.result.U2FTokens)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user