mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 19:17:32 +00:00
fix: commandside queries (#1313)
* fix: move user by id to query side * fix: move get passwordless to query side # Conflicts: # internal/user/repository/eventsourcing/eventstore.go * fix: move get passwordless to query side * remove user eventstore * remove unused models * org changes * org changes * fix: move org queries to query side * fix: remove org eventstore * fix: remove org eventstore * fix: remove org eventstore * remove project from es v1 * project cleanup * project cleanup * fix: remove org eventstore * fix: remove iam eventstore Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
@@ -1,19 +1,5 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
)
|
||||
|
||||
type OTP struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
Secret *crypto.CryptoValue
|
||||
SecretString string
|
||||
Url string
|
||||
State MFAState
|
||||
}
|
||||
|
||||
type MFAState int32
|
||||
|
||||
const (
|
||||
|
@@ -1,103 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
"strings"
|
||||
|
||||
caos_errors "github.com/caos/zitadel/internal/errors"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/golang/protobuf/ptypes/timestamp"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
es_models.ObjectRoot
|
||||
State UserState
|
||||
UserName string
|
||||
|
||||
*Human
|
||||
*Machine
|
||||
}
|
||||
|
||||
type UserState int32
|
||||
|
||||
const (
|
||||
UserStateUnspecified UserState = iota
|
||||
UserStateActive
|
||||
UserStateInactive
|
||||
UserStateDeleted
|
||||
UserStateLocked
|
||||
UserStateSuspend
|
||||
UserStateInitial
|
||||
)
|
||||
|
||||
func (u *User) CheckOrgIAMPolicy(policy *iam_model.OrgIAMPolicyView) error {
|
||||
if policy == nil {
|
||||
return caos_errors.ThrowPreconditionFailed(nil, "MODEL-zSH7j", "Errors.Users.OrgIamPolicyNil")
|
||||
}
|
||||
if policy.UserLoginMustBeDomain && strings.Contains(u.UserName, "@") {
|
||||
return caos_errors.ThrowPreconditionFailed(nil, "MODEL-se4sJ", "Errors.User.EmailAsUsernameNotAllowed")
|
||||
}
|
||||
if !policy.UserLoginMustBeDomain && u.Profile != nil && u.UserName == "" && u.Email != nil {
|
||||
u.UserName = u.EmailAddress
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *User) SetNamesAsDisplayname() {
|
||||
if u.Profile != nil && u.DisplayName == "" && u.FirstName != "" && u.LastName != "" {
|
||||
u.DisplayName = u.FirstName + " " + u.LastName
|
||||
}
|
||||
}
|
||||
|
||||
type UserChanges struct {
|
||||
Changes []*UserChange
|
||||
LastSequence uint64
|
||||
}
|
||||
|
||||
type UserChange struct {
|
||||
ChangeDate *timestamp.Timestamp `json:"changeDate,omitempty"`
|
||||
EventType string `json:"eventType,omitempty"`
|
||||
Sequence uint64 `json:"sequence,omitempty"`
|
||||
ModifierID string `json:"modifierUser,omitempty"`
|
||||
ModifierName string `json:"-"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (u *User) IsActive() bool {
|
||||
return u.State == UserStateActive
|
||||
}
|
||||
|
||||
func (u *User) IsInitial() bool {
|
||||
return u.State == UserStateInitial
|
||||
}
|
||||
|
||||
func (u *User) IsInactive() bool {
|
||||
return u.State == UserStateInactive
|
||||
}
|
||||
|
||||
func (u *User) IsLocked() bool {
|
||||
return u.State == UserStateLocked
|
||||
}
|
||||
|
||||
func (u *User) IsValid() bool {
|
||||
if u.Human == nil && u.Machine == nil || u.UserName == "" {
|
||||
return false
|
||||
}
|
||||
if u.Human != nil {
|
||||
return u.Human.IsValid()
|
||||
}
|
||||
return u.Machine.IsValid()
|
||||
}
|
||||
|
||||
func (u *User) CheckOrgIamPolicy(policy *iam_model.OrgIAMPolicy) error {
|
||||
if policy == nil {
|
||||
return caos_errors.ThrowPreconditionFailed(nil, "MODEL-zSH7j", "Errors.Users.OrgIamPolicyNil")
|
||||
}
|
||||
if policy.UserLoginMustBeDomain && strings.Contains(u.UserName, "@") {
|
||||
return caos_errors.ThrowPreconditionFailed(nil, "MODEL-se4sJ", "Errors.User.EmailAsUsernameNotAllowed")
|
||||
}
|
||||
if !policy.UserLoginMustBeDomain && u.Profile != nil && u.UserName == "" && u.Email != nil {
|
||||
u.UserName = u.EmailAddress
|
||||
}
|
||||
return nil
|
||||
}
|
19
internal/user/model/user_changes.go
Normal file
19
internal/user/model/user_changes.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/ptypes/timestamp"
|
||||
)
|
||||
|
||||
type UserChanges struct {
|
||||
Changes []*UserChange
|
||||
LastSequence uint64
|
||||
}
|
||||
|
||||
type UserChange struct {
|
||||
ChangeDate *timestamp.Timestamp `json:"changeDate,omitempty"`
|
||||
EventType string `json:"eventType,omitempty"`
|
||||
Sequence uint64 `json:"sequence,omitempty"`
|
||||
ModifierID string `json:"modifierUser,omitempty"`
|
||||
ModifierName string `json:"-"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
@@ -1,203 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
caos_errors "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
)
|
||||
|
||||
type Human struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
*Password
|
||||
*Profile
|
||||
*Email
|
||||
*Phone
|
||||
*Address
|
||||
ExternalIDPs []*ExternalIDP
|
||||
InitCode *InitUserCode
|
||||
EmailCode *EmailCode
|
||||
PhoneCode *PhoneCode
|
||||
PasswordCode *PasswordCode
|
||||
OTP *OTP
|
||||
U2FTokens []*WebAuthNToken
|
||||
PasswordlessTokens []*WebAuthNToken
|
||||
U2FLogins []*WebAuthNLogin
|
||||
PasswordlessLogins []*WebAuthNLogin
|
||||
}
|
||||
|
||||
type InitUserCode struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
Code *crypto.CryptoValue
|
||||
Expiry time.Duration
|
||||
}
|
||||
|
||||
type Gender int32
|
||||
|
||||
const (
|
||||
GenderUnspecified Gender = iota
|
||||
GenderFemale
|
||||
GenderMale
|
||||
GenderDiverse
|
||||
)
|
||||
|
||||
func (u *Human) CheckOrgIAMPolicy(userName string, policy *domain.OrgIAMPolicy) error {
|
||||
if policy == nil {
|
||||
return caos_errors.ThrowPreconditionFailed(nil, "MODEL-zSH7j", "Errors.Users.OrgIamPolicyNil")
|
||||
}
|
||||
if policy.UserLoginMustBeDomain && strings.Contains(userName, "@") {
|
||||
return caos_errors.ThrowPreconditionFailed(nil, "MODEL-se4sJ", "Errors.User.EmailAsUsernameNotAllowed")
|
||||
}
|
||||
if !policy.UserLoginMustBeDomain && u.Profile != nil && userName == "" && u.Email != nil {
|
||||
userName = u.EmailAddress
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Human) SetNamesAsDisplayname() {
|
||||
if u.Profile != nil && u.DisplayName == "" && u.FirstName != "" && u.LastName != "" {
|
||||
u.DisplayName = u.FirstName + " " + u.LastName
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Human) IsValid() bool {
|
||||
return u.Profile != nil && u.FirstName != "" && u.LastName != "" && u.Email != nil && u.Email.IsValid() && u.Phone == nil || (u.Phone != nil && u.Phone.PhoneNumber != "" && u.Phone.IsValid())
|
||||
}
|
||||
|
||||
func (u *Human) IsInitialState() bool {
|
||||
return u.Email == nil || !u.IsEmailVerified || (u.ExternalIDPs == nil || len(u.ExternalIDPs) == 0) && (u.Password == nil || u.SecretString == "")
|
||||
}
|
||||
|
||||
func (u *Human) IsOTPReady() bool {
|
||||
return u.OTP != nil && u.OTP.State == MFAStateReady
|
||||
}
|
||||
|
||||
func (u *Human) HashPasswordIfExisting(policy *iam_model.PasswordComplexityPolicyView, passwordAlg crypto.HashAlgorithm, onetime bool) error {
|
||||
if u.Password != nil {
|
||||
return u.Password.HashPasswordIfExisting(policy, passwordAlg, onetime)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Human) GenerateInitCodeIfNeeded(initGenerator crypto.Generator) error {
|
||||
if !u.IsInitialState() {
|
||||
return nil
|
||||
}
|
||||
u.InitCode = new(InitUserCode)
|
||||
return u.InitCode.GenerateInitUserCode(initGenerator)
|
||||
}
|
||||
|
||||
func (u *Human) GeneratePhoneCodeIfNeeded(phoneGenerator crypto.Generator) error {
|
||||
if u.Phone == nil || u.IsPhoneVerified {
|
||||
return nil
|
||||
}
|
||||
u.PhoneCode = new(PhoneCode)
|
||||
return u.PhoneCode.GeneratePhoneCode(phoneGenerator)
|
||||
}
|
||||
|
||||
func (u *Human) GenerateEmailCodeIfNeeded(emailGenerator crypto.Generator) error {
|
||||
if u.Email == nil || u.IsEmailVerified {
|
||||
return nil
|
||||
}
|
||||
u.EmailCode = new(EmailCode)
|
||||
return u.EmailCode.GenerateEmailCode(emailGenerator)
|
||||
}
|
||||
|
||||
func (init *InitUserCode) GenerateInitUserCode(generator crypto.Generator) error {
|
||||
initCodeCrypto, _, err := crypto.NewCode(generator)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
init.Code = initCodeCrypto
|
||||
init.Expiry = generator.Expiry()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Human) GetExternalIDP(externalIDP *ExternalIDP) (int, *ExternalIDP) {
|
||||
for i, idp := range u.ExternalIDPs {
|
||||
if idp.UserID == externalIDP.UserID {
|
||||
return i, idp
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (u *Human) GetU2F(webAuthNTokenID string) (int, *WebAuthNToken) {
|
||||
for i, u2f := range u.U2FTokens {
|
||||
if u2f.WebAuthNTokenID == webAuthNTokenID {
|
||||
return i, u2f
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (u *Human) GetU2FByKeyID(keyID []byte) (int, *WebAuthNToken) {
|
||||
for i, u2f := range u.U2FTokens {
|
||||
if bytes.Compare(u2f.KeyID, keyID) == 0 {
|
||||
return i, u2f
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (u *Human) GetU2FToVerify() (int, *WebAuthNToken) {
|
||||
for i, u2f := range u.U2FTokens {
|
||||
if u2f.State == MFAStateNotReady {
|
||||
return i, u2f
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (u *Human) GetPasswordless(webAuthNTokenID string) (int, *WebAuthNToken) {
|
||||
for i, u2f := range u.PasswordlessTokens {
|
||||
if u2f.WebAuthNTokenID == webAuthNTokenID {
|
||||
return i, u2f
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (u *Human) GetPasswordlessByKeyID(keyID []byte) (int, *WebAuthNToken) {
|
||||
for i, pwl := range u.PasswordlessTokens {
|
||||
if bytes.Compare(pwl.KeyID, keyID) == 0 {
|
||||
return i, pwl
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (u *Human) GetPasswordlessToVerify() (int, *WebAuthNToken) {
|
||||
for i, u2f := range u.PasswordlessTokens {
|
||||
if u2f.State == MFAStateNotReady {
|
||||
return i, u2f
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (u *Human) GetU2FLogin(authReqID string) (int, *WebAuthNLogin) {
|
||||
for i, u2f := range u.U2FLogins {
|
||||
if u2f.AuthRequest.ID == authReqID {
|
||||
return i, u2f
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (u *Human) GetPasswordlessLogin(authReqID string) (int, *WebAuthNLogin) {
|
||||
for i, pw := range u.PasswordlessLogins {
|
||||
if pw.AuthRequest.ID == authReqID {
|
||||
return i, pw
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
@@ -1,105 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsUserValid(t *testing.T) {
|
||||
type args struct {
|
||||
user *Human
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result bool
|
||||
errFunc func(err error) bool
|
||||
}{
|
||||
{
|
||||
name: "user with minimal data",
|
||||
args: args{
|
||||
user: &Human{
|
||||
Profile: &Profile{
|
||||
FirstName: "FirstName",
|
||||
LastName: "LastName",
|
||||
},
|
||||
Email: &Email{
|
||||
EmailAddress: "Email",
|
||||
},
|
||||
},
|
||||
},
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
name: "user with phone data",
|
||||
args: args{
|
||||
user: &Human{
|
||||
Profile: &Profile{
|
||||
FirstName: "FirstName",
|
||||
LastName: "LastName",
|
||||
},
|
||||
Email: &Email{
|
||||
EmailAddress: "Email",
|
||||
},
|
||||
Phone: &Phone{
|
||||
PhoneNumber: "+41711234569",
|
||||
},
|
||||
},
|
||||
},
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
name: "user with address data",
|
||||
args: args{
|
||||
user: &Human{
|
||||
Profile: &Profile{
|
||||
FirstName: "FirstName",
|
||||
LastName: "LastName",
|
||||
},
|
||||
Email: &Email{
|
||||
EmailAddress: "Email",
|
||||
},
|
||||
Address: &Address{
|
||||
StreetAddress: "Teufenerstrasse 19",
|
||||
PostalCode: "9000",
|
||||
Locality: "St. Gallen",
|
||||
Country: "Switzerland",
|
||||
},
|
||||
},
|
||||
},
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
name: "user with all data",
|
||||
args: args{
|
||||
user: &Human{
|
||||
Profile: &Profile{
|
||||
FirstName: "FirstName",
|
||||
LastName: "LastName",
|
||||
},
|
||||
Email: &Email{
|
||||
EmailAddress: "Email",
|
||||
},
|
||||
Phone: &Phone{
|
||||
PhoneNumber: "+41711234569",
|
||||
},
|
||||
Address: &Address{
|
||||
StreetAddress: "Teufenerstrasse 19",
|
||||
PostalCode: "9000",
|
||||
Locality: "St. Gallen",
|
||||
Country: "Switzerland",
|
||||
},
|
||||
},
|
||||
},
|
||||
result: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
isValid := tt.args.user.IsValid()
|
||||
|
||||
if tt.result != isValid {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, isValid)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
key_model "github.com/caos/zitadel/internal/key/model"
|
||||
)
|
||||
|
||||
type Machine struct {
|
||||
models.ObjectRoot
|
||||
|
||||
Name string
|
||||
Description string
|
||||
}
|
||||
|
||||
func (sa *Machine) IsValid() bool {
|
||||
return sa.Name != ""
|
||||
}
|
||||
|
||||
type MachineKey struct {
|
||||
models.ObjectRoot
|
||||
|
||||
KeyID string
|
||||
Type key_model.AuthNKeyType
|
||||
ExpirationDate time.Time
|
||||
PrivateKey []byte
|
||||
}
|
@@ -108,6 +108,27 @@ type UserSearchResponse struct {
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
type UserState int32
|
||||
|
||||
const (
|
||||
UserStateUnspecified UserState = iota
|
||||
UserStateActive
|
||||
UserStateInactive
|
||||
UserStateDeleted
|
||||
UserStateLocked
|
||||
UserStateSuspend
|
||||
UserStateInitial
|
||||
)
|
||||
|
||||
type Gender int32
|
||||
|
||||
const (
|
||||
GenderUnspecified Gender = iota
|
||||
GenderFemale
|
||||
GenderMale
|
||||
GenderDiverse
|
||||
)
|
||||
|
||||
func (r *UserSearchRequest) EnsureLimit(limit uint64) {
|
||||
if r.Limit == 0 || r.Limit > limit {
|
||||
r.Limit = limit
|
||||
|
@@ -1,58 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
)
|
||||
|
||||
type WebAuthNToken struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
WebAuthNTokenID string
|
||||
CredentialCreationData []byte
|
||||
State MFAState
|
||||
Challenge string
|
||||
AllowedCredentialIDs [][]byte
|
||||
UserVerification UserVerificationRequirement
|
||||
KeyID []byte
|
||||
PublicKey []byte
|
||||
AttestationType string
|
||||
AAGUID []byte
|
||||
SignCount uint32
|
||||
WebAuthNTokenName string
|
||||
}
|
||||
|
||||
type WebAuthNLogin struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
CredentialAssertionData []byte
|
||||
Challenge string
|
||||
AllowedCredentialIDs [][]byte
|
||||
UserVerification UserVerificationRequirement
|
||||
*model.AuthRequest
|
||||
}
|
||||
|
||||
type WebAuthNMethod int32
|
||||
|
||||
const (
|
||||
WebAuthNMethodUnspecified WebAuthNMethod = iota
|
||||
WebAuthNMethodU2F
|
||||
WebAuthNMethodPasswordless
|
||||
)
|
||||
|
||||
type UserVerificationRequirement int32
|
||||
|
||||
const (
|
||||
UserVerificationRequirementUnspecified UserVerificationRequirement = iota
|
||||
UserVerificationRequirementRequired
|
||||
UserVerificationRequirementPreferred
|
||||
UserVerificationRequirementDiscouraged
|
||||
)
|
||||
|
||||
type AuthenticatorAttachment int32
|
||||
|
||||
const (
|
||||
AuthenticatorAttachmentUnspecified AuthenticatorAttachment = iota
|
||||
AuthenticatorAttachmentPlattform
|
||||
AuthenticatorAttachmentCrossPlattform
|
||||
)
|
Reference in New Issue
Block a user