mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 08:07:32 +00:00
User view (#94)
* fix: some funcitons * feat(eventstore): implemented push events * fix: move project eventstore to project package * fix: change project eventstore funcs * feat(eventstore): overwrite context data * fix: change project eventstore * fix: add project repo to mgmt server * feat(types): SQL-config * fix: commented code * feat(eventstore): options to overwrite editor * feat: auth interceptor and cockroach migrations * fix: migrations * fix: fix filter * fix: not found on getbyid * fix: use global sql config * fix: add sequence * fix: add some tests * fix(eventstore): nullable sequence * fix: add some tests * merge * fix: add some tests * fix(migrations): correct statements for sequence * fix: add some tests * fix: add some tests * fix: changes from mr * fix: changes from mr * fix: add some tests * Update internal/eventstore/models/field.go Co-Authored-By: livio-a <livio.a@gmail.com> * fix(eventstore): code quality * fix: add types to aggregate/Event-types * fix: try tests * fix(eventstore): rename modifier* to editor* * fix(eventstore): delete editor_org * fix(migrations): remove editor_org field, rename modifier_* to editor_* * fix: query tests * fix: use prepare funcs * fix: go mod * fix: generate files * fix(eventstore): tests * fix(eventstore): rename modifier to editor * fix(migrations): add cluster migration, fix(migrations): fix typo of host in clean clsuter * fix(eventstore): move health * fix(eventstore): AggregateTypeFilter aggregateType as param * code quality * fix: go tests * feat: add member funcs * feat: add member model * feat: add member events * feat: add member repo model * fix: better error func testing * fix: project member funcs * fix: add tests * fix: add tests * feat: implement member requests * fix: merge master * fix: merge master * fix: read existing in project repo * fix: fix tests * feat: add internal cache * feat: add cache mock * fix: return values of cache mock * feat: add project role * fix: add cache config * fix: add role to eventstore * fix: use eventstore sdk * fix: use eventstore sdk * fix: add project role grpc requests * fix: fix getby id * fix: changes for mr * fix: change value to interface * feat: add app event creations * fix: searchmethods * Update internal/project/model/project_member.go Co-Authored-By: Silvan <silvan.reusser@gmail.com> * fix: use get project func * fix: append events * fix: check if value is string on equal ignore case * fix: add changes test * fix: add go mod * fix: add some tests * fix: return err not nil * fix: return err not nil * fix: add aggregate funcs and tests * fix: add oidc aggregate funcs and tests * fix: add oidc * fix: add some tests * fix: tests * fix: oidc validation * fix: generate client secret * fix: generate client id * fix: test change app * fix: deactivate/reactivate application * fix: change oidc config * fix: change oidc config secret * fix: implement grpc app funcs * fix: add application requests * fix: converter * fix: converter * fix: converter and generate clientid * fix: tests * feat: project grant aggregate * feat: project grant * fix: project grant check if role existing * fix: project grant requests * fix: project grant fixes * fix: project grant member model * fix: project grant member aggregate * fix: project grant member eventstore * fix: project grant member requests * feat: user model * feat: user command side * user command side * profile requests * local config with gopass and more * init for views (spooler, handler) * init for views (spooler, handler) * start view in management * granted project * Update internal/user/model/user.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/address.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/address.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/email.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/email.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/email.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/mfa.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/mfa.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/password.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/password.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/password.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/phone.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/phone.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/phone.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/user.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/user.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/user.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/usergrant/repository/eventsourcing/model/user_grant.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/usergrant/repository/eventsourcing/model/user_grant.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/usergrant/repository/eventsourcing/user_grant.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/user_test.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/eventstore_mock_test.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * changes from mr review * save files into basedir * changes from mr review * changes from mr review * implement granted project view * Update internal/usergrant/repository/eventsourcing/cache.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * Update internal/usergrant/repository/eventsourcing/cache.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * changes requested on mr * fix generate codes * fix return if no events * password code * search granted projects * fix search column * update all projects on project change * search roles * filter org * project members * project grant members * fix tests * application view * project grant search * mock * Update internal/user/repository/eventsourcing/model/password.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * Update internal/user/repository/eventsourcing/model/user.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * requests of mr * check email * test appendevents * test appendevents * Update internal/view/query.go Co-authored-by: Livio Amstutz <livio.a@gmail.com> * Update internal/eventstore/spooler/spooler.go Co-authored-by: Livio Amstutz <livio.a@gmail.com> * Update internal/view/query.go Co-authored-by: Livio Amstutz <livio.a@gmail.com> * merge request changes * Update internal/project/repository/view/model/application.go Co-authored-by: Livio Amstutz <livio.a@gmail.com> * merge request changes * Project view sql (#92) * sql and configs * error handling * sql start in eventstore * on error handling, config * merge branches * user view * user grant view * fix test * user grant search * fill data on user grant * update data on user grant * return caos errors * converter list len * merge master * Update internal/management/repository/eventsourcing/handler/user_grant.go Co-authored-by: Livio Amstutz <livio.a@gmail.com> * Update internal/usergrant/repository/view/model/user_grant_query.go Co-authored-by: Livio Amstutz <livio.a@gmail.com> * typo * Update internal/user/repository/view/model/user_query.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * Update internal/user/repository/view/user_view.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * Update pkg/management/api/grpc/user_converter.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * Update pkg/management/api/grpc/user_grant_converter.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * set my org query Co-authored-by: adlerhurst <silvan.reusser@gmail.com> Co-authored-by: livio-a <livio.a@gmail.com>
This commit is contained in:
182
internal/user/repository/view/model/user.go
Normal file
182
internal/user/repository/view/model/user.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/caos/logging"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/user/model"
|
||||
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
UserKeyUserID = "id"
|
||||
UserKeyUserName = "user_name"
|
||||
UserKeyFirstName = "first_name"
|
||||
UserKeyLastName = "last_name"
|
||||
UserKeyNickName = "nick_name"
|
||||
UserKeyDisplayName = "display_name"
|
||||
UserKeyEmail = "email"
|
||||
UserKeyState = "user_state"
|
||||
UserKeyResourceOwner = "resource_owner"
|
||||
)
|
||||
|
||||
type UserView struct {
|
||||
ID string `json:"-" gorm:"column:id;primary_key"`
|
||||
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
|
||||
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
|
||||
ResourceOwner string `json:"-" gorm:"column:resource_owner"`
|
||||
State int32 `json:"-" gorm:"column:user_state"`
|
||||
PasswordChanged time.Time `json:"-" gorm:"column:password_change"`
|
||||
LastLogin time.Time `json:"-" gorm:"column:last_login"`
|
||||
UserName string `json:"userName" gorm:"column:user_name"`
|
||||
FirstName string `json:"firstName" gorm:"column:first_name"`
|
||||
LastName string `json:"lastName" gorm:"column:last_name"`
|
||||
NickName string `json:"nickName" gorm:"column:nick_name"`
|
||||
DisplayName string `json:"displayName" gorm:"column:display_name"`
|
||||
PreferredLanguage string `json:"preferredLanguage" gorm:"column:preferred_language"`
|
||||
Gender int32 `json:"gender" gorm:"column:gender"`
|
||||
Email string `json:"email" gorm:"column:email"`
|
||||
IsEmailVerified bool `json:"-" gorm:"column:is_email_verified"`
|
||||
Phone string `json:"phone" gorm:"column:phone"`
|
||||
IsPhoneVerified bool `json:"-" gorm:"column:is_phone_verified"`
|
||||
Country string `json:"country" gorm:"column:country"`
|
||||
Locality string `json:"locality" gorm:"column:locality"`
|
||||
PostalCode string `json:"postalCode" gorm:"column:postal_code"`
|
||||
Region string `json:"region" gorm:"column:region"`
|
||||
StreetAddress string `json:"streetAddress" gorm:"column:street_address"`
|
||||
OTPState int32 `json:"-" gorm:"column:otp_state"`
|
||||
Sequence uint64 `json:"-" gorm:"column:sequence"`
|
||||
}
|
||||
|
||||
func UserFromModel(user *model.UserView) *UserView {
|
||||
return &UserView{
|
||||
ID: user.ID,
|
||||
ChangeDate: user.ChangeDate,
|
||||
CreationDate: user.CreationDate,
|
||||
ResourceOwner: user.ResourceOwner,
|
||||
State: int32(user.State),
|
||||
PasswordChanged: user.PasswordChanged,
|
||||
LastLogin: user.LastLogin,
|
||||
UserName: user.UserName,
|
||||
FirstName: user.FirstName,
|
||||
LastName: user.LastName,
|
||||
NickName: user.NickName,
|
||||
DisplayName: user.DisplayName,
|
||||
PreferredLanguage: user.PreferredLanguage,
|
||||
Gender: int32(user.Gender),
|
||||
Email: user.Email,
|
||||
IsEmailVerified: user.IsEmailVerified,
|
||||
Phone: user.Phone,
|
||||
IsPhoneVerified: user.IsPhoneVerified,
|
||||
Country: user.Country,
|
||||
Locality: user.Locality,
|
||||
PostalCode: user.PostalCode,
|
||||
Region: user.Region,
|
||||
StreetAddress: user.StreetAddress,
|
||||
OTPState: int32(user.OTPState),
|
||||
Sequence: user.Sequence,
|
||||
}
|
||||
}
|
||||
|
||||
func UserToModel(user *UserView) *model.UserView {
|
||||
return &model.UserView{
|
||||
ID: user.ID,
|
||||
ChangeDate: user.ChangeDate,
|
||||
CreationDate: user.CreationDate,
|
||||
ResourceOwner: user.ResourceOwner,
|
||||
State: model.UserState(user.State),
|
||||
PasswordChanged: user.PasswordChanged,
|
||||
LastLogin: user.LastLogin,
|
||||
UserName: user.UserName,
|
||||
FirstName: user.FirstName,
|
||||
LastName: user.LastName,
|
||||
NickName: user.NickName,
|
||||
DisplayName: user.DisplayName,
|
||||
PreferredLanguage: user.PreferredLanguage,
|
||||
Gender: model.Gender(user.Gender),
|
||||
Email: user.Email,
|
||||
IsEmailVerified: user.IsEmailVerified,
|
||||
Phone: user.Phone,
|
||||
IsPhoneVerified: user.IsPhoneVerified,
|
||||
Country: user.Country,
|
||||
Locality: user.Locality,
|
||||
PostalCode: user.PostalCode,
|
||||
Region: user.Region,
|
||||
StreetAddress: user.StreetAddress,
|
||||
OTPState: model.MfaState(user.OTPState),
|
||||
Sequence: user.Sequence,
|
||||
}
|
||||
}
|
||||
|
||||
func UsersToModel(users []*UserView) []*model.UserView {
|
||||
result := make([]*model.UserView, len(users))
|
||||
for i, p := range users {
|
||||
result[i] = UserToModel(p)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (p *UserView) AppendEvent(event *models.Event) (err error) {
|
||||
p.ChangeDate = event.CreationDate
|
||||
p.Sequence = event.Sequence
|
||||
switch event.Type {
|
||||
case es_model.UserAdded,
|
||||
es_model.UserRegistered:
|
||||
p.CreationDate = event.CreationDate
|
||||
p.setRootData(event)
|
||||
err = p.setData(event)
|
||||
case es_model.UserProfileChanged,
|
||||
es_model.UserAddressChanged:
|
||||
err = p.setData(event)
|
||||
case es_model.UserEmailChanged:
|
||||
p.IsEmailVerified = false
|
||||
err = p.setData(event)
|
||||
case es_model.UserEmailVerified:
|
||||
p.IsEmailVerified = true
|
||||
case es_model.UserPhoneChanged:
|
||||
p.IsPhoneVerified = false
|
||||
err = p.setData(event)
|
||||
case es_model.UserPhoneVerified:
|
||||
p.IsPhoneVerified = true
|
||||
case es_model.UserDeactivated:
|
||||
p.State = int32(model.USERSTATE_INACTIVE)
|
||||
case es_model.UserReactivated,
|
||||
es_model.UserUnlocked:
|
||||
p.State = int32(model.USERSTATE_ACTIVE)
|
||||
case es_model.UserLocked:
|
||||
p.State = int32(model.USERSTATE_LOCKED)
|
||||
case es_model.MfaOtpAdded:
|
||||
p.OTPState = int32(model.MFASTATE_NOTREADY)
|
||||
case es_model.MfaOtpVerified:
|
||||
p.OTPState = int32(model.MFASTATE_READY)
|
||||
case es_model.MfaOtpRemoved:
|
||||
p.OTPState = int32(model.MFASTATE_UNSPECIFIED)
|
||||
}
|
||||
p.ComputeObject()
|
||||
return err
|
||||
}
|
||||
|
||||
func (u *UserView) setRootData(event *models.Event) {
|
||||
u.ID = event.AggregateID
|
||||
u.ResourceOwner = event.ResourceOwner
|
||||
}
|
||||
|
||||
func (u *UserView) setData(event *models.Event) error {
|
||||
if err := json.Unmarshal(event.Data, u); err != nil {
|
||||
logging.Log("EVEN-lso9e").WithError(err).Error("could not unmarshal event data")
|
||||
return caos_errs.ThrowInternal(nil, "MODEL-8iows", "could not unmarshal data")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UserView) ComputeObject() {
|
||||
if u.State == int32(model.USERSTATE_UNSPECIFIED) || u.State == int32(model.USERSTATE_INITIAL) {
|
||||
if u.IsEmailVerified {
|
||||
u.State = int32(model.USERSTATE_ACTIVE)
|
||||
} else {
|
||||
u.State = int32(model.USERSTATE_INITIAL)
|
||||
}
|
||||
}
|
||||
}
|
75
internal/user/repository/view/model/user_query.go
Normal file
75
internal/user/repository/view/model/user_query.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
global_model "github.com/caos/zitadel/internal/model"
|
||||
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||
"github.com/caos/zitadel/internal/view"
|
||||
)
|
||||
|
||||
type UserSearchRequest usr_model.UserSearchRequest
|
||||
type UserSearchQuery usr_model.UserSearchQuery
|
||||
type UserSearchKey usr_model.UserSearchKey
|
||||
|
||||
func (req UserSearchRequest) GetLimit() uint64 {
|
||||
return req.Limit
|
||||
}
|
||||
|
||||
func (req UserSearchRequest) GetOffset() uint64 {
|
||||
return req.Offset
|
||||
}
|
||||
|
||||
func (req UserSearchRequest) GetSortingColumn() view.ColumnKey {
|
||||
if req.SortingColumn == usr_model.USERSEARCHKEY_UNSPECIFIED {
|
||||
return nil
|
||||
}
|
||||
return UserSearchKey(req.SortingColumn)
|
||||
}
|
||||
|
||||
func (req UserSearchRequest) GetAsc() bool {
|
||||
return req.Asc
|
||||
}
|
||||
|
||||
func (req UserSearchRequest) GetQueries() []view.SearchQuery {
|
||||
result := make([]view.SearchQuery, len(req.Queries))
|
||||
for i, q := range req.Queries {
|
||||
result[i] = UserSearchQuery{Key: q.Key, Value: q.Value, Method: q.Method}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (req UserSearchQuery) GetKey() view.ColumnKey {
|
||||
return UserSearchKey(req.Key)
|
||||
}
|
||||
|
||||
func (req UserSearchQuery) GetMethod() global_model.SearchMethod {
|
||||
return req.Method
|
||||
}
|
||||
|
||||
func (req UserSearchQuery) GetValue() interface{} {
|
||||
return req.Value
|
||||
}
|
||||
|
||||
func (key UserSearchKey) ToColumnName() string {
|
||||
switch usr_model.UserSearchKey(key) {
|
||||
case usr_model.USERSEARCHKEY_USER_ID:
|
||||
return UserKeyUserID
|
||||
case usr_model.USERSEARCHKEY_USER_NAME:
|
||||
return UserKeyUserName
|
||||
case usr_model.USERSEARCHKEY_FIRST_NAME:
|
||||
return UserKeyFirstName
|
||||
case usr_model.USERSEARCHKEY_LAST_NAME:
|
||||
return UserKeyLastName
|
||||
case usr_model.USERSEARCHKEY_DISPLAY_NAME:
|
||||
return UserKeyDisplayName
|
||||
case usr_model.USERSEARCHKEY_NICK_NAME:
|
||||
return UserKeyNickName
|
||||
case usr_model.USERSEARCHKEY_EMAIL:
|
||||
return UserKeyEmail
|
||||
case usr_model.USERSEARCHKEY_STATE:
|
||||
return UserKeyState
|
||||
case usr_model.USERSEARCHKEY_RESOURCEOWNER:
|
||||
return UserKeyResourceOwner
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
216
internal/user/repository/view/model/user_test.go
Normal file
216
internal/user/repository/view/model/user_test.go
Normal file
@@ -0,0 +1,216 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/user/model"
|
||||
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func mockUserData(user *es_model.User) []byte {
|
||||
data, _ := json.Marshal(user)
|
||||
return data
|
||||
}
|
||||
|
||||
func mockProfileData(profile *es_model.Profile) []byte {
|
||||
data, _ := json.Marshal(profile)
|
||||
return data
|
||||
}
|
||||
|
||||
func mockEmailData(email *es_model.Email) []byte {
|
||||
data, _ := json.Marshal(email)
|
||||
return data
|
||||
}
|
||||
|
||||
func mockPhoneData(phone *es_model.Phone) []byte {
|
||||
data, _ := json.Marshal(phone)
|
||||
return data
|
||||
}
|
||||
|
||||
func mockAddressData(address *es_model.Address) []byte {
|
||||
data, _ := json.Marshal(address)
|
||||
return data
|
||||
}
|
||||
|
||||
func getFullUser() *es_model.User {
|
||||
return &es_model.User{
|
||||
Profile: &es_model.Profile{
|
||||
UserName: "UserName",
|
||||
FirstName: "FirstName",
|
||||
LastName: "LastName",
|
||||
},
|
||||
Email: &es_model.Email{
|
||||
EmailAddress: "Email",
|
||||
},
|
||||
Phone: &es_model.Phone{
|
||||
PhoneNumber: "Phone",
|
||||
},
|
||||
Address: &es_model.Address{
|
||||
Country: "Country",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserAppendEvent(t *testing.T) {
|
||||
type args struct {
|
||||
event *es_models.Event
|
||||
user *UserView
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *UserView
|
||||
}{
|
||||
{
|
||||
name: "append added user event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.UserAdded, ResourceOwner: "OrgID", Data: mockUserData(getFullUser())},
|
||||
user: &UserView{},
|
||||
},
|
||||
result: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_INITIAL)},
|
||||
},
|
||||
{
|
||||
name: "append change user profile event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.UserProfileChanged, ResourceOwner: "OrgID", Data: mockProfileData(&es_model.Profile{FirstName: "FirstNameChanged"})},
|
||||
user: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_INITIAL)},
|
||||
},
|
||||
result: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstNameChanged", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_INITIAL)},
|
||||
},
|
||||
{
|
||||
name: "append change user email event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.UserEmailChanged, ResourceOwner: "OrgID", Data: mockEmailData(&es_model.Email{EmailAddress: "EmailChanged"})},
|
||||
user: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_ACTIVE)},
|
||||
},
|
||||
result: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "EmailChanged", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_ACTIVE)},
|
||||
},
|
||||
{
|
||||
name: "append verify user email event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.UserEmailVerified, ResourceOwner: "OrgID"},
|
||||
user: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_INITIAL)},
|
||||
},
|
||||
result: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_ACTIVE)},
|
||||
},
|
||||
{
|
||||
name: "append change user phone event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.UserPhoneChanged, ResourceOwner: "OrgID", Data: mockPhoneData(&es_model.Phone{PhoneNumber: "PhoneChanged"})},
|
||||
user: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_ACTIVE)},
|
||||
},
|
||||
result: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "PhoneChanged", Country: "Country", State: int32(model.USERSTATE_ACTIVE)},
|
||||
},
|
||||
{
|
||||
name: "append verify user phone event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.UserPhoneVerified, ResourceOwner: "OrgID"},
|
||||
user: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_ACTIVE)},
|
||||
},
|
||||
result: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", IsPhoneVerified: true, Country: "Country", State: int32(model.USERSTATE_ACTIVE)},
|
||||
},
|
||||
{
|
||||
name: "append change user address event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.UserAddressChanged, ResourceOwner: "OrgID", Data: mockAddressData(&es_model.Address{Country: "CountryChanged"})},
|
||||
user: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_ACTIVE)},
|
||||
},
|
||||
result: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "CountryChanged", State: int32(model.USERSTATE_ACTIVE)},
|
||||
},
|
||||
{
|
||||
name: "append user deactivate event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.UserDeactivated, ResourceOwner: "OrgID"},
|
||||
user: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_ACTIVE)},
|
||||
},
|
||||
result: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_INACTIVE)},
|
||||
},
|
||||
{
|
||||
name: "append user reactivate event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.UserReactivated, ResourceOwner: "OrgID"},
|
||||
user: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_INACTIVE)},
|
||||
},
|
||||
result: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_ACTIVE)},
|
||||
},
|
||||
{
|
||||
name: "append user lock event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.UserLocked, ResourceOwner: "OrgID"},
|
||||
user: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_ACTIVE)},
|
||||
},
|
||||
result: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_LOCKED)},
|
||||
},
|
||||
{
|
||||
name: "append user unlock event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.UserUnlocked, ResourceOwner: "OrgID"},
|
||||
user: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_LOCKED)},
|
||||
},
|
||||
result: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_ACTIVE)},
|
||||
},
|
||||
{
|
||||
name: "append user add otp event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.MfaOtpAdded, ResourceOwner: "OrgID"},
|
||||
user: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_ACTIVE)},
|
||||
},
|
||||
result: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_ACTIVE), OTPState: int32(model.MFASTATE_NOTREADY)},
|
||||
},
|
||||
{
|
||||
name: "append user verify otp event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.MfaOtpVerified, ResourceOwner: "OrgID"},
|
||||
user: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_ACTIVE), OTPState: int32(model.MFASTATE_NOTREADY)},
|
||||
},
|
||||
result: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_ACTIVE), OTPState: int32(model.MFASTATE_READY)},
|
||||
},
|
||||
{
|
||||
name: "append user remove otp event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.MfaOtpRemoved, ResourceOwner: "OrgID"},
|
||||
user: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_ACTIVE), OTPState: int32(model.MFASTATE_READY)},
|
||||
},
|
||||
result: &UserView{ID: "AggregateID", ResourceOwner: "OrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", State: int32(model.USERSTATE_ACTIVE), OTPState: int32(model.MFASTATE_UNSPECIFIED)},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.args.user.AppendEvent(tt.args.event)
|
||||
if tt.args.user.ID != tt.result.ID {
|
||||
t.Errorf("got wrong result ID: expected: %v, actual: %v ", tt.result.ID, tt.args.user.ID)
|
||||
}
|
||||
if tt.args.user.FirstName != tt.result.FirstName {
|
||||
t.Errorf("got wrong result FirstName: expected: %v, actual: %v ", tt.result.FirstName, tt.args.user.FirstName)
|
||||
}
|
||||
if tt.args.user.LastName != tt.result.LastName {
|
||||
t.Errorf("got wrong result FirstName: expected: %v, actual: %v ", tt.result.FirstName, tt.args.user.FirstName)
|
||||
}
|
||||
if tt.args.user.ResourceOwner != tt.result.ResourceOwner {
|
||||
t.Errorf("got wrong result ResourceOwner: expected: %v, actual: %v ", tt.result.ResourceOwner, tt.args.user.ResourceOwner)
|
||||
}
|
||||
if tt.args.user.Email != tt.result.Email {
|
||||
t.Errorf("got wrong result email: expected: %v, actual: %v ", tt.result.Email, tt.args.user.Email)
|
||||
}
|
||||
if tt.args.user.IsEmailVerified != tt.result.IsEmailVerified {
|
||||
t.Errorf("got wrong result IsEmailVerified: expected: %v, actual: %v ", tt.result.IsEmailVerified, tt.args.user.IsEmailVerified)
|
||||
}
|
||||
if tt.args.user.Phone != tt.result.Phone {
|
||||
t.Errorf("got wrong result Phone: expected: %v, actual: %v ", tt.result.Phone, tt.args.user.Phone)
|
||||
}
|
||||
if tt.args.user.IsPhoneVerified != tt.result.IsPhoneVerified {
|
||||
t.Errorf("got wrong result IsPhoneVerified: expected: %v, actual: %v ", tt.result.IsPhoneVerified, tt.args.user.IsPhoneVerified)
|
||||
}
|
||||
if tt.args.user.Country != tt.result.Country {
|
||||
t.Errorf("got wrong result Country: expected: %v, actual: %v ", tt.result.Country, tt.args.user.Country)
|
||||
}
|
||||
if tt.args.user.State != tt.result.State {
|
||||
t.Errorf("got wrong result state: expected: %v, actual: %v ", tt.result.State, tt.args.user.State)
|
||||
}
|
||||
if tt.args.user.OTPState != tt.result.OTPState {
|
||||
t.Errorf("got wrong result OTPState: expected: %v, actual: %v ", tt.result.OTPState, tt.args.user.OTPState)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
79
internal/user/repository/view/user_view.go
Normal file
79
internal/user/repository/view/user_view.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package view
|
||||
|
||||
import (
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||
"github.com/caos/zitadel/internal/user/repository/view/model"
|
||||
"github.com/caos/zitadel/internal/view"
|
||||
"github.com/jinzhu/gorm"
|
||||
)
|
||||
|
||||
func UserByID(db *gorm.DB, table, userID string) (*model.UserView, error) {
|
||||
user := new(model.UserView)
|
||||
query := view.PrepareGetByKey(table, model.UserSearchKey(usr_model.USERSEARCHKEY_USER_ID), userID)
|
||||
err := query(db, user)
|
||||
return user, err
|
||||
}
|
||||
|
||||
func UserByUserName(db *gorm.DB, table, userName string) (*model.UserView, error) {
|
||||
user := new(model.UserView)
|
||||
query := view.PrepareGetByKey(table, model.UserSearchKey(usr_model.USERSEARCHKEY_USER_NAME), userName)
|
||||
err := query(db, user)
|
||||
return user, err
|
||||
}
|
||||
|
||||
func SearchUsers(db *gorm.DB, table string, req *usr_model.UserSearchRequest) ([]*model.UserView, int, error) {
|
||||
users := make([]*model.UserView, 0)
|
||||
query := view.PrepareSearchQuery(table, model.UserSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries})
|
||||
count, err := query(db, &users)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return users, count, nil
|
||||
}
|
||||
|
||||
func GetGlobalUserByEmail(db *gorm.DB, table, email string) (*model.UserView, error) {
|
||||
user := new(model.UserView)
|
||||
query := view.PrepareGetByKey(table, model.UserSearchKey(usr_model.USERSEARCHKEY_EMAIL), email)
|
||||
err := query(db, user)
|
||||
return user, err
|
||||
}
|
||||
|
||||
func IsUserUnique(db *gorm.DB, table, userName, email string) (bool, error) {
|
||||
user := new(model.UserView)
|
||||
query := view.PrepareGetByKey(table, model.UserSearchKey(usr_model.USERSEARCHKEY_EMAIL), email)
|
||||
err := query(db, user)
|
||||
if err != nil && !caos_errs.IsNotFound(err) {
|
||||
return false, err
|
||||
}
|
||||
if user != nil {
|
||||
return false, nil
|
||||
}
|
||||
query = view.PrepareGetByKey(table, model.UserSearchKey(usr_model.USERSEARCHKEY_USER_NAME), email)
|
||||
err = query(db, user)
|
||||
if err != nil && !caos_errs.IsNotFound(err) {
|
||||
return false, err
|
||||
}
|
||||
return user == nil, nil
|
||||
}
|
||||
|
||||
func UserMfas(db *gorm.DB, table, userID string) ([]*usr_model.MultiFactor, error) {
|
||||
user, err := UserByID(db, table, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user.OTPState == int32(usr_model.MFASTATE_UNSPECIFIED) {
|
||||
return []*usr_model.MultiFactor{}, nil
|
||||
}
|
||||
return []*usr_model.MultiFactor{&usr_model.MultiFactor{Type: usr_model.MFATYPE_OTP, State: usr_model.MfaState(user.OTPState)}}, nil
|
||||
}
|
||||
|
||||
func PutUser(db *gorm.DB, table string, project *model.UserView) error {
|
||||
save := view.PrepareSave(table)
|
||||
return save(db, project)
|
||||
}
|
||||
|
||||
func DeleteUser(db *gorm.DB, table, userID string) error {
|
||||
delete := view.PrepareDeleteByKey(table, model.UserSearchKey(usr_model.USERSEARCHKEY_USER_ID), userID)
|
||||
return delete(db)
|
||||
}
|
Reference in New Issue
Block a user