mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 19:07:30 +00:00
feat: add auth command side (#107)
* 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 * feat: eventstore repository * fix: remove gorm * version * feat: pkg * feat: eventstore without eventstore-lib * rename files * gnueg * fix: global model * feat: add global view functions * feat(eventstore): sdk * fix(eventstore): rename app to eventstore * delete empty test * fix(models): delete unused struct * feat(eventstore): overwrite context data * fix: use global sql config * 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 * begin models * begin repo * 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 * begin repo * repo models and more * feat: user command side * lots of functions * user command side * profile requests * commit before rebase on user * save * local config with gopass and more * begin new auth command (user centric) * 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 * move to auth request * 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 * email verification step * more steps * lot of mfa * begin tests * more next steps * auth api * auth api (user) * auth api (user) * auth api (user) * differ requests * merge * tests * fix compilation error * mock for id generator * 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 * begin separation of command and query * otp * change packages * some cleanup and fixes * tests for auth request / next steps * add VerificationLifetimes to config and make it run * tests * fix code challenge validation * cleanup * fix merge * begin view * repackaging tests and configs * fix startup config for auth * add migration * add PromptSelectAccount * fix copy / paste * remove user_agent files * fixes * fix sequences in user_session * token commands * token queries and signout * fix * fix set password test * add token handler and table * handle session init * add session state * add user view test cases * change VerifyMyMfaOTP * some fixes * fix user repo in auth api * cleanup * add user session view test * fix merge * fixes * Update internal/auth/repository/eventsourcing/eventstore/auth_request.go Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> * Update internal/auth/repository/eventsourcing/eventstore/auth_request.go Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> * Update internal/auth/repository/eventsourcing/eventstore/auth_request.go Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> * Update internal/auth/repository/eventsourcing/eventstore/auth_request.go Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> * extract method usersForUserSelection * add todo for policy check * id on auth req * fix enum name Co-authored-by: Fabiennne <fabienne.gerschwiler@gmail.com> Co-authored-by: adlerhurst <silvan.reusser@gmail.com> Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
This commit is contained in:
@@ -50,6 +50,37 @@ Auth:
|
|||||||
GatewayPort: 50021
|
GatewayPort: 50021
|
||||||
CustomHeaders:
|
CustomHeaders:
|
||||||
- x-zitadel-
|
- x-zitadel-
|
||||||
|
Repository:
|
||||||
|
SearchLimit: 100
|
||||||
|
Eventstore:
|
||||||
|
ServiceName: 'authAPI'
|
||||||
|
Repository:
|
||||||
|
SQL:
|
||||||
|
Host: $ZITADEL_EVENTSTORE_HOST
|
||||||
|
Port: $ZITADEL_EVENTSTORE_PORT
|
||||||
|
User: 'auth'
|
||||||
|
Database: 'eventstore'
|
||||||
|
SSLmode: disable
|
||||||
|
Cache:
|
||||||
|
Type: 'fastcache'
|
||||||
|
Config:
|
||||||
|
MaxCacheSizeInByte: 10485760 #10mb
|
||||||
|
AuthRequest:
|
||||||
|
Host: $ZITADEL_EVENTSTORE_HOST
|
||||||
|
Port: $ZITADEL_EVENTSTORE_PORT
|
||||||
|
User: 'auth'
|
||||||
|
Database: 'auth'
|
||||||
|
SSLmode: disable
|
||||||
|
View:
|
||||||
|
Host: $ZITADEL_EVENTSTORE_HOST
|
||||||
|
Port: $ZITADEL_EVENTSTORE_PORT
|
||||||
|
User: 'auth'
|
||||||
|
Database: 'auth'
|
||||||
|
SSLmode: disable
|
||||||
|
Spooler:
|
||||||
|
ConcurrentTasks: 4
|
||||||
|
BulkLimit: 100
|
||||||
|
FailureCountUntilSkip: 5
|
||||||
|
|
||||||
Login:
|
Login:
|
||||||
# will get port range 5003x
|
# will get port range 5003x
|
||||||
|
@@ -42,6 +42,11 @@ SystemDefaults:
|
|||||||
Issuer: 'Zitadel'
|
Issuer: 'Zitadel'
|
||||||
VerificationKey:
|
VerificationKey:
|
||||||
EncryptionKeyID: $ZITADEL_OTP_VERIFICATION_KEY
|
EncryptionKeyID: $ZITADEL_OTP_VERIFICATION_KEY
|
||||||
|
VerificationLifetimes:
|
||||||
|
PasswordCheck: 240h #10d
|
||||||
|
MfaInitSkip: 720h #30d
|
||||||
|
MfaSoftwareCheck: 18h
|
||||||
|
MfaHardwareCheck: 12h
|
||||||
DefaultPolicies:
|
DefaultPolicies:
|
||||||
Age:
|
Age:
|
||||||
Description: Standard age policy
|
Description: Standard age policy
|
||||||
|
2
go.mod
2
go.mod
@@ -15,7 +15,7 @@ require (
|
|||||||
github.com/aws/aws-sdk-go v1.30.25 // indirect
|
github.com/aws/aws-sdk-go v1.30.25 // indirect
|
||||||
github.com/caos/logging v0.0.1
|
github.com/caos/logging v0.0.1
|
||||||
github.com/cockroachdb/cockroach-go v0.0.0-20200504194139-73ffeee90b62
|
github.com/cockroachdb/cockroach-go v0.0.0-20200504194139-73ffeee90b62
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.3.0
|
github.com/envoyproxy/protoc-gen-validate v0.1.0
|
||||||
github.com/ghodss/yaml v1.0.0
|
github.com/ghodss/yaml v1.0.0
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
|
||||||
github.com/golang/mock v1.4.3
|
github.com/golang/mock v1.4.3
|
||||||
|
2
go.sum
2
go.sum
@@ -84,8 +84,6 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
|
|||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.3.0 h1:Y2J74o+yAfcD8jpqtkLnUqRo+yshLr4eR1WPYGX0cic=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.3.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
|
||||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
|
25
internal/auth/auth/token_verifier.go
Normal file
25
internal/auth/auth/token_verifier.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/caos/zitadel/internal/api/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TokenVerifier struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func Start() (v *TokenVerifier) {
|
||||||
|
return new(TokenVerifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *TokenVerifier) VerifyAccessToken(ctx context.Context, token string) (string, string, string, error) {
|
||||||
|
return "", "", "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *TokenVerifier) ResolveGrants(ctx context.Context, userID, orgID string) ([]*auth.Grant, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *TokenVerifier) GetProjectIDByClientID(ctx context.Context, clientID string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
15
internal/auth/repository/auth_request.go
Normal file
15
internal/auth/repository/auth_request.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/auth_request/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthRequestRepository interface {
|
||||||
|
CreateAuthRequest(ctx context.Context, request *model.AuthRequest) (*model.AuthRequest, error)
|
||||||
|
AuthRequestByID(ctx context.Context, id string) (*model.AuthRequest, error)
|
||||||
|
CheckUsername(ctx context.Context, id, username string) error
|
||||||
|
VerifyPassword(ctx context.Context, id, userID, password string, info *model.BrowserInfo) error
|
||||||
|
VerifyMfaOTP(ctx context.Context, agentID, authRequestID string, code string, info *model.BrowserInfo) error
|
||||||
|
}
|
@@ -0,0 +1,239 @@
|
|||||||
|
package eventstore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
|
||||||
|
"github.com/caos/zitadel/internal/auth_request/model"
|
||||||
|
"github.com/caos/zitadel/internal/auth_request/repository/cache"
|
||||||
|
"github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/id"
|
||||||
|
user_model "github.com/caos/zitadel/internal/user/model"
|
||||||
|
user_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
||||||
|
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthRequestRepo struct {
|
||||||
|
UserEvents *user_event.UserEventstore
|
||||||
|
AuthRequests *cache.AuthRequestCache
|
||||||
|
View *view.View
|
||||||
|
|
||||||
|
UserSessionViewProvider userSessionViewProvider
|
||||||
|
UserViewProvider userViewProvider
|
||||||
|
|
||||||
|
IdGenerator id.Generator
|
||||||
|
|
||||||
|
PasswordCheckLifeTime time.Duration
|
||||||
|
MfaInitSkippedLifeTime time.Duration
|
||||||
|
MfaSoftwareCheckLifeTime time.Duration
|
||||||
|
MfaHardwareCheckLifeTime time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type userSessionViewProvider interface {
|
||||||
|
UserSessionByIDs(string, string) (*view_model.UserSessionView, error)
|
||||||
|
UserSessionsByAgentID(string) ([]*view_model.UserSessionView, error)
|
||||||
|
}
|
||||||
|
type userViewProvider interface {
|
||||||
|
UserByID(string) (*view_model.UserView, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *AuthRequestRepo) Health(ctx context.Context) error {
|
||||||
|
if err := repo.UserEvents.Health(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return repo.AuthRequests.Health(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *AuthRequestRepo) CreateAuthRequest(ctx context.Context, request *model.AuthRequest) (*model.AuthRequest, error) {
|
||||||
|
reqID, err := repo.IdGenerator.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
request.ID = reqID
|
||||||
|
err = repo.AuthRequests.SaveAuthRequest(ctx, request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return request, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *AuthRequestRepo) AuthRequestByID(ctx context.Context, id string) (*model.AuthRequest, error) {
|
||||||
|
request, err := repo.AuthRequests.GetAuthRequestByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
steps, err := repo.nextSteps(request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
request.PossibleSteps = steps
|
||||||
|
return request, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *AuthRequestRepo) CheckUsername(ctx context.Context, id, username string) error {
|
||||||
|
request, err := repo.AuthRequests.GetAuthRequestByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user, err := repo.View.UserByUsername(username)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
request.UserID = user.ID
|
||||||
|
return repo.AuthRequests.SaveAuthRequest(ctx, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *AuthRequestRepo) VerifyPassword(ctx context.Context, id, userID, password string, info *model.BrowserInfo) error {
|
||||||
|
request, err := repo.AuthRequests.GetAuthRequestByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if request.UserID == userID {
|
||||||
|
return errors.ThrowPreconditionFailed(nil, "EVENT-ds35D", "user id does not match request id ")
|
||||||
|
}
|
||||||
|
return repo.UserEvents.CheckPassword(ctx, userID, password, request.WithCurrentInfo(info))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *AuthRequestRepo) VerifyMfaOTP(ctx context.Context, authRequestID, userID string, code string, info *model.BrowserInfo) error {
|
||||||
|
request, err := repo.AuthRequests.GetAuthRequestByID(ctx, authRequestID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if request.UserID != userID {
|
||||||
|
return errors.ThrowPreconditionFailed(nil, "EVENT-ADJ26", "user id does not match request id")
|
||||||
|
}
|
||||||
|
return repo.UserEvents.CheckMfaOTP(ctx, userID, code, request.WithCurrentInfo(info))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *AuthRequestRepo) nextSteps(request *model.AuthRequest) ([]model.NextStep, error) {
|
||||||
|
if request == nil {
|
||||||
|
return nil, errors.ThrowInvalidArgument(nil, "EVENT-ds27a", "request must not be nil")
|
||||||
|
}
|
||||||
|
steps := make([]model.NextStep, 0)
|
||||||
|
if request.UserID == "" {
|
||||||
|
if request.Prompt != model.PromptNone {
|
||||||
|
steps = append(steps, &model.LoginStep{})
|
||||||
|
}
|
||||||
|
if request.Prompt == model.PromptSelectAccount {
|
||||||
|
users, err := repo.usersForUserSelection(request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
steps = append(steps, &model.SelectUserStep{Users: users})
|
||||||
|
}
|
||||||
|
return steps, nil
|
||||||
|
}
|
||||||
|
userSession, err := userSessionByIDs(repo.UserSessionViewProvider, request.AgentID, request.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
user, err := userByID(repo.UserViewProvider, request.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !user.PasswordSet {
|
||||||
|
return append(steps, &model.InitPasswordStep{}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !checkVerificationTime(userSession.PasswordVerification, repo.PasswordCheckLifeTime) {
|
||||||
|
return append(steps, &model.PasswordStep{}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if step, ok := repo.mfaChecked(userSession, request, user); !ok {
|
||||||
|
return append(steps, step), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.PasswordChangeRequired {
|
||||||
|
steps = append(steps, &model.ChangePasswordStep{})
|
||||||
|
}
|
||||||
|
if !user.IsEmailVerified {
|
||||||
|
steps = append(steps, &model.VerifyEMailStep{})
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.PasswordChangeRequired || !user.IsEmailVerified {
|
||||||
|
return steps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//PLANNED: consent step
|
||||||
|
return append(steps, &model.RedirectToCallbackStep{}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *AuthRequestRepo) usersForUserSelection(request *model.AuthRequest) ([]model.UserSelection, error) {
|
||||||
|
userSessions, err := userSessionsByUserAgentID(repo.UserSessionViewProvider, request.AgentID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
users := make([]model.UserSelection, len(userSessions))
|
||||||
|
for i, session := range userSessions {
|
||||||
|
users[i] = model.UserSelection{
|
||||||
|
UserID: session.UserID,
|
||||||
|
UserName: session.UserName,
|
||||||
|
UserSessionState: session.State,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView, request *model.AuthRequest, user *user_model.UserView) (model.NextStep, bool) {
|
||||||
|
mfaLevel := request.MfaLevel()
|
||||||
|
required := user.MfaMaxSetUp < mfaLevel
|
||||||
|
if required || !repo.mfaSkippedOrSetUp(user) {
|
||||||
|
return &model.MfaPromptStep{
|
||||||
|
Required: required,
|
||||||
|
MfaProviders: user.MfaTypesSetupPossible(mfaLevel),
|
||||||
|
}, false
|
||||||
|
}
|
||||||
|
switch mfaLevel {
|
||||||
|
default:
|
||||||
|
fallthrough
|
||||||
|
case model.MfaLevelSoftware:
|
||||||
|
if checkVerificationTime(userSession.MfaSoftwareVerification, repo.MfaSoftwareCheckLifeTime) {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case model.MfaLevelHardware:
|
||||||
|
if checkVerificationTime(userSession.MfaHardwareVerification, repo.MfaHardwareCheckLifeTime) {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &model.MfaVerificationStep{
|
||||||
|
MfaProviders: user.MfaTypesAllowed(mfaLevel),
|
||||||
|
}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *AuthRequestRepo) mfaSkippedOrSetUp(user *user_model.UserView) bool {
|
||||||
|
if user.MfaMaxSetUp >= 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return checkVerificationTime(user.MfaInitSkipped, repo.MfaInitSkippedLifeTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkVerificationTime(verificationTime time.Time, lifetime time.Duration) bool {
|
||||||
|
return verificationTime.Add(lifetime).After(time.Now().UTC())
|
||||||
|
}
|
||||||
|
|
||||||
|
func userSessionsByUserAgentID(provider userSessionViewProvider, agentID string) ([]*user_model.UserSessionView, error) {
|
||||||
|
session, err := provider.UserSessionsByAgentID(agentID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return view_model.UserSessionsToModel(session), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func userSessionByIDs(provider userSessionViewProvider, agentID, userID string) (*user_model.UserSessionView, error) {
|
||||||
|
session, err := provider.UserSessionByIDs(agentID, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return view_model.UserSessionToModel(session), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func userByID(provider userViewProvider, userID string) (*user_model.UserView, error) {
|
||||||
|
user, err := provider.UserByID(userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return view_model.UserToModel(user), nil
|
||||||
|
}
|
@@ -0,0 +1,475 @@
|
|||||||
|
package eventstore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
|
||||||
|
"github.com/caos/zitadel/internal/auth_request/model"
|
||||||
|
"github.com/caos/zitadel/internal/auth_request/repository/cache"
|
||||||
|
"github.com/caos/zitadel/internal/errors"
|
||||||
|
user_model "github.com/caos/zitadel/internal/user/model"
|
||||||
|
user_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
||||||
|
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockViewNoUserSession struct{}
|
||||||
|
|
||||||
|
func (m *mockViewNoUserSession) UserSessionByIDs(string, string) (*view_model.UserSessionView, error) {
|
||||||
|
return nil, errors.ThrowNotFound(nil, "id", "user session not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockViewNoUserSession) UserSessionsByAgentID(string) ([]*view_model.UserSessionView, error) {
|
||||||
|
return nil, errors.ThrowInternal(nil, "id", "internal error")
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockViewUserSession struct {
|
||||||
|
PasswordVerification time.Time
|
||||||
|
MfaSoftwareVerification time.Time
|
||||||
|
Users []mockUser
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockUser struct {
|
||||||
|
UserID string
|
||||||
|
UserName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockViewUserSession) UserSessionByIDs(string, string) (*view_model.UserSessionView, error) {
|
||||||
|
return &view_model.UserSessionView{
|
||||||
|
PasswordVerification: m.PasswordVerification,
|
||||||
|
MfaSoftwareVerification: m.MfaSoftwareVerification,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockViewUserSession) UserSessionsByAgentID(string) ([]*view_model.UserSessionView, error) {
|
||||||
|
sessions := make([]*view_model.UserSessionView, len(m.Users))
|
||||||
|
for i, user := range m.Users {
|
||||||
|
sessions[i] = &view_model.UserSessionView{
|
||||||
|
UserID: user.UserID,
|
||||||
|
UserName: user.UserName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sessions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockViewNoUser struct{}
|
||||||
|
|
||||||
|
func (m *mockViewNoUser) UserByID(string) (*view_model.UserView, error) {
|
||||||
|
return nil, errors.ThrowNotFound(nil, "id", "user not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockViewUser struct {
|
||||||
|
PasswordSet bool
|
||||||
|
PasswordChangeRequired bool
|
||||||
|
IsEmailVerified bool
|
||||||
|
OTPState int32
|
||||||
|
MfaMaxSetUp int32
|
||||||
|
MfaInitSkipped time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockViewUser) UserByID(string) (*view_model.UserView, error) {
|
||||||
|
return &view_model.UserView{
|
||||||
|
PasswordSet: m.PasswordSet,
|
||||||
|
PasswordChangeRequired: m.PasswordChangeRequired,
|
||||||
|
IsEmailVerified: m.IsEmailVerified,
|
||||||
|
OTPState: m.OTPState,
|
||||||
|
MfaMaxSetUp: m.MfaMaxSetUp,
|
||||||
|
MfaInitSkipped: m.MfaInitSkipped,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
UserEvents *user_event.UserEventstore
|
||||||
|
AuthRequests *cache.AuthRequestCache
|
||||||
|
View *view.View
|
||||||
|
userSessionViewProvider userSessionViewProvider
|
||||||
|
userViewProvider userViewProvider
|
||||||
|
PasswordCheckLifeTime time.Duration
|
||||||
|
MfaInitSkippedLifeTime time.Duration
|
||||||
|
MfaSoftwareCheckLifeTime time.Duration
|
||||||
|
MfaHardwareCheckLifeTime time.Duration
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
request *model.AuthRequest
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
want []model.NextStep
|
||||||
|
wantErr func(error) bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"request nil, error",
|
||||||
|
fields{},
|
||||||
|
args{nil},
|
||||||
|
nil,
|
||||||
|
errors.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user not set, login step",
|
||||||
|
fields{},
|
||||||
|
args{&model.AuthRequest{}},
|
||||||
|
[]model.NextStep{&model.LoginStep{}},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user not set and prompt none, no step",
|
||||||
|
fields{},
|
||||||
|
args{&model.AuthRequest{Prompt: model.PromptNone}},
|
||||||
|
[]model.NextStep{},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user not set, prompt select account and internal error, internal error",
|
||||||
|
fields{
|
||||||
|
userSessionViewProvider: &mockViewNoUserSession{},
|
||||||
|
},
|
||||||
|
args{&model.AuthRequest{Prompt: model.PromptSelectAccount}},
|
||||||
|
nil,
|
||||||
|
errors.IsInternal,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user not set, prompt select account, login and select account steps",
|
||||||
|
fields{
|
||||||
|
userSessionViewProvider: &mockViewUserSession{
|
||||||
|
Users: []mockUser{
|
||||||
|
{
|
||||||
|
"id1",
|
||||||
|
"username1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id2",
|
||||||
|
"username2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args{&model.AuthRequest{Prompt: model.PromptSelectAccount}},
|
||||||
|
[]model.NextStep{
|
||||||
|
&model.LoginStep{},
|
||||||
|
&model.SelectUserStep{
|
||||||
|
Users: []model.UserSelection{
|
||||||
|
{
|
||||||
|
UserID: "id1",
|
||||||
|
UserName: "username1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
UserID: "id2",
|
||||||
|
UserName: "username2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"usersession not found, not found error",
|
||||||
|
fields{
|
||||||
|
userSessionViewProvider: &mockViewNoUserSession{},
|
||||||
|
},
|
||||||
|
args{&model.AuthRequest{UserID: "UserID"}},
|
||||||
|
nil,
|
||||||
|
errors.IsNotFound,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user not not found, not found error",
|
||||||
|
fields{
|
||||||
|
userSessionViewProvider: &mockViewUserSession{},
|
||||||
|
userViewProvider: &mockViewNoUser{},
|
||||||
|
},
|
||||||
|
args{&model.AuthRequest{UserID: "UserID"}},
|
||||||
|
nil,
|
||||||
|
errors.IsNotFound,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"password not set, init password step",
|
||||||
|
fields{
|
||||||
|
userSessionViewProvider: &mockViewUserSession{},
|
||||||
|
userViewProvider: &mockViewUser{},
|
||||||
|
},
|
||||||
|
args{&model.AuthRequest{UserID: "UserID"}},
|
||||||
|
[]model.NextStep{&model.InitPasswordStep{}},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"password not verified, password check step",
|
||||||
|
fields{
|
||||||
|
userSessionViewProvider: &mockViewUserSession{},
|
||||||
|
userViewProvider: &mockViewUser{
|
||||||
|
PasswordSet: true,
|
||||||
|
},
|
||||||
|
PasswordCheckLifeTime: 10 * 24 * time.Hour,
|
||||||
|
},
|
||||||
|
args{&model.AuthRequest{UserID: "UserID"}},
|
||||||
|
[]model.NextStep{&model.PasswordStep{}},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mfa not verified, mfa check step",
|
||||||
|
fields{
|
||||||
|
userSessionViewProvider: &mockViewUserSession{
|
||||||
|
PasswordVerification: time.Now().UTC().Add(-5 * time.Minute),
|
||||||
|
},
|
||||||
|
userViewProvider: &mockViewUser{
|
||||||
|
PasswordSet: true,
|
||||||
|
OTPState: int32(user_model.MFASTATE_READY),
|
||||||
|
MfaMaxSetUp: int32(model.MfaLevelSoftware),
|
||||||
|
},
|
||||||
|
PasswordCheckLifeTime: 10 * 24 * time.Hour,
|
||||||
|
MfaSoftwareCheckLifeTime: 18 * time.Hour,
|
||||||
|
},
|
||||||
|
args{&model.AuthRequest{UserID: "UserID"}},
|
||||||
|
[]model.NextStep{&model.MfaVerificationStep{
|
||||||
|
MfaProviders: []model.MfaType{model.MfaTypeOTP},
|
||||||
|
}},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"password change required and email verified, password change step",
|
||||||
|
fields{
|
||||||
|
userSessionViewProvider: &mockViewUserSession{
|
||||||
|
PasswordVerification: time.Now().UTC().Add(-5 * time.Minute),
|
||||||
|
MfaSoftwareVerification: time.Now().UTC().Add(-5 * time.Minute),
|
||||||
|
},
|
||||||
|
userViewProvider: &mockViewUser{
|
||||||
|
PasswordSet: true,
|
||||||
|
PasswordChangeRequired: true,
|
||||||
|
IsEmailVerified: true,
|
||||||
|
},
|
||||||
|
PasswordCheckLifeTime: 10 * 24 * time.Hour,
|
||||||
|
MfaSoftwareCheckLifeTime: 18 * time.Hour,
|
||||||
|
},
|
||||||
|
args{&model.AuthRequest{UserID: "UserID"}},
|
||||||
|
[]model.NextStep{&model.ChangePasswordStep{}},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email not verified and no password change required, mail verification step",
|
||||||
|
fields{
|
||||||
|
userSessionViewProvider: &mockViewUserSession{
|
||||||
|
PasswordVerification: time.Now().UTC().Add(-5 * time.Minute),
|
||||||
|
MfaSoftwareVerification: time.Now().UTC().Add(-5 * time.Minute),
|
||||||
|
},
|
||||||
|
userViewProvider: &mockViewUser{
|
||||||
|
PasswordSet: true,
|
||||||
|
},
|
||||||
|
PasswordCheckLifeTime: 10 * 24 * time.Hour,
|
||||||
|
MfaSoftwareCheckLifeTime: 18 * time.Hour,
|
||||||
|
},
|
||||||
|
args{&model.AuthRequest{UserID: "UserID"}},
|
||||||
|
[]model.NextStep{&model.VerifyEMailStep{}},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email not verified and password change required, mail verification step",
|
||||||
|
fields{
|
||||||
|
userSessionViewProvider: &mockViewUserSession{
|
||||||
|
PasswordVerification: time.Now().UTC().Add(-5 * time.Minute),
|
||||||
|
MfaSoftwareVerification: time.Now().UTC().Add(-5 * time.Minute),
|
||||||
|
},
|
||||||
|
userViewProvider: &mockViewUser{
|
||||||
|
PasswordSet: true,
|
||||||
|
PasswordChangeRequired: true,
|
||||||
|
},
|
||||||
|
PasswordCheckLifeTime: 10 * 24 * time.Hour,
|
||||||
|
MfaSoftwareCheckLifeTime: 18 * time.Hour,
|
||||||
|
},
|
||||||
|
args{&model.AuthRequest{UserID: "UserID"}},
|
||||||
|
[]model.NextStep{&model.ChangePasswordStep{}, &model.VerifyEMailStep{}},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email verified and no password change required, redirect to callback step",
|
||||||
|
fields{
|
||||||
|
userSessionViewProvider: &mockViewUserSession{
|
||||||
|
PasswordVerification: time.Now().UTC().Add(-5 * time.Minute),
|
||||||
|
MfaSoftwareVerification: time.Now().UTC().Add(-5 * time.Minute),
|
||||||
|
},
|
||||||
|
userViewProvider: &mockViewUser{
|
||||||
|
PasswordSet: true,
|
||||||
|
IsEmailVerified: true,
|
||||||
|
},
|
||||||
|
PasswordCheckLifeTime: 10 * 24 * time.Hour,
|
||||||
|
MfaSoftwareCheckLifeTime: 18 * time.Hour,
|
||||||
|
},
|
||||||
|
args{&model.AuthRequest{UserID: "UserID"}},
|
||||||
|
[]model.NextStep{&model.RedirectToCallbackStep{}},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
repo := &AuthRequestRepo{
|
||||||
|
UserEvents: tt.fields.UserEvents,
|
||||||
|
AuthRequests: tt.fields.AuthRequests,
|
||||||
|
View: tt.fields.View,
|
||||||
|
UserSessionViewProvider: tt.fields.userSessionViewProvider,
|
||||||
|
UserViewProvider: tt.fields.userViewProvider,
|
||||||
|
PasswordCheckLifeTime: tt.fields.PasswordCheckLifeTime,
|
||||||
|
MfaInitSkippedLifeTime: tt.fields.MfaInitSkippedLifeTime,
|
||||||
|
MfaSoftwareCheckLifeTime: tt.fields.MfaSoftwareCheckLifeTime,
|
||||||
|
MfaHardwareCheckLifeTime: tt.fields.MfaHardwareCheckLifeTime,
|
||||||
|
}
|
||||||
|
got, err := repo.nextSteps(tt.args.request)
|
||||||
|
if (err != nil && tt.wantErr == nil) || (tt.wantErr != nil && !tt.wantErr(err)) {
|
||||||
|
t.Errorf("nextSteps() wrong error = %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.ElementsMatch(t, got, tt.want)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthRequestRepo_mfaChecked(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
MfaInitSkippedLifeTime time.Duration
|
||||||
|
MfaSoftwareCheckLifeTime time.Duration
|
||||||
|
MfaHardwareCheckLifeTime time.Duration
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
userSession *user_model.UserSessionView
|
||||||
|
request *model.AuthRequest
|
||||||
|
user *user_model.UserView
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
want model.NextStep
|
||||||
|
wantChecked bool
|
||||||
|
}{
|
||||||
|
//{
|
||||||
|
// "required, prompt and false", //TODO: enable when LevelsOfAssurance is checked
|
||||||
|
// fields{},
|
||||||
|
// args{
|
||||||
|
// request: &model.AuthRequest{PossibleLOAs: []model.LevelOfAssurance{}},
|
||||||
|
// user: &user_model.UserView{
|
||||||
|
// OTPState: user_model.MFASTATE_READY,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// false,
|
||||||
|
//},
|
||||||
|
{
|
||||||
|
"not set up, prompt and false",
|
||||||
|
fields{
|
||||||
|
MfaInitSkippedLifeTime: 30 * 24 * time.Hour,
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
request: &model.AuthRequest{},
|
||||||
|
user: &user_model.UserView{
|
||||||
|
MfaMaxSetUp: -1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&model.MfaPromptStep{
|
||||||
|
MfaProviders: []model.MfaType{},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checked mfa software, true",
|
||||||
|
fields{
|
||||||
|
MfaSoftwareCheckLifeTime: 18 * time.Hour,
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
request: &model.AuthRequest{},
|
||||||
|
user: &user_model.UserView{
|
||||||
|
OTPState: user_model.MFASTATE_READY,
|
||||||
|
},
|
||||||
|
userSession: &user_model.UserSessionView{MfaSoftwareVerification: time.Now().UTC().Add(-5 * time.Hour)},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"not checked, check and false",
|
||||||
|
fields{
|
||||||
|
MfaSoftwareCheckLifeTime: 18 * time.Hour,
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
request: &model.AuthRequest{},
|
||||||
|
user: &user_model.UserView{
|
||||||
|
OTPState: user_model.MFASTATE_READY,
|
||||||
|
},
|
||||||
|
userSession: &user_model.UserSessionView{},
|
||||||
|
},
|
||||||
|
|
||||||
|
&model.MfaVerificationStep{
|
||||||
|
MfaProviders: []model.MfaType{model.MfaTypeOTP},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
repo := &AuthRequestRepo{
|
||||||
|
MfaInitSkippedLifeTime: tt.fields.MfaInitSkippedLifeTime,
|
||||||
|
MfaSoftwareCheckLifeTime: tt.fields.MfaSoftwareCheckLifeTime,
|
||||||
|
MfaHardwareCheckLifeTime: tt.fields.MfaHardwareCheckLifeTime,
|
||||||
|
}
|
||||||
|
got, ok := repo.mfaChecked(tt.args.userSession, tt.args.request, tt.args.user)
|
||||||
|
if ok != tt.wantChecked {
|
||||||
|
t.Errorf("mfaChecked() checked = %v, want %v", ok, tt.wantChecked)
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthRequestRepo_mfaSkippedOrSetUp(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
MfaInitSkippedLifeTime time.Duration
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
user *user_model.UserView
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"mfa set up, true",
|
||||||
|
fields{},
|
||||||
|
args{&user_model.UserView{
|
||||||
|
MfaMaxSetUp: model.MfaLevelSoftware,
|
||||||
|
}},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mfa skipped active, true",
|
||||||
|
fields{
|
||||||
|
MfaInitSkippedLifeTime: 30 * 24 * time.Hour,
|
||||||
|
},
|
||||||
|
args{&user_model.UserView{
|
||||||
|
MfaMaxSetUp: -1,
|
||||||
|
MfaInitSkipped: time.Now().UTC().Add(-10 * time.Hour),
|
||||||
|
}},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mfa skipped inactive, false",
|
||||||
|
fields{
|
||||||
|
MfaInitSkippedLifeTime: 30 * 24 * time.Hour,
|
||||||
|
},
|
||||||
|
args{&user_model.UserView{
|
||||||
|
MfaMaxSetUp: -1,
|
||||||
|
MfaInitSkipped: time.Now().UTC().Add(-40 * 24 * time.Hour),
|
||||||
|
}},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
repo := &AuthRequestRepo{
|
||||||
|
MfaInitSkippedLifeTime: tt.fields.MfaInitSkippedLifeTime,
|
||||||
|
}
|
||||||
|
if got := repo.mfaSkippedOrSetUp(tt.args.user); got != tt.want {
|
||||||
|
t.Errorf("mfaSkippedOrSetUp() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
26
internal/auth/repository/eventsourcing/eventstore/token.go
Normal file
26
internal/auth/repository/eventsourcing/eventstore/token.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package eventstore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
|
||||||
|
token_model "github.com/caos/zitadel/internal/token/model"
|
||||||
|
token_view_model "github.com/caos/zitadel/internal/token/repository/view/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TokenRepo struct {
|
||||||
|
View *view.View
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *TokenRepo) CreateToken(ctx context.Context, agentID, applicationID, userID string, lifetime time.Duration) (*token_model.Token, error) {
|
||||||
|
token, err := repo.View.CreateToken(agentID, applicationID, userID, lifetime)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return token_view_model.TokenToModel(token), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *TokenRepo) IsTokenValid(ctx context.Context, tokenID string) (bool, error) {
|
||||||
|
return repo.View.IsTokenValid(tokenID)
|
||||||
|
}
|
129
internal/auth/repository/eventsourcing/eventstore/user.go
Normal file
129
internal/auth/repository/eventsourcing/eventstore/user.go
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
package eventstore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/api/auth"
|
||||||
|
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
|
||||||
|
"github.com/caos/zitadel/internal/errors"
|
||||||
|
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||||
|
"github.com/caos/zitadel/internal/user/model"
|
||||||
|
user_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserRepo struct {
|
||||||
|
UserEvents *user_event.UserEventstore
|
||||||
|
View *view.View
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) Health(ctx context.Context) error {
|
||||||
|
return repo.UserEvents.Health(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) Register(ctx context.Context, user *model.User, resourceOwner string) (*model.User, error) {
|
||||||
|
return repo.UserEvents.RegisterUser(ctx, user, resourceOwner)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) MyProfile(ctx context.Context) (*model.Profile, error) {
|
||||||
|
return repo.UserEvents.ProfileByID(ctx, auth.GetCtxData(ctx).UserID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) ChangeMyProfile(ctx context.Context, profile *model.Profile) (*model.Profile, error) {
|
||||||
|
if err := checkIDs(ctx, profile.ObjectRoot); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return repo.UserEvents.ChangeProfile(ctx, profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) MyEmail(ctx context.Context) (*model.Email, error) {
|
||||||
|
return repo.UserEvents.EmailByID(ctx, auth.GetCtxData(ctx).UserID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) ChangeMyEmail(ctx context.Context, email *model.Email) (*model.Email, error) {
|
||||||
|
if err := checkIDs(ctx, email.ObjectRoot); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return repo.UserEvents.ChangeEmail(ctx, email)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) VerifyMyEmail(ctx context.Context, code string) error {
|
||||||
|
return repo.UserEvents.VerifyEmail(ctx, auth.GetCtxData(ctx).UserID, code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) ResendMyEmailVerificationMail(ctx context.Context) error {
|
||||||
|
return repo.UserEvents.CreateEmailVerificationCode(ctx, auth.GetCtxData(ctx).UserID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) MyPhone(ctx context.Context) (*model.Phone, error) {
|
||||||
|
return repo.UserEvents.PhoneByID(ctx, auth.GetCtxData(ctx).UserID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) ChangeMyPhone(ctx context.Context, phone *model.Phone) (*model.Phone, error) {
|
||||||
|
if err := checkIDs(ctx, phone.ObjectRoot); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return repo.UserEvents.ChangePhone(ctx, phone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) VerifyMyPhone(ctx context.Context, code string) error {
|
||||||
|
return repo.UserEvents.VerifyPhone(ctx, auth.GetCtxData(ctx).UserID, code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) ResendMyPhoneVerificationCode(ctx context.Context) error {
|
||||||
|
return repo.UserEvents.CreatePhoneVerificationCode(ctx, auth.GetCtxData(ctx).UserID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) MyAddress(ctx context.Context) (*model.Address, error) {
|
||||||
|
return repo.UserEvents.AddressByID(ctx, auth.GetCtxData(ctx).UserID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) ChangeMyAddress(ctx context.Context, address *model.Address) (*model.Address, error) {
|
||||||
|
if err := checkIDs(ctx, address.ObjectRoot); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return repo.UserEvents.ChangeAddress(ctx, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) ChangeMyPassword(ctx context.Context, old, new string) error {
|
||||||
|
_, err := repo.UserEvents.ChangePassword(ctx, auth.GetCtxData(ctx).UserID, old, new)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) AddMyMfaOTP(ctx context.Context) (*model.OTP, error) {
|
||||||
|
return repo.UserEvents.AddOTP(ctx, auth.GetCtxData(ctx).UserID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) VerifyMyMfaOTP(ctx context.Context, code string) error {
|
||||||
|
return repo.UserEvents.CheckMfaOTPSetup(ctx, auth.GetCtxData(ctx).UserID, code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) RemoveMyMfaOTP(ctx context.Context) error {
|
||||||
|
return repo.UserEvents.RemoveOTP(ctx, auth.GetCtxData(ctx).UserID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) SkipMfaInit(ctx context.Context, userID string) error {
|
||||||
|
return repo.UserEvents.SkipMfaInit(ctx, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) RequestPasswordReset(ctx context.Context, username string) error {
|
||||||
|
user, err := repo.View.UserByUsername(username)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return repo.UserEvents.RequestSetPassword(ctx, user.ID, model.NOTIFICATIONTYPE_EMAIL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) SetPassword(ctx context.Context, userID, code, password string) error {
|
||||||
|
return repo.UserEvents.SetPassword(ctx, userID, code, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) SignOut(ctx context.Context, agentID, userID string) error {
|
||||||
|
return repo.UserEvents.SignOut(ctx, agentID, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkIDs(ctx context.Context, obj es_models.ObjectRoot) error {
|
||||||
|
if obj.AggregateID != auth.GetCtxData(ctx).UserID {
|
||||||
|
return errors.ThrowPermissionDenied(nil, "EVENT-kFi9w", "object does not belong to user")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
42
internal/auth/repository/eventsourcing/handler/handler.go
Normal file
42
internal/auth/repository/eventsourcing/handler/handler.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/spooler"
|
||||||
|
usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Configs map[string]*Config
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
MinimumCycleDurationMillisecond int
|
||||||
|
}
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
view *view.View
|
||||||
|
bulkLimit uint64
|
||||||
|
cycleDuration time.Duration
|
||||||
|
errorCountUntilSkip uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type EventstoreRepos struct {
|
||||||
|
UserEvents *usr_event.UserEventstore
|
||||||
|
}
|
||||||
|
|
||||||
|
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, repos EventstoreRepos) []spooler.Handler {
|
||||||
|
return []spooler.Handler{
|
||||||
|
&User{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}},
|
||||||
|
&UserSession{handler: handler{view, bulkLimit, configs.cycleDuration("UserSession"), errorCount}, userEvents: repos.UserEvents},
|
||||||
|
&Token{handler: handler{view, bulkLimit, configs.cycleDuration("Token"), errorCount}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (configs Configs) cycleDuration(viewModel string) time.Duration {
|
||||||
|
c, ok := configs[viewModel]
|
||||||
|
if !ok {
|
||||||
|
return 1 * time.Second
|
||||||
|
}
|
||||||
|
return time.Duration(c.MinimumCycleDurationMillisecond) * time.Millisecond
|
||||||
|
}
|
69
internal/auth/repository/eventsourcing/handler/token.go
Normal file
69
internal/auth/repository/eventsourcing/handler/token.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||||
|
|
||||||
|
"github.com/caos/logging"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/models"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/spooler"
|
||||||
|
"github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Token struct {
|
||||||
|
handler
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
tokenTable = "auth.tokens"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (u *Token) MinimumCycleDuration() time.Duration { return u.cycleDuration }
|
||||||
|
|
||||||
|
func (u *Token) ViewModel() string {
|
||||||
|
return tokenTable
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Token) EventQuery() (*models.SearchQuery, error) {
|
||||||
|
sequence, err := u.view.GetLatestTokenSequence()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return eventsourcing.UserQuery(sequence), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Token) Process(event *models.Event) (err error) {
|
||||||
|
switch event.Type {
|
||||||
|
case es_model.SignedOut:
|
||||||
|
id, err := agentIDFromSession(event)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = u.view.DeleteSessionTokens(id, event.AggregateID, event.Sequence)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return u.view.ProcessedTokenSequence(event.Sequence)
|
||||||
|
default:
|
||||||
|
return u.view.ProcessedTokenSequence(event.Sequence)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Token) OnError(event *models.Event, err error) error {
|
||||||
|
logging.LogWithFields("SPOOL-3jkl4", "id", event.AggregateID).WithError(err).Warn("something went wrong in token handler")
|
||||||
|
return spooler.HandleError(event, err, u.view.GetLatestTokenFailedEvent, u.view.ProcessedTokenFailedEvent, u.view.ProcessedTokenSequence, u.errorCountUntilSkip)
|
||||||
|
}
|
||||||
|
|
||||||
|
func agentIDFromSession(event *models.Event) (string, error) {
|
||||||
|
session := make(map[string]interface{})
|
||||||
|
if err := json.Unmarshal(event.Data, session); err != nil {
|
||||||
|
logging.Log("EVEN-s3bq9").WithError(err).Error("could not unmarshal event data")
|
||||||
|
return "", caos_errs.ThrowInternal(nil, "MODEL-sd325", "could not unmarshal data")
|
||||||
|
}
|
||||||
|
return session["agentID"].(string), nil
|
||||||
|
}
|
77
internal/auth/repository/eventsourcing/handler/user.go
Normal file
77
internal/auth/repository/eventsourcing/handler/user.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/caos/logging"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/models"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/spooler"
|
||||||
|
"github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
||||||
|
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
handler
|
||||||
|
eventstore eventstore.Eventstore
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
userTable = "auth.users"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *User) MinimumCycleDuration() time.Duration { return p.cycleDuration }
|
||||||
|
|
||||||
|
func (p *User) ViewModel() string {
|
||||||
|
return userTable
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *User) EventQuery() (*models.SearchQuery, error) {
|
||||||
|
sequence, err := p.view.GetLatestUserSequence()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return eventsourcing.UserQuery(sequence), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *User) Process(event *models.Event) (err error) {
|
||||||
|
user := new(view_model.UserView)
|
||||||
|
switch event.Type {
|
||||||
|
case es_model.UserAdded,
|
||||||
|
es_model.UserRegistered:
|
||||||
|
user.AppendEvent(event)
|
||||||
|
case es_model.UserProfileChanged,
|
||||||
|
es_model.UserEmailChanged,
|
||||||
|
es_model.UserEmailVerified,
|
||||||
|
es_model.UserPhoneChanged,
|
||||||
|
es_model.UserPhoneVerified,
|
||||||
|
es_model.UserAddressChanged,
|
||||||
|
es_model.UserDeactivated,
|
||||||
|
es_model.UserReactivated,
|
||||||
|
es_model.UserLocked,
|
||||||
|
es_model.UserUnlocked,
|
||||||
|
es_model.MfaOtpAdded,
|
||||||
|
es_model.MfaOtpVerified,
|
||||||
|
es_model.MfaOtpRemoved:
|
||||||
|
user, err = p.view.UserByID(event.AggregateID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = user.AppendEvent(event)
|
||||||
|
case es_model.UserDeleted:
|
||||||
|
err = p.view.DeleteUser(event.AggregateID, event.Sequence)
|
||||||
|
default:
|
||||||
|
return p.view.ProcessedUserSequence(event.Sequence)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return p.view.PutUser(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *User) OnError(event *models.Event, err error) error {
|
||||||
|
logging.LogWithFields("SPOOL-is8wa", "id", event.AggregateID).WithError(err).Warn("something went wrong in user handler")
|
||||||
|
return spooler.HandleError(event, err, p.view.GetLatestUserFailedEvent, p.view.ProcessedUserFailedEvent, p.view.ProcessedUserSequence, p.errorCountUntilSkip)
|
||||||
|
}
|
@@ -0,0 +1,90 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
req_model "github.com/caos/zitadel/internal/auth_request/model"
|
||||||
|
"github.com/caos/zitadel/internal/errors"
|
||||||
|
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||||
|
|
||||||
|
"github.com/caos/logging"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/models"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/spooler"
|
||||||
|
"github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
||||||
|
user_events "github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
||||||
|
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserSession struct {
|
||||||
|
handler
|
||||||
|
userEvents *user_events.UserEventstore
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
userSessionTable = "auth.user_sessions"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (u *UserSession) MinimumCycleDuration() time.Duration { return u.cycleDuration }
|
||||||
|
|
||||||
|
func (u *UserSession) ViewModel() string {
|
||||||
|
return userSessionTable
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserSession) EventQuery() (*models.SearchQuery, error) {
|
||||||
|
sequence, err := u.view.GetLatestUserSessionSequence()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return eventsourcing.UserQuery(sequence), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserSession) Process(event *models.Event) (err error) {
|
||||||
|
eventData, err := view_model.UserSessionFromEvent(event)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
session, err := u.view.UserSessionByIDs(eventData.UserAgentID, event.AggregateID)
|
||||||
|
if err != nil {
|
||||||
|
if !errors.IsNotFound(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
session = &view_model.UserSessionView{
|
||||||
|
CreationDate: event.CreationDate,
|
||||||
|
ResourceOwner: event.ResourceOwner,
|
||||||
|
UserAgentID: eventData.UserAgentID,
|
||||||
|
UserID: event.AggregateID,
|
||||||
|
State: int32(req_model.UserSessionStateActive),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch event.Type {
|
||||||
|
case es_model.UserPasswordCheckSucceeded,
|
||||||
|
es_model.UserPasswordCheckFailed,
|
||||||
|
es_model.UserPasswordChanged,
|
||||||
|
es_model.MfaOtpCheckSucceeded,
|
||||||
|
es_model.MfaOtpCheckFailed,
|
||||||
|
es_model.MfaOtpRemoved:
|
||||||
|
session.AppendEvent(event)
|
||||||
|
default:
|
||||||
|
return u.view.ProcessedUserSessionSequence(event.Sequence)
|
||||||
|
}
|
||||||
|
if err := u.FillUserInfo(session, event.AggregateID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return u.view.PutUserSession(session)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserSession) OnError(event *models.Event, err error) error {
|
||||||
|
logging.LogWithFields("SPOOL-sdfw3s", "id", event.AggregateID).WithError(err).Warn("something went wrong in user session handler")
|
||||||
|
return spooler.HandleError(event, err, u.view.GetLatestUserSessionFailedEvent, u.view.ProcessedUserSessionFailedEvent, u.view.ProcessedUserSessionSequence, u.errorCountUntilSkip)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserSession) FillUserInfo(session *view_model.UserSessionView, id string) error {
|
||||||
|
user, err := u.userEvents.UserByID(context.Background(), id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
session.UserName = user.UserName
|
||||||
|
return nil
|
||||||
|
}
|
93
internal/auth/repository/eventsourcing/repository.go
Normal file
93
internal/auth/repository/eventsourcing/repository.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package eventsourcing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/handler"
|
||||||
|
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/spooler"
|
||||||
|
auth_view "github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
|
||||||
|
"github.com/caos/zitadel/internal/auth_request/repository/cache"
|
||||||
|
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||||
|
"github.com/caos/zitadel/internal/config/types"
|
||||||
|
es_int "github.com/caos/zitadel/internal/eventstore"
|
||||||
|
es_spol "github.com/caos/zitadel/internal/eventstore/spooler"
|
||||||
|
"github.com/caos/zitadel/internal/id"
|
||||||
|
es_user "github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Eventstore es_int.Config
|
||||||
|
AuthRequest cache.Config
|
||||||
|
View types.SQL
|
||||||
|
Spooler spooler.SpoolerConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type EsRepository struct {
|
||||||
|
spooler *es_spol.Spooler
|
||||||
|
eventstore.UserRepo
|
||||||
|
eventstore.AuthRequestRepo
|
||||||
|
eventstore.TokenRepo
|
||||||
|
}
|
||||||
|
|
||||||
|
func Start(conf Config, systemDefaults sd.SystemDefaults) (*EsRepository, error) {
|
||||||
|
es, err := es_int.Start(conf.Eventstore)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlClient, err := conf.View.Start()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
view, err := auth_view.StartView(sqlClient)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := es_user.StartUser(
|
||||||
|
es_user.UserConfig{
|
||||||
|
Eventstore: es,
|
||||||
|
Cache: conf.Eventstore.Cache,
|
||||||
|
},
|
||||||
|
systemDefaults,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
authReq, err := cache.Start(conf.AuthRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
repos := handler.EventstoreRepos{UserEvents: user}
|
||||||
|
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, repos)
|
||||||
|
|
||||||
|
return &EsRepository{
|
||||||
|
spool,
|
||||||
|
eventstore.UserRepo{
|
||||||
|
UserEvents: user,
|
||||||
|
View: view,
|
||||||
|
},
|
||||||
|
eventstore.AuthRequestRepo{
|
||||||
|
UserEvents: user,
|
||||||
|
AuthRequests: authReq,
|
||||||
|
View: view,
|
||||||
|
UserSessionViewProvider: view,
|
||||||
|
UserViewProvider: view,
|
||||||
|
IdGenerator: id.SonyFlakeGenerator,
|
||||||
|
PasswordCheckLifeTime: systemDefaults.VerificationLifetimes.PasswordCheck.Duration,
|
||||||
|
MfaInitSkippedLifeTime: systemDefaults.VerificationLifetimes.MfaInitSkip.Duration,
|
||||||
|
MfaSoftwareCheckLifeTime: systemDefaults.VerificationLifetimes.MfaSoftwareCheck.Duration,
|
||||||
|
MfaHardwareCheckLifeTime: systemDefaults.VerificationLifetimes.MfaHardwareCheck.Duration,
|
||||||
|
},
|
||||||
|
eventstore.TokenRepo{View: view},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *EsRepository) Health(ctx context.Context) error {
|
||||||
|
if err := repo.UserRepo.Health(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return repo.AuthRequestRepo.Health(ctx)
|
||||||
|
}
|
46
internal/auth/repository/eventsourcing/spooler/lock.go
Normal file
46
internal/auth/repository/eventsourcing/spooler/lock.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package spooler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/cockroachdb/cockroach-go/crdb"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
lockTable = "auth.locks"
|
||||||
|
lockedUntilKey = "locked_until"
|
||||||
|
lockerIDKey = "locker_id"
|
||||||
|
objectTypeKey = "object_type"
|
||||||
|
)
|
||||||
|
|
||||||
|
type locker struct {
|
||||||
|
dbClient *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
type lock struct {
|
||||||
|
LockerID string `gorm:"column:locker_id;primary_key"`
|
||||||
|
LockedUntil time.Time `gorm:"column:locked_until"`
|
||||||
|
ViewName string `gorm:"column:object_type;primary_key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error {
|
||||||
|
return crdb.ExecuteTx(context.Background(), l.dbClient, nil, func(tx *sql.Tx) error {
|
||||||
|
query := fmt.Sprintf("INSERT INTO %s (%s, %s, %s) VALUES ($1, $2, now()+$3) ON CONFLICT (%s) DO UPDATE SET %s = now()+$4, %s = $5 WHERE (locks.%s < now() OR locks.%s = $6) AND locks.%s = $7",
|
||||||
|
lockTable, objectTypeKey, lockerIDKey, lockedUntilKey, objectTypeKey, lockedUntilKey, lockerIDKey, lockedUntilKey, lockerIDKey, objectTypeKey)
|
||||||
|
|
||||||
|
rs, err := tx.Exec(query, viewModel, lockerID, waitTime.Seconds(), waitTime.Seconds(), lockerID, lockerID, viewModel)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rows, _ := rs.RowsAffected(); rows == 0 {
|
||||||
|
tx.Rollback()
|
||||||
|
return caos_errs.ThrowAlreadyExists(nil, "SPOOL-lso0e", "view already locked")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
127
internal/auth/repository/eventsourcing/spooler/lock_test.go
Normal file
127
internal/auth/repository/eventsourcing/spooler/lock_test.go
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
package spooler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dbMock struct {
|
||||||
|
db *sql.DB
|
||||||
|
mock sqlmock.Sqlmock
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockDB(t *testing.T) *dbMock {
|
||||||
|
mockDB := dbMock{}
|
||||||
|
var err error
|
||||||
|
mockDB.db, mockDB.mock, err = sqlmock.New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error occured while creating stub db %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mockDB.mock.MatchExpectationsInOrder(true)
|
||||||
|
|
||||||
|
return &mockDB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *dbMock) expectCommit() *dbMock {
|
||||||
|
db.mock.ExpectCommit()
|
||||||
|
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *dbMock) expectRollback() *dbMock {
|
||||||
|
db.mock.ExpectRollback()
|
||||||
|
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *dbMock) expectBegin() *dbMock {
|
||||||
|
db.mock.ExpectBegin()
|
||||||
|
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *dbMock) expectSavepoint() *dbMock {
|
||||||
|
db.mock.ExpectExec("SAVEPOINT").WillReturnResult(sqlmock.NewResult(1, 1))
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *dbMock) expectReleaseSavepoint() *dbMock {
|
||||||
|
db.mock.ExpectExec("RELEASE SAVEPOINT").WillReturnResult(sqlmock.NewResult(1, 1))
|
||||||
|
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *dbMock) expectRenew(lockerID, view string, affectedRows int64) *dbMock {
|
||||||
|
query := db.mock.
|
||||||
|
ExpectExec(`INSERT INTO auth\.locks \(object_type, locker_id, locked_until\) VALUES \(\$1, \$2, now\(\)\+\$3\) ON CONFLICT \(object_type\) DO UPDATE SET locked_until = now\(\)\+\$4, locker_id = \$5 WHERE \(locks\.locked_until < now\(\) OR locks\.locker_id = \$6\) AND locks\.object_type = \$7`).
|
||||||
|
WithArgs(view, lockerID, sqlmock.AnyArg(), sqlmock.AnyArg(), lockerID, lockerID, view).
|
||||||
|
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||||
|
|
||||||
|
if affectedRows == 0 {
|
||||||
|
query.WillReturnResult(sqlmock.NewResult(0, 0))
|
||||||
|
} else {
|
||||||
|
query.WillReturnResult(sqlmock.NewResult(1, affectedRows))
|
||||||
|
}
|
||||||
|
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_locker_Renew(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
db *dbMock
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
lockerID string
|
||||||
|
viewModel string
|
||||||
|
waitTime time.Duration
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "renew succeeded",
|
||||||
|
fields: fields{
|
||||||
|
db: mockDB(t).
|
||||||
|
expectBegin().
|
||||||
|
expectSavepoint().
|
||||||
|
expectRenew("locker", "view", 1).
|
||||||
|
expectReleaseSavepoint().
|
||||||
|
expectCommit(),
|
||||||
|
},
|
||||||
|
args: args{lockerID: "locker", viewModel: "view", waitTime: 1 * time.Second},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "renew now rows updated",
|
||||||
|
fields: fields{
|
||||||
|
db: mockDB(t).
|
||||||
|
expectBegin().
|
||||||
|
expectSavepoint().
|
||||||
|
expectRenew("locker", "view", 0).
|
||||||
|
expectRollback(),
|
||||||
|
},
|
||||||
|
args: args{lockerID: "locker", viewModel: "view", waitTime: 1 * time.Second},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
l := &locker{
|
||||||
|
dbClient: tt.fields.db.db,
|
||||||
|
}
|
||||||
|
if err := l.Renew(tt.args.lockerID, tt.args.viewModel, tt.args.waitTime); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("locker.Renew() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
if err := tt.fields.db.mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("not all database expectations met: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
30
internal/auth/repository/eventsourcing/spooler/spooler.go
Normal file
30
internal/auth/repository/eventsourcing/spooler/spooler.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package spooler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/handler"
|
||||||
|
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/spooler"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SpoolerConfig struct {
|
||||||
|
BulkLimit uint64
|
||||||
|
FailureCountUntilSkip uint64
|
||||||
|
ConcurrentTasks int
|
||||||
|
Handlers handler.Configs
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartSpooler(c SpoolerConfig, es eventstore.Eventstore, view *view.View, sql *sql.DB, repos handler.EventstoreRepos) *spooler.Spooler {
|
||||||
|
spoolerConfig := spooler.Config{
|
||||||
|
Eventstore: es,
|
||||||
|
Locker: &locker{dbClient: sql},
|
||||||
|
ConcurrentTasks: c.ConcurrentTasks,
|
||||||
|
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, repos),
|
||||||
|
}
|
||||||
|
spool := spoolerConfig.New()
|
||||||
|
spool.Start()
|
||||||
|
return spool
|
||||||
|
}
|
17
internal/auth/repository/eventsourcing/view/error_event.go
Normal file
17
internal/auth/repository/eventsourcing/view/error_event.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package view
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/caos/zitadel/internal/view"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
errTable = "auth.failed_event"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v *View) saveFailedEvent(failedEvent *view.FailedEvent) error {
|
||||||
|
return view.SaveFailedEvent(v.Db, errTable, failedEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) latestFailedEvent(viewName string, sequence uint64) (*view.FailedEvent, error) {
|
||||||
|
return view.LatestFailedEvent(v.Db, errTable, viewName, sequence)
|
||||||
|
}
|
17
internal/auth/repository/eventsourcing/view/sequence.go
Normal file
17
internal/auth/repository/eventsourcing/view/sequence.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package view
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/caos/zitadel/internal/view"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
sequencesTable = "auth.current_sequences"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v *View) saveCurrentSequence(viewName string, sequence uint64) error {
|
||||||
|
return view.SaveCurrentSequence(v.Db, sequencesTable, viewName, sequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) latestSequence(viewName string) (uint64, error) {
|
||||||
|
return view.LatestSequence(v.Db, sequencesTable, viewName)
|
||||||
|
}
|
77
internal/auth/repository/eventsourcing/view/token.go
Normal file
77
internal/auth/repository/eventsourcing/view/token.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package view
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/token/repository/view"
|
||||||
|
"github.com/caos/zitadel/internal/token/repository/view/model"
|
||||||
|
global_view "github.com/caos/zitadel/internal/view"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tokenTable = "auth.tokens"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v *View) TokenByID(tokenID string) (*model.Token, error) {
|
||||||
|
return view.TokenByID(v.Db, tokenTable, tokenID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) IsTokenValid(tokenID string) (bool, error) {
|
||||||
|
return view.IsTokenValid(v.Db, tokenTable, tokenID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) CreateToken(agentID, applicationID, userID string, lifetime time.Duration) (*model.Token, error) {
|
||||||
|
now := time.Now().UTC()
|
||||||
|
token := &model.Token{
|
||||||
|
CreationDate: now,
|
||||||
|
UserID: userID,
|
||||||
|
ApplicationID: applicationID,
|
||||||
|
UserAgentID: agentID,
|
||||||
|
Expiration: now.Add(lifetime),
|
||||||
|
}
|
||||||
|
err := view.PutToken(v.Db, tokenTable, token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) PutToken(token *model.Token) error {
|
||||||
|
err := view.PutToken(v.Db, tokenTable, token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return v.ProcessedTokenSequence(token.Sequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) DeleteToken(tokenID string, eventSequence uint64) error {
|
||||||
|
err := view.DeleteToken(v.Db, tokenTable, tokenID)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return v.ProcessedTokenSequence(eventSequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) DeleteSessionTokens(agentID, userID string, eventSequence uint64) error {
|
||||||
|
err := view.DeleteTokens(v.Db, tokenTable, agentID, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return v.ProcessedTokenSequence(eventSequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) GetLatestTokenSequence() (uint64, error) {
|
||||||
|
return v.latestSequence(tokenTable)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) ProcessedTokenSequence(eventSequence uint64) error {
|
||||||
|
return v.saveCurrentSequence(tokenTable, eventSequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) GetLatestTokenFailedEvent(sequence uint64) (*global_view.FailedEvent, error) {
|
||||||
|
return v.latestFailedEvent(tokenTable, sequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) ProcessedTokenFailedEvent(failedEvent *global_view.FailedEvent) error {
|
||||||
|
return v.saveFailedEvent(failedEvent)
|
||||||
|
}
|
68
internal/auth/repository/eventsourcing/view/user.go
Normal file
68
internal/auth/repository/eventsourcing/view/user.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package view
|
||||||
|
|
||||||
|
import (
|
||||||
|
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||||
|
"github.com/caos/zitadel/internal/user/repository/view"
|
||||||
|
"github.com/caos/zitadel/internal/user/repository/view/model"
|
||||||
|
global_view "github.com/caos/zitadel/internal/view"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
userTable = "auth.users"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v *View) UserByID(userID string) (*model.UserView, error) {
|
||||||
|
return view.UserByID(v.Db, userTable, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) UserByUsername(userName string) (*model.UserView, error) {
|
||||||
|
return view.UserByUserName(v.Db, userTable, userName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) SearchUsers(request *usr_model.UserSearchRequest) ([]*model.UserView, int, error) {
|
||||||
|
return view.SearchUsers(v.Db, userTable, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) GetGlobalUserByEmail(email string) (*model.UserView, error) {
|
||||||
|
return view.GetGlobalUserByEmail(v.Db, userTable, email)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) IsUserUnique(userName, email string) (bool, error) {
|
||||||
|
return view.IsUserUnique(v.Db, userTable, userName, email)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) UserMfas(userID string) ([]*usr_model.MultiFactor, error) {
|
||||||
|
return view.UserMfas(v.Db, userTable, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) PutUser(user *model.UserView) error {
|
||||||
|
err := view.PutUser(v.Db, userTable, user)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return v.ProcessedUserSequence(user.Sequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) DeleteUser(userID string, eventSequence uint64) error {
|
||||||
|
err := view.DeleteUser(v.Db, userTable, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return v.ProcessedUserSequence(eventSequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) GetLatestUserSequence() (uint64, error) {
|
||||||
|
return v.latestSequence(userTable)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) ProcessedUserSequence(eventSequence uint64) error {
|
||||||
|
return v.saveCurrentSequence(userTable, eventSequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) GetLatestUserFailedEvent(sequence uint64) (*global_view.FailedEvent, error) {
|
||||||
|
return v.latestFailedEvent(userTable, sequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) ProcessedUserFailedEvent(failedEvent *global_view.FailedEvent) error {
|
||||||
|
return v.saveFailedEvent(failedEvent)
|
||||||
|
}
|
55
internal/auth/repository/eventsourcing/view/user_session.go
Normal file
55
internal/auth/repository/eventsourcing/view/user_session.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package view
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/caos/zitadel/internal/user/repository/view"
|
||||||
|
"github.com/caos/zitadel/internal/user/repository/view/model"
|
||||||
|
global_view "github.com/caos/zitadel/internal/view"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
userSessionTable = "auth.user_sessions"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v *View) UserSessionByID(sessionID string) (*model.UserSessionView, error) {
|
||||||
|
return view.UserSessionByID(v.Db, userSessionTable, sessionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) UserSessionByIDs(agentID, userID string) (*model.UserSessionView, error) {
|
||||||
|
return view.UserSessionByIDs(v.Db, userSessionTable, agentID, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) UserSessionsByAgentID(agentID string) ([]*model.UserSessionView, error) {
|
||||||
|
return view.UserSessionsByAgentID(v.Db, userSessionTable, agentID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) PutUserSession(userSession *model.UserSessionView) error {
|
||||||
|
err := view.PutUserSession(v.Db, userSessionTable, userSession)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return v.ProcessedUserSessionSequence(userSession.Sequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) DeleteUserSession(sessionID string, eventSequence uint64) error {
|
||||||
|
err := view.DeleteUserSession(v.Db, userSessionTable, sessionID)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return v.ProcessedUserSessionSequence(eventSequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) GetLatestUserSessionSequence() (uint64, error) {
|
||||||
|
return v.latestSequence(userSessionTable)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) ProcessedUserSessionSequence(eventSequence uint64) error {
|
||||||
|
return v.saveCurrentSequence(userSessionTable, eventSequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) GetLatestUserSessionFailedEvent(sequence uint64) (*global_view.FailedEvent, error) {
|
||||||
|
return v.latestFailedEvent(userSessionTable, sequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) ProcessedUserSessionFailedEvent(failedEvent *global_view.FailedEvent) error {
|
||||||
|
return v.saveFailedEvent(failedEvent)
|
||||||
|
}
|
25
internal/auth/repository/eventsourcing/view/view.go
Normal file
25
internal/auth/repository/eventsourcing/view/view.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package view
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type View struct {
|
||||||
|
Db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartView(sqlClient *sql.DB) (*View, error) {
|
||||||
|
gorm, err := gorm.Open("postgres", sqlClient)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &View{
|
||||||
|
Db: gorm,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) Health() (err error) {
|
||||||
|
return v.Db.DB().Ping()
|
||||||
|
}
|
12
internal/auth/repository/repository.go
Normal file
12
internal/auth/repository/repository.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Repository interface {
|
||||||
|
Health(context.Context) error
|
||||||
|
UserRepository
|
||||||
|
AuthRequestRepository
|
||||||
|
TokenRepository
|
||||||
|
}
|
13
internal/auth/repository/token.go
Normal file
13
internal/auth/repository/token.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/token/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TokenRepository interface {
|
||||||
|
CreateToken(ctx context.Context, agentID, applicationID, userID string, lifetime time.Duration) (*model.Token, error)
|
||||||
|
IsTokenValid(ctx context.Context, tokenID string) (bool, error)
|
||||||
|
}
|
42
internal/auth/repository/user.go
Normal file
42
internal/auth/repository/user.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/user/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserRepository interface {
|
||||||
|
Register(ctx context.Context, user *model.User, resourceOwner string) (*model.User, error)
|
||||||
|
|
||||||
|
myUserRepo
|
||||||
|
SkipMfaInit(ctx context.Context, userID string) error
|
||||||
|
RequestPasswordReset(ctx context.Context, username string) error
|
||||||
|
SetPassword(ctx context.Context, userID, code, password string) error
|
||||||
|
|
||||||
|
SignOut(ctx context.Context, agentID, userID string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type myUserRepo interface {
|
||||||
|
MyProfile(ctx context.Context) (*model.Profile, error)
|
||||||
|
ChangeMyProfile(ctx context.Context, profile *model.Profile) (*model.Profile, error)
|
||||||
|
|
||||||
|
MyEmail(ctx context.Context) (*model.Email, error)
|
||||||
|
ChangeMyEmail(ctx context.Context, email *model.Email) (*model.Email, error)
|
||||||
|
VerifyMyEmail(ctx context.Context, code string) error
|
||||||
|
ResendMyEmailVerificationMail(ctx context.Context) error
|
||||||
|
|
||||||
|
MyPhone(ctx context.Context) (*model.Phone, error)
|
||||||
|
ChangeMyPhone(ctx context.Context, phone *model.Phone) (*model.Phone, error)
|
||||||
|
VerifyMyPhone(ctx context.Context, code string) error
|
||||||
|
ResendMyPhoneVerificationCode(ctx context.Context) error
|
||||||
|
|
||||||
|
MyAddress(ctx context.Context) (*model.Address, error)
|
||||||
|
ChangeMyAddress(ctx context.Context, address *model.Address) (*model.Address, error)
|
||||||
|
|
||||||
|
ChangeMyPassword(ctx context.Context, old, new string) error
|
||||||
|
|
||||||
|
AddMyMfaOTP(ctx context.Context) (*model.OTP, error)
|
||||||
|
VerifyMyMfaOTP(ctx context.Context, code string) error
|
||||||
|
RemoveMyMfaOTP(ctx context.Context) error
|
||||||
|
}
|
82
internal/auth_request/model/auth_request.go
Normal file
82
internal/auth_request/model/auth_request.go
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthRequest struct {
|
||||||
|
ID string
|
||||||
|
AgentID string
|
||||||
|
CreationDate time.Time
|
||||||
|
ChangeDate time.Time
|
||||||
|
BrowserInfo *BrowserInfo
|
||||||
|
ApplicationID string
|
||||||
|
CallbackURI string
|
||||||
|
TransferState string
|
||||||
|
Prompt Prompt
|
||||||
|
PossibleLOAs []LevelOfAssurance
|
||||||
|
UiLocales []string
|
||||||
|
LoginHint string
|
||||||
|
PreselectedUserID string
|
||||||
|
MaxAuthAge uint32
|
||||||
|
Request Request
|
||||||
|
|
||||||
|
levelOfAssurance LevelOfAssurance
|
||||||
|
projectApplicationIDs []string
|
||||||
|
UserID string
|
||||||
|
PossibleSteps []NextStep
|
||||||
|
}
|
||||||
|
|
||||||
|
type Prompt int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
PromptUnspecified Prompt = iota
|
||||||
|
PromptNone
|
||||||
|
PromptLogin
|
||||||
|
PromptConsent
|
||||||
|
PromptSelectAccount
|
||||||
|
)
|
||||||
|
|
||||||
|
type LevelOfAssurance int
|
||||||
|
|
||||||
|
const (
|
||||||
|
LevelOfAssuranceNone LevelOfAssurance = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewAuthRequest(id, agentID string, info *BrowserInfo, applicationID, callbackURI, transferState string,
|
||||||
|
prompt Prompt, possibleLOAs []LevelOfAssurance, uiLocales []string, loginHint, preselectedUserID string, maxAuthAge uint32, request Request) *AuthRequest {
|
||||||
|
return &AuthRequest{
|
||||||
|
ID: id,
|
||||||
|
AgentID: agentID,
|
||||||
|
BrowserInfo: info,
|
||||||
|
ApplicationID: applicationID,
|
||||||
|
CallbackURI: callbackURI,
|
||||||
|
TransferState: transferState,
|
||||||
|
Prompt: prompt,
|
||||||
|
PossibleLOAs: possibleLOAs,
|
||||||
|
UiLocales: uiLocales,
|
||||||
|
LoginHint: loginHint,
|
||||||
|
PreselectedUserID: preselectedUserID,
|
||||||
|
MaxAuthAge: maxAuthAge,
|
||||||
|
Request: request,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthRequest) IsValid() bool {
|
||||||
|
return a.ID != "" &&
|
||||||
|
a.AgentID != "" &&
|
||||||
|
a.BrowserInfo != nil && a.BrowserInfo.IsValid() &&
|
||||||
|
a.ApplicationID != "" &&
|
||||||
|
a.CallbackURI != "" &&
|
||||||
|
a.Request != nil && a.Request.IsValid()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthRequest) MfaLevel() MfaLevel {
|
||||||
|
return -1
|
||||||
|
//PLANNED: check a.PossibleLOAs (and Prompt Login?)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthRequest) WithCurrentInfo(info *BrowserInfo) *AuthRequest {
|
||||||
|
a.BrowserInfo = info
|
||||||
|
return a
|
||||||
|
}
|
263
internal/auth_request/model/auth_request_test.go
Normal file
263
internal/auth_request/model/auth_request_test.go
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAuthRequest_IsValid(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
ID string
|
||||||
|
AgentID string
|
||||||
|
BrowserInfo *BrowserInfo
|
||||||
|
ApplicationID string
|
||||||
|
CallbackURI string
|
||||||
|
Request Request
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"missing id, false",
|
||||||
|
fields{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"missing agent id, false",
|
||||||
|
fields{
|
||||||
|
ID: "id",
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"missing browser info, false",
|
||||||
|
fields{
|
||||||
|
ID: "id",
|
||||||
|
AgentID: "agentID",
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"browser info invalid, false",
|
||||||
|
fields{
|
||||||
|
ID: "id",
|
||||||
|
AgentID: "agentID",
|
||||||
|
BrowserInfo: &BrowserInfo{},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"missing application id, false",
|
||||||
|
fields{
|
||||||
|
ID: "id",
|
||||||
|
AgentID: "agentID",
|
||||||
|
BrowserInfo: &BrowserInfo{
|
||||||
|
UserAgent: "user agent",
|
||||||
|
AcceptLanguage: "accept language",
|
||||||
|
RemoteIP: net.IPv4(29, 4, 20, 19),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"missing callback uri, false",
|
||||||
|
fields{
|
||||||
|
ID: "id",
|
||||||
|
AgentID: "agentID",
|
||||||
|
BrowserInfo: &BrowserInfo{
|
||||||
|
UserAgent: "user agent",
|
||||||
|
AcceptLanguage: "accept language",
|
||||||
|
RemoteIP: net.IPv4(29, 4, 20, 19),
|
||||||
|
},
|
||||||
|
ApplicationID: "appID",
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"missing request, false",
|
||||||
|
fields{
|
||||||
|
ID: "id",
|
||||||
|
AgentID: "agentID",
|
||||||
|
BrowserInfo: &BrowserInfo{
|
||||||
|
UserAgent: "user agent",
|
||||||
|
AcceptLanguage: "accept language",
|
||||||
|
RemoteIP: net.IPv4(29, 4, 20, 19),
|
||||||
|
},
|
||||||
|
ApplicationID: "appID",
|
||||||
|
CallbackURI: "schema://callback",
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"request invalid, false",
|
||||||
|
fields{
|
||||||
|
ID: "id",
|
||||||
|
AgentID: "agentID",
|
||||||
|
BrowserInfo: &BrowserInfo{
|
||||||
|
UserAgent: "user agent",
|
||||||
|
AcceptLanguage: "accept language",
|
||||||
|
RemoteIP: net.IPv4(29, 4, 20, 19),
|
||||||
|
},
|
||||||
|
ApplicationID: "appID",
|
||||||
|
CallbackURI: "schema://callback",
|
||||||
|
Request: &AuthRequestOIDC{},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"valid auth request, true",
|
||||||
|
fields{
|
||||||
|
ID: "id",
|
||||||
|
AgentID: "agentID",
|
||||||
|
BrowserInfo: &BrowserInfo{
|
||||||
|
UserAgent: "user agent",
|
||||||
|
AcceptLanguage: "accept language",
|
||||||
|
RemoteIP: net.IPv4(29, 4, 20, 19),
|
||||||
|
},
|
||||||
|
ApplicationID: "appID",
|
||||||
|
CallbackURI: "schema://callback",
|
||||||
|
Request: &AuthRequestOIDC{
|
||||||
|
Scopes: []string{"openid"},
|
||||||
|
CodeChallenge: &OIDCCodeChallenge{
|
||||||
|
Challenge: "challenge",
|
||||||
|
Method: CodeChallengeMethodS256,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
a := &AuthRequest{
|
||||||
|
ID: tt.fields.ID,
|
||||||
|
AgentID: tt.fields.AgentID,
|
||||||
|
BrowserInfo: tt.fields.BrowserInfo,
|
||||||
|
ApplicationID: tt.fields.ApplicationID,
|
||||||
|
CallbackURI: tt.fields.CallbackURI,
|
||||||
|
Request: tt.fields.Request,
|
||||||
|
}
|
||||||
|
if got := a.IsValid(); got != tt.want {
|
||||||
|
t.Errorf("IsValid() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthRequest_MfaLevel(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
Prompt Prompt
|
||||||
|
PossibleLOAs []LevelOfAssurance
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
want MfaLevel
|
||||||
|
}{
|
||||||
|
//PLANNED: Add / replace test cases when LOA is set
|
||||||
|
{"-1",
|
||||||
|
fields{},
|
||||||
|
-1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
a := &AuthRequest{
|
||||||
|
Prompt: tt.fields.Prompt,
|
||||||
|
PossibleLOAs: tt.fields.PossibleLOAs,
|
||||||
|
}
|
||||||
|
if got := a.MfaLevel(); got != tt.want {
|
||||||
|
t.Errorf("MfaLevel() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthRequest_WithCurrentInfo(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
ID string
|
||||||
|
AgentID string
|
||||||
|
BrowserInfo *BrowserInfo
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
info *BrowserInfo
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
want *AuthRequest
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"unchanged",
|
||||||
|
fields{
|
||||||
|
ID: "id",
|
||||||
|
AgentID: "agentID",
|
||||||
|
BrowserInfo: &BrowserInfo{
|
||||||
|
UserAgent: "ua",
|
||||||
|
AcceptLanguage: "de",
|
||||||
|
RemoteIP: net.IPv4(29, 4, 20, 19),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
&BrowserInfo{
|
||||||
|
UserAgent: "ua",
|
||||||
|
AcceptLanguage: "de",
|
||||||
|
RemoteIP: net.IPv4(29, 4, 20, 19),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&AuthRequest{
|
||||||
|
ID: "id",
|
||||||
|
AgentID: "agentID",
|
||||||
|
BrowserInfo: &BrowserInfo{
|
||||||
|
UserAgent: "ua",
|
||||||
|
AcceptLanguage: "de",
|
||||||
|
RemoteIP: net.IPv4(29, 4, 20, 19),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changed",
|
||||||
|
fields{
|
||||||
|
ID: "id",
|
||||||
|
AgentID: "agentID",
|
||||||
|
BrowserInfo: &BrowserInfo{
|
||||||
|
UserAgent: "ua",
|
||||||
|
AcceptLanguage: "de",
|
||||||
|
RemoteIP: net.IPv4(29, 4, 20, 19),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
&BrowserInfo{
|
||||||
|
UserAgent: "ua",
|
||||||
|
AcceptLanguage: "de",
|
||||||
|
RemoteIP: net.IPv4(16, 12, 20, 19),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&AuthRequest{
|
||||||
|
ID: "id",
|
||||||
|
AgentID: "agentID",
|
||||||
|
BrowserInfo: &BrowserInfo{
|
||||||
|
UserAgent: "ua",
|
||||||
|
AcceptLanguage: "de",
|
||||||
|
RemoteIP: net.IPv4(16, 12, 20, 19),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
a := &AuthRequest{
|
||||||
|
ID: tt.fields.ID,
|
||||||
|
AgentID: tt.fields.AgentID,
|
||||||
|
BrowserInfo: tt.fields.BrowserInfo,
|
||||||
|
}
|
||||||
|
if got := a.WithCurrentInfo(tt.args.info); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("WithCurrentInfo() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
15
internal/auth_request/model/browser_info.go
Normal file
15
internal/auth_request/model/browser_info.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
type BrowserInfo struct {
|
||||||
|
UserAgent string
|
||||||
|
AcceptLanguage string
|
||||||
|
RemoteIP net.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *BrowserInfo) IsValid() bool {
|
||||||
|
return i.UserAgent != "" &&
|
||||||
|
i.AcceptLanguage != "" &&
|
||||||
|
i.RemoteIP != nil && !i.RemoteIP.IsUnspecified()
|
||||||
|
}
|
17
internal/auth_request/model/code_challenge.go
Normal file
17
internal/auth_request/model/code_challenge.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type OIDCCodeChallenge struct {
|
||||||
|
Challenge string
|
||||||
|
Method OIDCCodeChallengeMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *OIDCCodeChallenge) IsValid() bool {
|
||||||
|
return c.Challenge != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type OIDCCodeChallengeMethod int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
CodeChallengeMethodPlain OIDCCodeChallengeMethod = iota
|
||||||
|
CodeChallengeMethodS256
|
||||||
|
)
|
117
internal/auth_request/model/next_step.go
Normal file
117
internal/auth_request/model/next_step.go
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type NextStep interface {
|
||||||
|
Type() NextStepType
|
||||||
|
}
|
||||||
|
|
||||||
|
type NextStepType int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
NextStepUnspecified NextStepType = iota
|
||||||
|
NextStepLogin
|
||||||
|
NextStepUserSelection
|
||||||
|
NextStepPassword
|
||||||
|
NextStepChangePassword
|
||||||
|
NextStepInitPassword
|
||||||
|
NextStepVerifyEmail
|
||||||
|
NextStepMfaPrompt
|
||||||
|
NextStepMfaVerify
|
||||||
|
NextStepRedirectToCallback
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserSessionState int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
UserSessionStateActive UserSessionState = iota
|
||||||
|
UserSessionStateTerminated
|
||||||
|
)
|
||||||
|
|
||||||
|
type LoginStep struct {
|
||||||
|
NotFound bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LoginStep) Type() NextStepType {
|
||||||
|
return NextStepLogin
|
||||||
|
}
|
||||||
|
|
||||||
|
type SelectUserStep struct {
|
||||||
|
Users []UserSelection
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SelectUserStep) Type() NextStepType {
|
||||||
|
return NextStepUserSelection
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserSelection struct {
|
||||||
|
UserID string
|
||||||
|
UserName string
|
||||||
|
UserSessionState UserSessionState
|
||||||
|
}
|
||||||
|
|
||||||
|
type PasswordStep struct {
|
||||||
|
FailureCount uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PasswordStep) Type() NextStepType {
|
||||||
|
return NextStepPassword
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChangePasswordStep struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ChangePasswordStep) Type() NextStepType {
|
||||||
|
return NextStepChangePassword
|
||||||
|
}
|
||||||
|
|
||||||
|
type InitPasswordStep struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *InitPasswordStep) Type() NextStepType {
|
||||||
|
return NextStepInitPassword
|
||||||
|
}
|
||||||
|
|
||||||
|
type VerifyEMailStep struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *VerifyEMailStep) Type() NextStepType {
|
||||||
|
return NextStepVerifyEmail
|
||||||
|
}
|
||||||
|
|
||||||
|
type MfaPromptStep struct {
|
||||||
|
Required bool
|
||||||
|
MfaProviders []MfaType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MfaPromptStep) Type() NextStepType {
|
||||||
|
return NextStepMfaPrompt
|
||||||
|
}
|
||||||
|
|
||||||
|
type MfaVerificationStep struct {
|
||||||
|
FailureCount uint16
|
||||||
|
MfaProviders []MfaType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MfaVerificationStep) Type() NextStepType {
|
||||||
|
return NextStepMfaVerify
|
||||||
|
}
|
||||||
|
|
||||||
|
type RedirectToCallbackStep struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedirectToCallbackStep) Type() NextStepType {
|
||||||
|
return NextStepRedirectToCallback
|
||||||
|
}
|
||||||
|
|
||||||
|
type MfaType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
MfaTypeOTP MfaType = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
type MfaLevel int
|
||||||
|
|
||||||
|
const (
|
||||||
|
MfaLevelSoftware MfaLevel = iota
|
||||||
|
MfaLevelHardware
|
||||||
|
MfaLevelHardwareCertified
|
||||||
|
)
|
48
internal/auth_request/model/request.go
Normal file
48
internal/auth_request/model/request.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type Request interface {
|
||||||
|
Type() AuthRequestType
|
||||||
|
IsValid() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthRequestType int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
AuthRequestTypeOIDC AuthRequestType = iota
|
||||||
|
AuthRequestTypeSAML
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthRequestOIDC struct {
|
||||||
|
Scopes []string
|
||||||
|
ResponseType OIDCResponseType
|
||||||
|
Nonce string
|
||||||
|
CodeChallenge *OIDCCodeChallenge
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthRequestOIDC) Type() AuthRequestType {
|
||||||
|
return AuthRequestTypeOIDC
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthRequestOIDC) IsValid() bool {
|
||||||
|
return len(a.Scopes) > 0 &&
|
||||||
|
a.CodeChallenge == nil || a.CodeChallenge != nil && a.CodeChallenge.IsValid()
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthRequestSAML struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthRequestSAML) Type() AuthRequestType {
|
||||||
|
return AuthRequestTypeSAML
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthRequestSAML) IsValid() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type OIDCResponseType int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
OIDCResponseTypeCode OIDCResponseType = iota
|
||||||
|
OIDCResponseTypeIdToken
|
||||||
|
OIDCResponseTypeToken
|
||||||
|
)
|
67
internal/auth_request/repository/cache/cache.go
vendored
Normal file
67
internal/auth_request/repository/cache/cache.go
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/auth_request/model"
|
||||||
|
"github.com/caos/zitadel/internal/config/types"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Connection types.SQL
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthRequestCache struct {
|
||||||
|
client *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func Start(conf Config) (*AuthRequestCache, error) {
|
||||||
|
client, err := sql.Open("postgres", conf.Connection.ConnectionString())
|
||||||
|
if err != nil {
|
||||||
|
return nil, caos_errs.ThrowPreconditionFailed(err, "SQL-9qBtr", "unable to open database connection")
|
||||||
|
}
|
||||||
|
return &AuthRequestCache{
|
||||||
|
client: client,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AuthRequestCache) Health(ctx context.Context) error {
|
||||||
|
return c.client.PingContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AuthRequestCache) GetAuthRequestByID(_ context.Context, id string) (*model.AuthRequest, error) {
|
||||||
|
var b []byte
|
||||||
|
err := c.client.QueryRow("SELECT request FROM auth.authrequests WHERE id = ?", id).Scan(&b)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, caos_errs.ThrowNotFound(err, "CACHE-d24aD", "auth request not found")
|
||||||
|
}
|
||||||
|
return nil, caos_errs.ThrowInternal(err, "CACHE-as3kj", "unable to get auth request from database")
|
||||||
|
}
|
||||||
|
request := new(model.AuthRequest)
|
||||||
|
err = json.Unmarshal(b, &request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, caos_errs.ThrowInternal(err, "CACHE-2wshg", "unable to unmarshal auth request")
|
||||||
|
}
|
||||||
|
return request, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AuthRequestCache) SaveAuthRequest(_ context.Context, request *model.AuthRequest) error {
|
||||||
|
b, err := json.Marshal(request)
|
||||||
|
if err != nil {
|
||||||
|
return caos_errs.ThrowInternal(err, "CACHE-32FH9", "unable to marshal auth request")
|
||||||
|
}
|
||||||
|
stmt, err := c.client.Prepare("INSERT INTO auth.authrequests (id, request) VALUES($1, $2)")
|
||||||
|
if err != nil {
|
||||||
|
return caos_errs.ThrowInternal(err, "CACHE-dswfF", "sql prepare failed")
|
||||||
|
}
|
||||||
|
_, err = stmt.Exec(request.ID, b)
|
||||||
|
if err != nil {
|
||||||
|
return caos_errs.ThrowInternal(err, "CACHE-sw4af", "unable to save auth request")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
3
internal/auth_request/repository/gen_mock.go
Normal file
3
internal/auth_request/repository/gen_mock.go
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
//go:generate mockgen -package mock -destination ./mock/repository.mock.go github.com/caos/zitadel/internal/auth_request/repository Repository
|
12
internal/auth_request/repository/mock/repository.go
Normal file
12
internal/auth_request/repository/mock/repository.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/auth_request/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewMockAuthRequestRepository(ctrl *gomock.Controller) repository.Repository {
|
||||||
|
repo := NewMockRepository(ctrl)
|
||||||
|
return repo
|
||||||
|
}
|
79
internal/auth_request/repository/mock/repository.mock.go
Normal file
79
internal/auth_request/repository/mock/repository.mock.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/caos/zitadel/internal/auth_request/repository (interfaces: Repository)
|
||||||
|
|
||||||
|
// Package mock is a generated GoMock package.
|
||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
model "github.com/caos/zitadel/internal/auth_request/model"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockRepository is a mock of Repository interface
|
||||||
|
type MockRepository struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockRepositoryMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockRepositoryMockRecorder is the mock recorder for MockRepository
|
||||||
|
type MockRepositoryMockRecorder struct {
|
||||||
|
mock *MockRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockRepository creates a new mock instance
|
||||||
|
func NewMockRepository(ctrl *gomock.Controller) *MockRepository {
|
||||||
|
mock := &MockRepository{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockRepositoryMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
|
func (m *MockRepository) EXPECT() *MockRepositoryMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAuthRequestByID mocks base method
|
||||||
|
func (m *MockRepository) GetAuthRequestByID(arg0 context.Context, arg1 string) (*model.AuthRequest, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetAuthRequestByID", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(*model.AuthRequest)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAuthRequestByID indicates an expected call of GetAuthRequestByID
|
||||||
|
func (mr *MockRepositoryMockRecorder) GetAuthRequestByID(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAuthRequestByID", reflect.TypeOf((*MockRepository)(nil).GetAuthRequestByID), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Health mocks base method
|
||||||
|
func (m *MockRepository) Health(arg0 context.Context) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Health", arg0)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Health indicates an expected call of Health
|
||||||
|
func (mr *MockRepositoryMockRecorder) Health(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Health", reflect.TypeOf((*MockRepository)(nil).Health), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveAuthRequest mocks base method
|
||||||
|
func (m *MockRepository) SaveAuthRequest(arg0 context.Context, arg1 string) (*model.AuthRequest, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SaveAuthRequest", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(*model.AuthRequest)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveAuthRequest indicates an expected call of SaveAuthRequest
|
||||||
|
func (mr *MockRepositoryMockRecorder) SaveAuthRequest(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAuthRequest", reflect.TypeOf((*MockRepository)(nil).SaveAuthRequest), arg0, arg1)
|
||||||
|
}
|
14
internal/auth_request/repository/repository.go
Normal file
14
internal/auth_request/repository/repository.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/auth_request/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Repository interface {
|
||||||
|
Health(ctx context.Context) error
|
||||||
|
|
||||||
|
GetAuthRequestByID(ctx context.Context, id string) (*model.AuthRequest, error)
|
||||||
|
SaveAuthRequest(ctx context.Context, id string) (*model.AuthRequest, error)
|
||||||
|
}
|
@@ -7,12 +7,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type SystemDefaults struct {
|
type SystemDefaults struct {
|
||||||
SecretGenerators SecretGenerators
|
SecretGenerators SecretGenerators
|
||||||
UserVerificationKey *crypto.KeyConfig
|
UserVerificationKey *crypto.KeyConfig
|
||||||
Multifactors MultifactorConfig
|
Multifactors MultifactorConfig
|
||||||
DefaultPolicies DefaultPolicies
|
VerificationLifetimes VerificationLifetimes
|
||||||
IamID string
|
DefaultPolicies DefaultPolicies
|
||||||
SetUp types.IAMSetUp
|
IamID string
|
||||||
|
SetUp types.IAMSetUp
|
||||||
}
|
}
|
||||||
|
|
||||||
type SecretGenerators struct {
|
type SecretGenerators struct {
|
||||||
@@ -33,6 +34,13 @@ type OTPConfig struct {
|
|||||||
VerificationKey *crypto.KeyConfig
|
VerificationKey *crypto.KeyConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VerificationLifetimes struct {
|
||||||
|
PasswordCheck types.Duration
|
||||||
|
MfaInitSkip types.Duration
|
||||||
|
MfaSoftwareCheck types.Duration
|
||||||
|
MfaHardwareCheck types.Duration
|
||||||
|
}
|
||||||
|
|
||||||
type DefaultPolicies struct {
|
type DefaultPolicies struct {
|
||||||
Age pol.PasswordAgePolicyDefault
|
Age pol.PasswordAgePolicyDefault
|
||||||
Complexity pol.PasswordComplexityPolicyDefault
|
Complexity pol.PasswordComplexityPolicyDefault
|
||||||
|
@@ -27,8 +27,8 @@ func CreateMockEncryptionAlg(ctrl *gomock.Controller) EncryptionAlgorithm {
|
|||||||
return mCrypto
|
return mCrypto
|
||||||
}
|
}
|
||||||
|
|
||||||
func createMockHashAlg(t *testing.T) HashAlgorithm {
|
func CreateMockHashAlg(ctrl *gomock.Controller) HashAlgorithm {
|
||||||
mCrypto := NewMockHashAlgorithm(gomock.NewController(t))
|
mCrypto := NewMockHashAlgorithm(ctrl)
|
||||||
mCrypto.EXPECT().Algorithm().AnyTimes().Return("hash")
|
mCrypto.EXPECT().Algorithm().AnyTimes().Return("hash")
|
||||||
mCrypto.EXPECT().Hash(gomock.Any()).DoAndReturn(
|
mCrypto.EXPECT().Hash(gomock.Any()).DoAndReturn(
|
||||||
func(code []byte) ([]byte, error) {
|
func(code []byte) ([]byte, error) {
|
||||||
|
@@ -113,7 +113,7 @@ func TestVerifyCode(t *testing.T) {
|
|||||||
Crypted: []byte("code"),
|
Crypted: []byte("code"),
|
||||||
},
|
},
|
||||||
verificationCode: "code",
|
verificationCode: "code",
|
||||||
g: createMockGenerator(t, createMockHashAlg(t)),
|
g: createMockGenerator(t, CreateMockHashAlg(gomock.NewController(t))),
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
@@ -240,7 +240,7 @@ func Test_verifyHashedCode(t *testing.T) {
|
|||||||
args{
|
args{
|
||||||
cryptoCode: nil,
|
cryptoCode: nil,
|
||||||
verificationCode: "",
|
verificationCode: "",
|
||||||
alg: createMockHashAlg(t),
|
alg: CreateMockHashAlg(gomock.NewController(t)),
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
@@ -252,7 +252,7 @@ func Test_verifyHashedCode(t *testing.T) {
|
|||||||
Crypted: nil,
|
Crypted: nil,
|
||||||
},
|
},
|
||||||
verificationCode: "",
|
verificationCode: "",
|
||||||
alg: createMockHashAlg(t),
|
alg: CreateMockHashAlg(gomock.NewController(t)),
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
@@ -265,7 +265,7 @@ func Test_verifyHashedCode(t *testing.T) {
|
|||||||
Crypted: nil,
|
Crypted: nil,
|
||||||
},
|
},
|
||||||
verificationCode: "",
|
verificationCode: "",
|
||||||
alg: createMockHashAlg(t),
|
alg: CreateMockHashAlg(gomock.NewController(t)),
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
@@ -278,7 +278,7 @@ func Test_verifyHashedCode(t *testing.T) {
|
|||||||
Crypted: []byte("code"),
|
Crypted: []byte("code"),
|
||||||
},
|
},
|
||||||
verificationCode: "wrong",
|
verificationCode: "wrong",
|
||||||
alg: createMockHashAlg(t),
|
alg: CreateMockHashAlg(gomock.NewController(t)),
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
@@ -291,7 +291,7 @@ func Test_verifyHashedCode(t *testing.T) {
|
|||||||
Crypted: []byte("code"),
|
Crypted: []byte("code"),
|
||||||
},
|
},
|
||||||
verificationCode: "code",
|
verificationCode: "code",
|
||||||
alg: createMockHashAlg(t),
|
alg: CreateMockHashAlg(gomock.NewController(t)),
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
@@ -100,7 +100,7 @@ func Hash(value []byte, alg HashAlgorithm) (*CryptoValue, error) {
|
|||||||
|
|
||||||
func CompareHash(value *CryptoValue, comparer []byte, alg HashAlgorithm) error {
|
func CompareHash(value *CryptoValue, comparer []byte, alg HashAlgorithm) error {
|
||||||
if value.Algorithm != alg.Algorithm() {
|
if value.Algorithm != alg.Algorithm() {
|
||||||
return errors.ThrowInvalidArgument(nil, "CRYPT-HF32f", "value was hash with a different algorithm")
|
return errors.ThrowInvalidArgument(nil, "CRYPT-HF32f", "value was hashed with a different algorithm")
|
||||||
}
|
}
|
||||||
return alg.CompareHash(value.Crypted, comparer)
|
return alg.CompareHash(value.Crypted, comparer)
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
type filterFunc func(context.Context, *es_models.SearchQuery) ([]*es_models.Event, error)
|
type filterFunc func(context.Context, *es_models.SearchQuery) ([]*es_models.Event, error)
|
||||||
type appendFunc func(...*es_models.Event) error
|
type appendFunc func(...*es_models.Event) error
|
||||||
type aggregateFunc func(context.Context) (*es_models.Aggregate, error)
|
type AggregateFunc func(context.Context) (*es_models.Aggregate, error)
|
||||||
type pushFunc func(context.Context, ...*es_models.Aggregate) error
|
type pushFunc func(context.Context, ...*es_models.Aggregate) error
|
||||||
|
|
||||||
func Filter(ctx context.Context, filter filterFunc, appender appendFunc, query *es_models.SearchQuery) error {
|
func Filter(ctx context.Context, filter filterFunc, appender appendFunc, query *es_models.SearchQuery) error {
|
||||||
@@ -32,7 +32,7 @@ func Filter(ctx context.Context, filter filterFunc, appender appendFunc, query *
|
|||||||
// Push creates the aggregates from aggregater
|
// Push creates the aggregates from aggregater
|
||||||
// and pushes the aggregates to the given pushFunc
|
// and pushes the aggregates to the given pushFunc
|
||||||
// the given events are appended by the appender
|
// the given events are appended by the appender
|
||||||
func Push(ctx context.Context, push pushFunc, appender appendFunc, aggregaters ...aggregateFunc) (err error) {
|
func Push(ctx context.Context, push pushFunc, appender appendFunc, aggregaters ...AggregateFunc) (err error) {
|
||||||
if len(aggregaters) < 1 {
|
if len(aggregaters) < 1 {
|
||||||
return errors.ThrowPreconditionFailed(nil, "SDK-q9wjp", "no aggregaters passed")
|
return errors.ThrowPreconditionFailed(nil, "SDK-q9wjp", "no aggregaters passed")
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ func appendAggregates(appender appendFunc, aggregates []*models.Aggregate) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeAggregates(ctx context.Context, aggregaters []aggregateFunc) (aggregates []*models.Aggregate, err error) {
|
func makeAggregates(ctx context.Context, aggregaters []AggregateFunc) (aggregates []*models.Aggregate, err error) {
|
||||||
aggregates = make([]*models.Aggregate, len(aggregaters))
|
aggregates = make([]*models.Aggregate, len(aggregaters))
|
||||||
for i, aggregater := range aggregaters {
|
for i, aggregater := range aggregaters {
|
||||||
aggregates[i], err = aggregater(ctx)
|
aggregates[i], err = aggregater(ctx)
|
||||||
|
@@ -80,7 +80,7 @@ func TestPush(t *testing.T) {
|
|||||||
type args struct {
|
type args struct {
|
||||||
push pushFunc
|
push pushFunc
|
||||||
appender appendFunc
|
appender appendFunc
|
||||||
aggregaters []aggregateFunc
|
aggregaters []AggregateFunc
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -101,7 +101,7 @@ func TestPush(t *testing.T) {
|
|||||||
args: args{
|
args: args{
|
||||||
push: nil,
|
push: nil,
|
||||||
appender: nil,
|
appender: nil,
|
||||||
aggregaters: []aggregateFunc{
|
aggregaters: []AggregateFunc{
|
||||||
func(context.Context) (*es_models.Aggregate, error) {
|
func(context.Context) (*es_models.Aggregate, error) {
|
||||||
return nil, errors.ThrowInternal(nil, "SDK-Ec5x2", "test err")
|
return nil, errors.ThrowInternal(nil, "SDK-Ec5x2", "test err")
|
||||||
},
|
},
|
||||||
@@ -116,7 +116,7 @@ func TestPush(t *testing.T) {
|
|||||||
return errors.ThrowInternal(nil, "SDK-0g4gW", "test error")
|
return errors.ThrowInternal(nil, "SDK-0g4gW", "test error")
|
||||||
},
|
},
|
||||||
appender: nil,
|
appender: nil,
|
||||||
aggregaters: []aggregateFunc{
|
aggregaters: []AggregateFunc{
|
||||||
func(context.Context) (*es_models.Aggregate, error) {
|
func(context.Context) (*es_models.Aggregate, error) {
|
||||||
return &es_models.Aggregate{}, nil
|
return &es_models.Aggregate{}, nil
|
||||||
},
|
},
|
||||||
@@ -133,7 +133,7 @@ func TestPush(t *testing.T) {
|
|||||||
appender: func(...*es_models.Event) error {
|
appender: func(...*es_models.Event) error {
|
||||||
return errors.ThrowInvalidArgument(nil, "SDK-BDhcT", "test err")
|
return errors.ThrowInvalidArgument(nil, "SDK-BDhcT", "test err")
|
||||||
},
|
},
|
||||||
aggregaters: []aggregateFunc{
|
aggregaters: []AggregateFunc{
|
||||||
func(context.Context) (*es_models.Aggregate, error) {
|
func(context.Context) (*es_models.Aggregate, error) {
|
||||||
return &es_models.Aggregate{Events: []*es_models.Event{&es_models.Event{}}}, nil
|
return &es_models.Aggregate{Events: []*es_models.Event{&es_models.Event{}}}, nil
|
||||||
},
|
},
|
||||||
@@ -150,7 +150,7 @@ func TestPush(t *testing.T) {
|
|||||||
appender: func(...*es_models.Event) error {
|
appender: func(...*es_models.Event) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
aggregaters: []aggregateFunc{
|
aggregaters: []AggregateFunc{
|
||||||
func(context.Context) (*es_models.Aggregate, error) {
|
func(context.Context) (*es_models.Aggregate, error) {
|
||||||
return &es_models.Aggregate{Events: []*es_models.Event{&es_models.Event{}}}, nil
|
return &es_models.Aggregate{Events: []*es_models.Event{&es_models.Event{}}}, nil
|
||||||
},
|
},
|
||||||
@@ -167,7 +167,7 @@ func TestPush(t *testing.T) {
|
|||||||
appender: func(...*es_models.Event) error {
|
appender: func(...*es_models.Event) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
aggregaters: []aggregateFunc{
|
aggregaters: []AggregateFunc{
|
||||||
func(context.Context) (*es_models.Aggregate, error) {
|
func(context.Context) (*es_models.Aggregate, error) {
|
||||||
return &es_models.Aggregate{Events: []*es_models.Event{&es_models.Event{}}}, nil
|
return &es_models.Aggregate{Events: []*es_models.Event{&es_models.Event{}}}, nil
|
||||||
},
|
},
|
||||||
|
3
internal/id/gen_mock.go
Normal file
3
internal/id/gen_mock.go
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package id
|
||||||
|
|
||||||
|
//go:generate mockgen -package mock -destination ./mock/generator.mock.go github.com/caos/zitadel/internal/id Generator
|
5
internal/id/id_generator.go
Normal file
5
internal/id/id_generator.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package id
|
||||||
|
|
||||||
|
type Generator interface {
|
||||||
|
Next() (string, error)
|
||||||
|
}
|
48
internal/id/mock/generator.mock.go
Normal file
48
internal/id/mock/generator.mock.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/caos/zitadel/internal/id (interfaces: Generator)
|
||||||
|
|
||||||
|
// Package mock is a generated GoMock package.
|
||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockGenerator is a mock of Generator interface
|
||||||
|
type MockGenerator struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockGeneratorMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockGeneratorMockRecorder is the mock recorder for MockGenerator
|
||||||
|
type MockGeneratorMockRecorder struct {
|
||||||
|
mock *MockGenerator
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockGenerator creates a new mock instance
|
||||||
|
func NewMockGenerator(ctrl *gomock.Controller) *MockGenerator {
|
||||||
|
mock := &MockGenerator{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockGeneratorMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
|
func (m *MockGenerator) EXPECT() *MockGeneratorMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next mocks base method
|
||||||
|
func (m *MockGenerator) Next() (string, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Next")
|
||||||
|
ret0, _ := ret[0].(string)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next indicates an expected call of Next
|
||||||
|
func (mr *MockGeneratorMockRecorder) Next() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Next", reflect.TypeOf((*MockGenerator)(nil).Next))
|
||||||
|
}
|
28
internal/id/sonyflake.go
Normal file
28
internal/id/sonyflake.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package id
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sony/sonyflake"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sonyflakeGenerator struct {
|
||||||
|
*sonyflake.Sonyflake
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sonyflakeGenerator) Next() (string, error) {
|
||||||
|
id, err := s.NextID()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return strconv.FormatUint(id, 10), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
SonyFlakeGenerator = Generator(&sonyflakeGenerator{
|
||||||
|
sonyflake.NewSonyflake(sonyflake.Settings{
|
||||||
|
StartTime: time.Date(2019, 4, 29, 0, 0, 0, 0, time.UTC),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
87
internal/management/repository/eventsourcing/user.go
Normal file
87
internal/management/repository/eventsourcing/user.go
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package eventsourcing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||||
|
usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserRepo struct {
|
||||||
|
UserEvents *usr_event.UserEventstore
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) UserByID(ctx context.Context, id string) (project *usr_model.User, err error) {
|
||||||
|
return repo.UserEvents.UserByID(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) CreateUser(ctx context.Context, user *usr_model.User) (*usr_model.User, error) {
|
||||||
|
return repo.UserEvents.CreateUser(ctx, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) RegisterUser(ctx context.Context, user *usr_model.User, resourceOwner string) (*usr_model.User, error) {
|
||||||
|
return repo.UserEvents.RegisterUser(ctx, user, resourceOwner)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) DeactivateUser(ctx context.Context, id string) (*usr_model.User, error) {
|
||||||
|
return repo.UserEvents.DeactivateUser(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) ReactivateUser(ctx context.Context, id string) (*usr_model.User, error) {
|
||||||
|
return repo.UserEvents.ReactivateUser(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) LockUser(ctx context.Context, id string) (*usr_model.User, error) {
|
||||||
|
return repo.UserEvents.LockUser(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) UnlockUser(ctx context.Context, id string) (*usr_model.User, error) {
|
||||||
|
return repo.UserEvents.UnlockUser(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) SetOneTimePassword(ctx context.Context, password *usr_model.Password) (*usr_model.Password, error) {
|
||||||
|
return repo.UserEvents.SetOneTimePassword(ctx, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) RequestSetPassword(ctx context.Context, id string, notifyType usr_model.NotificationType) error {
|
||||||
|
return repo.UserEvents.RequestSetPassword(ctx, id, notifyType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) ProfileByID(ctx context.Context, userID string) (*usr_model.Profile, error) {
|
||||||
|
return repo.UserEvents.ProfileByID(ctx, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) ChangeProfile(ctx context.Context, profile *usr_model.Profile) (*usr_model.Profile, error) {
|
||||||
|
return repo.UserEvents.ChangeProfile(ctx, profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) EmailByID(ctx context.Context, userID string) (*usr_model.Email, error) {
|
||||||
|
return repo.UserEvents.EmailByID(ctx, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) ChangeEmail(ctx context.Context, email *usr_model.Email) (*usr_model.Email, error) {
|
||||||
|
return repo.UserEvents.ChangeEmail(ctx, email)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) CreateEmailVerificationCode(ctx context.Context, userID string) error {
|
||||||
|
return repo.UserEvents.CreateEmailVerificationCode(ctx, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) PhoneByID(ctx context.Context, userID string) (*usr_model.Phone, error) {
|
||||||
|
return repo.UserEvents.PhoneByID(ctx, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) ChangePhone(ctx context.Context, email *usr_model.Phone) (*usr_model.Phone, error) {
|
||||||
|
return repo.UserEvents.ChangePhone(ctx, email)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) CreatePhoneVerificationCode(ctx context.Context, userID string) error {
|
||||||
|
return repo.UserEvents.CreatePhoneVerificationCode(ctx, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) AddressByID(ctx context.Context, userID string) (*usr_model.Address, error) {
|
||||||
|
return repo.UserEvents.AddressByID(ctx, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) ChangeAddress(ctx context.Context, address *usr_model.Address) (*usr_model.Address, error) {
|
||||||
|
return repo.UserEvents.ChangeAddress(ctx, address)
|
||||||
|
}
|
35
internal/management/repository/eventsourcing/user_grant.go
Normal file
35
internal/management/repository/eventsourcing/user_grant.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package eventsourcing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
grant_model "github.com/caos/zitadel/internal/usergrant/model"
|
||||||
|
grant_event "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserGrantRepo struct {
|
||||||
|
UserGrantEvents *grant_event.UserGrantEventStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserGrantRepo) UserGrantByID(ctx context.Context, grantID string) (*grant_model.UserGrant, error) {
|
||||||
|
return repo.UserGrantEvents.UserGrantByID(ctx, grantID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserGrantRepo) AddUserGrant(ctx context.Context, grant *grant_model.UserGrant) (*grant_model.UserGrant, error) {
|
||||||
|
return repo.UserGrantEvents.AddUserGrant(ctx, grant)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserGrantRepo) ChangeUserGrant(ctx context.Context, grant *grant_model.UserGrant) (*grant_model.UserGrant, error) {
|
||||||
|
return repo.UserGrantEvents.ChangeUserGrant(ctx, grant)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserGrantRepo) DeactivateUserGrant(ctx context.Context, grantID string) (*grant_model.UserGrant, error) {
|
||||||
|
return repo.UserGrantEvents.DeactivateUserGrant(ctx, grantID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserGrantRepo) ReactivateUserGrant(ctx context.Context, grantID string) (*grant_model.UserGrant, error) {
|
||||||
|
return repo.UserGrantEvents.ReactivateUserGrant(ctx, grantID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserGrantRepo) RemoveUserGrant(ctx context.Context, grantID string) error {
|
||||||
|
return repo.UserGrantEvents.RemoveUserGrant(ctx, grantID)
|
||||||
|
}
|
58
internal/token/model/token.go
Normal file
58
internal/token/model/token.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Token struct {
|
||||||
|
ID string
|
||||||
|
CreationDate time.Time
|
||||||
|
ChangeDate time.Time
|
||||||
|
ResourceOwner string
|
||||||
|
UserID string
|
||||||
|
ApplicationID string
|
||||||
|
UserAgentID string
|
||||||
|
Expiration time.Time
|
||||||
|
Sequence uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type TokenSearchRequest struct {
|
||||||
|
Offset uint64
|
||||||
|
Limit uint64
|
||||||
|
SortingColumn TokenSearchKey
|
||||||
|
Asc bool
|
||||||
|
Queries []*TokenSearchQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
type TokenSearchKey int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
TOKENSEARCHKEY_UNSPECIFIED TokenSearchKey = iota
|
||||||
|
TOKENSEARCHKEY_TOKEN_ID
|
||||||
|
TOKENSEARCHKEY_USER_ID
|
||||||
|
TOKENSEARCHKEY_APPLICATION_ID
|
||||||
|
TOKENSEARCHKEY_USER_AGENT_ID
|
||||||
|
TOKENSEARCHKEY_EXPIRATION
|
||||||
|
TOKENSEARCHKEY_RESOURCEOWNER
|
||||||
|
)
|
||||||
|
|
||||||
|
type TokenSearchQuery struct {
|
||||||
|
Key TokenSearchKey
|
||||||
|
Method model.SearchMethod
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
type TokenSearchResponse struct {
|
||||||
|
Offset uint64
|
||||||
|
Limit uint64
|
||||||
|
TotalResult uint64
|
||||||
|
Result []*Token
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TokenSearchRequest) EnsureLimit(limit uint64) {
|
||||||
|
if r.Limit == 0 || r.Limit > limit {
|
||||||
|
r.Limit = limit
|
||||||
|
}
|
||||||
|
}
|
56
internal/token/repository/view/model/token.go
Normal file
56
internal/token/repository/view/model/token.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/token/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TokenKeyTokenID = "id"
|
||||||
|
TokenKeyUserID = "user_id"
|
||||||
|
TokenKeyApplicationID = "application_id"
|
||||||
|
TokenKeyUserAgentID = "user_agent_id"
|
||||||
|
TokenKeyExpiration = "expiration"
|
||||||
|
TokenKeyResourceOwner = "resource_owner"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Token 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"`
|
||||||
|
UserID string `json:"-" gorm:"column:user_id"`
|
||||||
|
ApplicationID string `json:"-" gorm:"column:application_id"`
|
||||||
|
UserAgentID string `json:"-" gorm:"column:user_agent_id"`
|
||||||
|
Expiration time.Time `json:"-" gorm:"column:expiration"`
|
||||||
|
Sequence uint64 `json:"-" gorm:"column:sequence"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TokenFromModel(token *model.Token) *Token {
|
||||||
|
return &Token{
|
||||||
|
ID: token.ID,
|
||||||
|
CreationDate: token.CreationDate,
|
||||||
|
ChangeDate: token.ChangeDate,
|
||||||
|
ResourceOwner: token.ResourceOwner,
|
||||||
|
UserID: token.UserID,
|
||||||
|
ApplicationID: token.ApplicationID,
|
||||||
|
UserAgentID: token.UserAgentID,
|
||||||
|
Expiration: token.Expiration,
|
||||||
|
Sequence: token.Sequence,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TokenToModel(token *Token) *model.Token {
|
||||||
|
return &model.Token{
|
||||||
|
ID: token.ID,
|
||||||
|
CreationDate: token.CreationDate,
|
||||||
|
ChangeDate: token.ChangeDate,
|
||||||
|
ResourceOwner: token.ResourceOwner,
|
||||||
|
UserID: token.UserID,
|
||||||
|
ApplicationID: token.ApplicationID,
|
||||||
|
UserAgentID: token.UserAgentID,
|
||||||
|
Expiration: token.Expiration,
|
||||||
|
Sequence: token.Sequence,
|
||||||
|
}
|
||||||
|
}
|
69
internal/token/repository/view/model/token_query.go
Normal file
69
internal/token/repository/view/model/token_query.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
global_model "github.com/caos/zitadel/internal/model"
|
||||||
|
token_model "github.com/caos/zitadel/internal/token/model"
|
||||||
|
"github.com/caos/zitadel/internal/view"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TokenSearchRequest token_model.TokenSearchRequest
|
||||||
|
type TokenSearchQuery token_model.TokenSearchQuery
|
||||||
|
type TokenSearchKey token_model.TokenSearchKey
|
||||||
|
|
||||||
|
func (req TokenSearchRequest) GetLimit() uint64 {
|
||||||
|
return req.Limit
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req TokenSearchRequest) GetOffset() uint64 {
|
||||||
|
return req.Offset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req TokenSearchRequest) GetSortingColumn() view.ColumnKey {
|
||||||
|
if req.SortingColumn == token_model.TOKENSEARCHKEY_UNSPECIFIED {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return TokenSearchKey(req.SortingColumn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req TokenSearchRequest) GetAsc() bool {
|
||||||
|
return req.Asc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req TokenSearchRequest) GetQueries() []view.SearchQuery {
|
||||||
|
result := make([]view.SearchQuery, len(req.Queries))
|
||||||
|
for i, q := range req.Queries {
|
||||||
|
result[i] = TokenSearchQuery{Key: q.Key, Value: q.Value, Method: q.Method}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req TokenSearchQuery) GetKey() view.ColumnKey {
|
||||||
|
return TokenSearchKey(req.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req TokenSearchQuery) GetMethod() global_model.SearchMethod {
|
||||||
|
return req.Method
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req TokenSearchQuery) GetValue() interface{} {
|
||||||
|
return req.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key TokenSearchKey) ToColumnName() string {
|
||||||
|
switch token_model.TokenSearchKey(key) {
|
||||||
|
case token_model.TOKENSEARCHKEY_TOKEN_ID:
|
||||||
|
return TokenKeyTokenID
|
||||||
|
case token_model.TOKENSEARCHKEY_USER_AGENT_ID:
|
||||||
|
return TokenKeyUserAgentID
|
||||||
|
case token_model.TOKENSEARCHKEY_USER_ID:
|
||||||
|
return TokenKeyUserID
|
||||||
|
case token_model.TOKENSEARCHKEY_APPLICATION_ID:
|
||||||
|
return TokenKeyApplicationID
|
||||||
|
case token_model.TOKENSEARCHKEY_EXPIRATION:
|
||||||
|
return TokenKeyExpiration
|
||||||
|
case token_model.TOKENSEARCHKEY_RESOURCEOWNER:
|
||||||
|
return TokenKeyResourceOwner
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
48
internal/token/repository/view/token.go
Normal file
48
internal/token/repository/view/token.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package view
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/errors"
|
||||||
|
token_model "github.com/caos/zitadel/internal/token/model"
|
||||||
|
"github.com/caos/zitadel/internal/token/repository/view/model"
|
||||||
|
"github.com/caos/zitadel/internal/view"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TokenByID(db *gorm.DB, table, tokenID string) (*model.Token, error) {
|
||||||
|
token := new(model.Token)
|
||||||
|
query := view.PrepareGetByKey(table, model.TokenSearchKey(token_model.TOKENSEARCHKEY_TOKEN_ID), tokenID)
|
||||||
|
err := query(db, token)
|
||||||
|
return token, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsTokenValid(db *gorm.DB, table, tokenID string) (bool, error) {
|
||||||
|
token, err := TokenByID(db, table, tokenID)
|
||||||
|
if err == nil {
|
||||||
|
return token.Expiration.After(time.Now().UTC()), nil
|
||||||
|
}
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func PutToken(db *gorm.DB, table string, token *model.Token) error {
|
||||||
|
save := view.PrepareSave(table)
|
||||||
|
return save(db, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteToken(db *gorm.DB, table, tokenID string) error {
|
||||||
|
delete := view.PrepareDeleteByKey(table, model.TokenSearchKey(token_model.TOKENSEARCHKEY_TOKEN_ID), tokenID)
|
||||||
|
return delete(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteTokens(db *gorm.DB, table, agentID, userID string) error {
|
||||||
|
delete := view.PrepareDeleteByKeys(table,
|
||||||
|
view.Key{Key: model.TokenSearchKey(token_model.TOKENSEARCHKEY_USER_AGENT_ID), Value: agentID},
|
||||||
|
view.Key{Key: model.TokenSearchKey(token_model.TOKENSEARCHKEY_USER_ID), Value: userID},
|
||||||
|
)
|
||||||
|
return delete(db)
|
||||||
|
}
|
61
internal/user/model/user_session_view.go
Normal file
61
internal/user/model/user_session_view.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
req_model "github.com/caos/zitadel/internal/auth_request/model"
|
||||||
|
"github.com/caos/zitadel/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserSessionView struct {
|
||||||
|
ID string
|
||||||
|
CreationDate time.Time
|
||||||
|
ChangeDate time.Time
|
||||||
|
State req_model.UserSessionState
|
||||||
|
ResourceOwner string
|
||||||
|
UserAgentID string
|
||||||
|
UserID string
|
||||||
|
UserName string
|
||||||
|
PasswordVerification time.Time
|
||||||
|
MfaSoftwareVerification time.Time
|
||||||
|
MfaHardwareVerification time.Time
|
||||||
|
Sequence uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserSessionSearchRequest struct {
|
||||||
|
Offset uint64
|
||||||
|
Limit uint64
|
||||||
|
SortingColumn UserSessionSearchKey
|
||||||
|
Asc bool
|
||||||
|
Queries []*UserSessionSearchQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserSessionSearchKey int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
USERSESSIONSEARCHKEY_UNSPECIFIED UserSessionSearchKey = iota
|
||||||
|
USERSESSIONSEARCHKEY_SESSION_ID
|
||||||
|
USERSESSIONSEARCHKEY_USER_AGENT_ID
|
||||||
|
USERSESSIONSEARCHKEY_USER_ID
|
||||||
|
USERSESSIONSEARCHKEY_STATE
|
||||||
|
USERSESSIONSEARCHKEY_RESOURCEOWNER
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserSessionSearchQuery struct {
|
||||||
|
Key UserSessionSearchKey
|
||||||
|
Method model.SearchMethod
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserSessionSearchResponse struct {
|
||||||
|
Offset uint64
|
||||||
|
Limit uint64
|
||||||
|
TotalResult uint64
|
||||||
|
Result []*UserSessionView
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *UserSessionSearchRequest) EnsureLimit(limit uint64) {
|
||||||
|
if r.Limit == 0 || r.Limit > limit {
|
||||||
|
r.Limit = limit
|
||||||
|
}
|
||||||
|
}
|
@@ -1,36 +1,42 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/caos/zitadel/internal/model"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
req_model "github.com/caos/zitadel/internal/auth_request/model"
|
||||||
|
"github.com/caos/zitadel/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserView struct {
|
type UserView struct {
|
||||||
ID string
|
ID string
|
||||||
CreationDate time.Time
|
CreationDate time.Time
|
||||||
ChangeDate time.Time
|
ChangeDate time.Time
|
||||||
State UserState
|
State UserState
|
||||||
ResourceOwner string
|
ResourceOwner string
|
||||||
PasswordChanged time.Time
|
PasswordSet bool
|
||||||
LastLogin time.Time
|
PasswordChangeRequired bool
|
||||||
UserName string
|
PasswordChanged time.Time
|
||||||
FirstName string
|
LastLogin time.Time
|
||||||
LastName string
|
UserName string
|
||||||
NickName string
|
FirstName string
|
||||||
DisplayName string
|
LastName string
|
||||||
PreferredLanguage string
|
NickName string
|
||||||
Gender Gender
|
DisplayName string
|
||||||
Email string
|
PreferredLanguage string
|
||||||
IsEmailVerified bool
|
Gender Gender
|
||||||
Phone string
|
Email string
|
||||||
IsPhoneVerified bool
|
IsEmailVerified bool
|
||||||
Country string
|
Phone string
|
||||||
Locality string
|
IsPhoneVerified bool
|
||||||
PostalCode string
|
Country string
|
||||||
Region string
|
Locality string
|
||||||
StreetAddress string
|
PostalCode string
|
||||||
OTPState MfaState
|
Region string
|
||||||
Sequence uint64
|
StreetAddress string
|
||||||
|
OTPState MfaState
|
||||||
|
MfaMaxSetUp req_model.MfaLevel
|
||||||
|
MfaInitSkipped time.Time
|
||||||
|
Sequence uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserSearchRequest struct {
|
type UserSearchRequest struct {
|
||||||
@@ -78,3 +84,35 @@ func (r *UserSearchRequest) EnsureLimit(limit uint64) {
|
|||||||
func (r *UserSearchRequest) AppendMyOrgQuery(orgID string) {
|
func (r *UserSearchRequest) AppendMyOrgQuery(orgID string) {
|
||||||
r.Queries = append(r.Queries, &UserSearchQuery{Key: USERSEARCHKEY_RESOURCEOWNER, Method: model.SEARCHMETHOD_EQUALS, Value: orgID})
|
r.Queries = append(r.Queries, &UserSearchQuery{Key: USERSEARCHKEY_RESOURCEOWNER, Method: model.SEARCHMETHOD_EQUALS, Value: orgID})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserView) MfaTypesSetupPossible(level req_model.MfaLevel) []req_model.MfaType {
|
||||||
|
types := make([]req_model.MfaType, 0)
|
||||||
|
switch level {
|
||||||
|
case req_model.MfaLevelSoftware:
|
||||||
|
if u.OTPState != MFASTATE_READY {
|
||||||
|
types = append(types, req_model.MfaTypeOTP)
|
||||||
|
}
|
||||||
|
//PLANNED: add sms
|
||||||
|
fallthrough
|
||||||
|
case req_model.MfaLevelHardware:
|
||||||
|
//PLANNED: add token
|
||||||
|
}
|
||||||
|
return types
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserView) MfaTypesAllowed(level req_model.MfaLevel) []req_model.MfaType {
|
||||||
|
types := make([]req_model.MfaType, 0)
|
||||||
|
switch level {
|
||||||
|
default:
|
||||||
|
fallthrough
|
||||||
|
case req_model.MfaLevelSoftware:
|
||||||
|
if u.OTPState == MFASTATE_READY {
|
||||||
|
types = append(types, req_model.MfaTypeOTP)
|
||||||
|
}
|
||||||
|
//PLANNED: add sms
|
||||||
|
fallthrough
|
||||||
|
case req_model.MfaLevelHardware:
|
||||||
|
//PLANNED: add token
|
||||||
|
}
|
||||||
|
return types
|
||||||
|
}
|
||||||
|
@@ -4,6 +4,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/pquerna/otp/totp"
|
||||||
|
"github.com/sony/sonyflake"
|
||||||
|
|
||||||
|
req_model "github.com/caos/zitadel/internal/auth_request/model"
|
||||||
"github.com/caos/zitadel/internal/cache/config"
|
"github.com/caos/zitadel/internal/cache/config"
|
||||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||||
"github.com/caos/zitadel/internal/crypto"
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
@@ -14,8 +18,6 @@ import (
|
|||||||
global_model "github.com/caos/zitadel/internal/model"
|
global_model "github.com/caos/zitadel/internal/model"
|
||||||
usr_model "github.com/caos/zitadel/internal/user/model"
|
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||||
"github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
"github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||||
"github.com/pquerna/otp/totp"
|
|
||||||
"github.com/sony/sonyflake"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserEventstore struct {
|
type UserEventstore struct {
|
||||||
@@ -28,6 +30,7 @@ type UserEventstore struct {
|
|||||||
PhoneVerificationCode crypto.Generator
|
PhoneVerificationCode crypto.Generator
|
||||||
PasswordVerificationCode crypto.Generator
|
PasswordVerificationCode crypto.Generator
|
||||||
Multifactors global_model.Multifactors
|
Multifactors global_model.Multifactors
|
||||||
|
validateTOTP func(string, string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserConfig struct {
|
type UserConfig struct {
|
||||||
@@ -71,6 +74,7 @@ func StartUser(conf UserConfig, systemDefaults sd.SystemDefaults) (*UserEventsto
|
|||||||
PasswordVerificationCode: passwordVerificationCode,
|
PasswordVerificationCode: passwordVerificationCode,
|
||||||
Multifactors: mfa,
|
Multifactors: mfa,
|
||||||
PasswordAlg: passwordAlg,
|
PasswordAlg: passwordAlg,
|
||||||
|
validateTOTP: totp.Validate,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,31 +337,80 @@ func (es *UserEventstore) UserPasswordByID(ctx context.Context, userID string) (
|
|||||||
return nil, caos_errs.ThrowNotFound(nil, "EVENT-d8e2", "password not found")
|
return nil, caos_errs.ThrowNotFound(nil, "EVENT-d8e2", "password not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (es *UserEventstore) SetOneTimePassword(ctx context.Context, password *usr_model.Password) (*usr_model.Password, error) {
|
func (es *UserEventstore) CheckPassword(ctx context.Context, userID, password string, authRequest *req_model.AuthRequest) error {
|
||||||
return es.changedPassword(ctx, password, true)
|
existing, err := es.UserByID(ctx, userID)
|
||||||
}
|
if err != nil {
|
||||||
|
return err
|
||||||
func (es *UserEventstore) SetPassword(ctx context.Context, password *usr_model.Password) (*usr_model.Password, error) {
|
|
||||||
return es.changedPassword(ctx, password, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (es *UserEventstore) changedPassword(ctx context.Context, password *usr_model.Password, onetime bool) (*usr_model.Password, error) {
|
|
||||||
if !password.IsValid() {
|
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-dosi3", "password invalid")
|
|
||||||
}
|
}
|
||||||
|
if existing.Password == nil {
|
||||||
|
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-s35Fa", "no password set")
|
||||||
|
}
|
||||||
|
if err := crypto.CompareHash(existing.Password.SecretCrypto, []byte(password), es.PasswordAlg); err == nil {
|
||||||
|
return es.setPasswordCheckResult(ctx, existing, authRequest, PasswordCheckSucceededAggregate)
|
||||||
|
}
|
||||||
|
if err := es.setPasswordCheckResult(ctx, existing, authRequest, PasswordCheckFailedAggregate); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "EVENT-452ad", "invalid password")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (es *UserEventstore) setPasswordCheckResult(ctx context.Context, user *usr_model.User, authRequest *req_model.AuthRequest, check func(*es_models.AggregateCreator, *model.User, *model.AuthRequest) es_sdk.AggregateFunc) error {
|
||||||
|
repoUser := model.UserFromModel(user)
|
||||||
|
repoAuthRequest := model.AuthRequestFromModel(authRequest)
|
||||||
|
agg := check(es.AggregateCreator(), repoUser, repoAuthRequest)
|
||||||
|
err := es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, agg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
es.userCache.cacheUser(repoUser)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (es *UserEventstore) SetOneTimePassword(ctx context.Context, password *usr_model.Password) (*usr_model.Password, error) {
|
||||||
user, err := es.UserByID(ctx, password.AggregateID)
|
user, err := es.UserByID(ctx, password.AggregateID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return es.changedPassword(ctx, user, password.SecretString, true)
|
||||||
|
}
|
||||||
|
|
||||||
err = password.HashPasswordIfExisting(es.PasswordAlg, onetime)
|
func (es *UserEventstore) SetPassword(ctx context.Context, userID, code, password string) error {
|
||||||
|
user, err := es.UserByID(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if user.PasswordCode == nil {
|
||||||
|
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-65sdr", "reset code not found")
|
||||||
|
}
|
||||||
|
if err := crypto.VerifyCode(user.PasswordCode.CreationDate, user.PasswordCode.Expiry, user.PasswordCode.Code, code, es.PasswordVerificationCode); err != nil {
|
||||||
|
return caos_errs.ThrowPreconditionFailed(err, "EVENT-sd6DF", "code invalid")
|
||||||
|
}
|
||||||
|
_, err = es.changedPassword(ctx, user, password, false)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (es *UserEventstore) ChangePassword(ctx context.Context, userID, old, new string) (*usr_model.Password, error) {
|
||||||
|
user, err := es.UserByID(ctx, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if user.Password == nil {
|
||||||
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-Fds3s", "user has no password")
|
||||||
|
}
|
||||||
|
if err := crypto.CompareHash(user.Password.SecretCrypto, []byte(old), es.PasswordAlg); err != nil {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "EVENT-s56a3", "invalid password")
|
||||||
|
}
|
||||||
|
return es.changedPassword(ctx, user, new, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (es *UserEventstore) changedPassword(ctx context.Context, user *usr_model.User, password string, onetime bool) (*usr_model.Password, error) {
|
||||||
|
//TODO: check password policy
|
||||||
|
secret, err := crypto.Hash([]byte(password), es.PasswordAlg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
repoPassword := &model.Password{Secret: secret, ChangeRequired: onetime}
|
||||||
repoUser := model.UserFromModel(user)
|
repoUser := model.UserFromModel(user)
|
||||||
repoPassword := model.PasswordFromModel(password)
|
|
||||||
|
|
||||||
agg := PasswordChangeAggregate(es.AggregateCreator(), repoUser, repoPassword)
|
agg := PasswordChangeAggregate(es.AggregateCreator(), repoUser, repoPassword)
|
||||||
err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, agg)
|
err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, agg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -666,21 +719,6 @@ func (es *UserEventstore) ChangeAddress(ctx context.Context, address *usr_model.
|
|||||||
return model.AddressToModel(repoExisting.Address), nil
|
return model.AddressToModel(repoExisting.Address), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (es *UserEventstore) OTPByID(ctx context.Context, userID string) (*usr_model.OTP, error) {
|
|
||||||
if userID == "" {
|
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-do9se", "userID missing")
|
|
||||||
}
|
|
||||||
user, err := es.UserByID(ctx, userID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if user.OTP != nil {
|
|
||||||
return user.OTP, nil
|
|
||||||
}
|
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "EVENT-dps09", "otp not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (es *UserEventstore) AddOTP(ctx context.Context, userID string) (*usr_model.OTP, error) {
|
func (es *UserEventstore) AddOTP(ctx context.Context, userID string) (*usr_model.OTP, error) {
|
||||||
existing, err := es.UserByID(ctx, userID)
|
existing, err := es.UserByID(ctx, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -731,22 +769,77 @@ func (es *UserEventstore) RemoveOTP(ctx context.Context, userID string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (es *UserEventstore) CheckMfaOTP(ctx context.Context, userID, code string) error {
|
func (es *UserEventstore) CheckMfaOTPSetup(ctx context.Context, userID, code string) error {
|
||||||
existing, err := es.UserByID(ctx, userID)
|
user, err := es.UserByID(ctx, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if existing.OTP == nil {
|
if user.OTP == nil || user.IsOTPReady() {
|
||||||
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-sp0de", "no otp existing")
|
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-sd5NJ", "otp not existing or already set up")
|
||||||
}
|
}
|
||||||
decrypt, err := crypto.DecryptString(existing.OTP.Secret, es.Multifactors.OTP.CryptoMFA)
|
if err := es.verifyMfaOTP(user.OTP, code); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
repoUser := model.UserFromModel(user)
|
||||||
|
err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, MfaOTPVerifyAggregate(es.AggregateCreator(), repoUser))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
valid := totp.Validate(code, decrypt)
|
es.userCache.cacheUser(repoUser)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (es *UserEventstore) CheckMfaOTP(ctx context.Context, userID, code string, authRequest *req_model.AuthRequest) error {
|
||||||
|
user, err := es.UserByID(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !user.IsOTPReady() {
|
||||||
|
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-sd5NJ", "opt not ready")
|
||||||
|
}
|
||||||
|
|
||||||
|
repoUser := model.UserFromModel(user)
|
||||||
|
repoAuthReq := model.AuthRequestFromModel(authRequest)
|
||||||
|
var aggregate func(*es_models.AggregateCreator, *model.User, *model.AuthRequest) es_sdk.AggregateFunc
|
||||||
|
if err := es.verifyMfaOTP(user.OTP, code); err != nil {
|
||||||
|
aggregate = MfaOTPCheckFailedAggregate
|
||||||
|
} else {
|
||||||
|
aggregate = MfaOTPCheckSucceededAggregate
|
||||||
|
}
|
||||||
|
err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, aggregate(es.AggregateCreator(), repoUser, repoAuthReq))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
es.userCache.cacheUser(repoUser)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (es *UserEventstore) verifyMfaOTP(otp *usr_model.OTP, code string) error {
|
||||||
|
decrypt, err := crypto.DecryptString(otp.Secret, es.Multifactors.OTP.CryptoMFA)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
valid := es.validateTOTP(code, decrypt)
|
||||||
if !valid {
|
if !valid {
|
||||||
return caos_errs.ThrowInvalidArgument(nil, "EVENT-8isk2", "Invalid code")
|
return caos_errs.ThrowInvalidArgument(nil, "EVENT-8isk2", "Invalid code")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (es *UserEventstore) SignOut(ctx context.Context, agentID, userID string) error {
|
||||||
|
user, err := es.UserByID(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
repoUser := model.UserFromModel(user)
|
||||||
|
err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, SignOutAggregate(es.AggregateCreator(), repoUser, agentID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
es.userCache.cacheUser(repoUser)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@@ -2,15 +2,17 @@ package eventsourcing
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/sony/sonyflake"
|
||||||
|
|
||||||
mock_cache "github.com/caos/zitadel/internal/cache/mock"
|
mock_cache "github.com/caos/zitadel/internal/cache/mock"
|
||||||
"github.com/caos/zitadel/internal/crypto"
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
"github.com/caos/zitadel/internal/eventstore/mock"
|
"github.com/caos/zitadel/internal/eventstore/mock"
|
||||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||||
global_model "github.com/caos/zitadel/internal/model"
|
global_model "github.com/caos/zitadel/internal/model"
|
||||||
"github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
"github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||||
"github.com/golang/mock/gomock"
|
|
||||||
"github.com/sony/sonyflake"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetMockedEventstore(ctrl *gomock.Controller, mockEs *mock.MockEventstore) *UserEventstore {
|
func GetMockedEventstore(ctrl *gomock.Controller, mockEs *mock.MockEventstore) *UserEventstore {
|
||||||
@@ -38,10 +40,7 @@ func GetMockedEventstoreWithPw(ctrl *gomock.Controller, mockEs *mock.MockEventst
|
|||||||
}
|
}
|
||||||
if password {
|
if password {
|
||||||
es.PasswordVerificationCode = GetMockPwGenerator(ctrl)
|
es.PasswordVerificationCode = GetMockPwGenerator(ctrl)
|
||||||
hash := crypto.NewMockHashAlgorithm(ctrl)
|
es.PasswordAlg = crypto.CreateMockHashAlg(ctrl)
|
||||||
hash.EXPECT().Hash(gomock.Any()).Return(nil, nil)
|
|
||||||
hash.EXPECT().Algorithm().Return("bcrypt")
|
|
||||||
es.PasswordAlg = hash
|
|
||||||
}
|
}
|
||||||
return es
|
return es
|
||||||
}
|
}
|
||||||
@@ -174,8 +173,10 @@ func GetMockManipulateUserWithPhoneCodeGen(ctrl *gomock.Controller, user model.U
|
|||||||
|
|
||||||
func GetMockManipulateUserWithPasswordCodeGen(ctrl *gomock.Controller, user model.User) *UserEventstore {
|
func GetMockManipulateUserWithPasswordCodeGen(ctrl *gomock.Controller, user model.User) *UserEventstore {
|
||||||
data, _ := json.Marshal(user)
|
data, _ := json.Marshal(user)
|
||||||
|
code, _ := json.Marshal(user.PasswordCode)
|
||||||
events := []*es_models.Event{
|
events := []*es_models.Event{
|
||||||
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.UserAdded, Data: data},
|
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.UserAdded, Data: data},
|
||||||
|
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.UserPasswordCodeAdded, Data: code},
|
||||||
}
|
}
|
||||||
mockEs := mock.NewMockEventstore(ctrl)
|
mockEs := mock.NewMockEventstore(ctrl)
|
||||||
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
|
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
|
||||||
@@ -394,29 +395,48 @@ func GetMockManipulateUserFull(ctrl *gomock.Controller) *UserEventstore {
|
|||||||
return GetMockedEventstore(ctrl, mockEs)
|
return GetMockedEventstore(ctrl, mockEs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMockManipulateUserWithOTP(ctrl *gomock.Controller) *UserEventstore {
|
func GetMockManipulateUserWithOTP(ctrl *gomock.Controller, decrypt, verified bool) *UserEventstore {
|
||||||
user := model.User{
|
user := model.User{
|
||||||
Profile: &model.Profile{
|
Profile: &model.Profile{
|
||||||
UserName: "UserName",
|
UserName: "UserName",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
otp := model.OTP{Secret: &crypto.CryptoValue{
|
otp := model.OTP{
|
||||||
CryptoType: crypto.TypeEncryption,
|
Secret: &crypto.CryptoValue{
|
||||||
Algorithm: "enc",
|
CryptoType: crypto.TypeEncryption,
|
||||||
KeyID: "id",
|
Algorithm: "enc",
|
||||||
Crypted: []byte("code"),
|
KeyID: "id",
|
||||||
}}
|
Crypted: []byte("code"),
|
||||||
|
},
|
||||||
|
}
|
||||||
dataUser, _ := json.Marshal(user)
|
dataUser, _ := json.Marshal(user)
|
||||||
dataOtp, _ := json.Marshal(otp)
|
dataOtp, _ := json.Marshal(otp)
|
||||||
events := []*es_models.Event{
|
events := []*es_models.Event{
|
||||||
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.UserAdded, Data: dataUser},
|
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.UserAdded, Data: dataUser},
|
||||||
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.MfaOtpAdded, Data: dataOtp},
|
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.MfaOtpAdded, Data: dataOtp},
|
||||||
}
|
}
|
||||||
|
if verified {
|
||||||
|
events = append(events, &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.MfaOtpVerified})
|
||||||
|
}
|
||||||
mockEs := mock.NewMockEventstore(ctrl)
|
mockEs := mock.NewMockEventstore(ctrl)
|
||||||
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
|
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
|
||||||
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
|
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
|
||||||
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
|
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
|
||||||
return GetMockedEventstore(ctrl, mockEs)
|
es := GetMockedEventstore(ctrl, mockEs)
|
||||||
|
if !decrypt {
|
||||||
|
return es
|
||||||
|
}
|
||||||
|
enc := crypto.NewMockEncryptionAlgorithm(ctrl)
|
||||||
|
enc.EXPECT().Algorithm().Return("enc")
|
||||||
|
enc.EXPECT().Encrypt(gomock.Any()).Return(nil, nil)
|
||||||
|
enc.EXPECT().EncryptionKeyID().Return("id")
|
||||||
|
enc.EXPECT().DecryptionKeyIDs().Return([]string{"id"})
|
||||||
|
enc.EXPECT().DecryptString(gomock.Any(), gomock.Any()).Return("code", nil)
|
||||||
|
es.Multifactors = global_model.Multifactors{OTP: global_model.OTP{
|
||||||
|
Issuer: "Issuer",
|
||||||
|
CryptoMFA: enc,
|
||||||
|
}}
|
||||||
|
return es
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMockManipulateUserNoEvents(ctrl *gomock.Controller) *UserEventstore {
|
func GetMockManipulateUserNoEvents(ctrl *gomock.Controller) *UserEventstore {
|
||||||
|
@@ -2,14 +2,19 @@ package eventsourcing
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/api/auth"
|
"github.com/caos/zitadel/internal/api/auth"
|
||||||
|
req_model "github.com/caos/zitadel/internal/auth_request/model"
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||||
"github.com/caos/zitadel/internal/user/model"
|
"github.com/caos/zitadel/internal/user/model"
|
||||||
repo_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
repo_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||||
"github.com/golang/mock/gomock"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUserByID(t *testing.T) {
|
func TestUserByID(t *testing.T) {
|
||||||
@@ -1025,16 +1030,17 @@ func TestSetOneTimePassword(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetPassword(t *testing.T) {
|
func TestCheckPassword(t *testing.T) {
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
type args struct {
|
type args struct {
|
||||||
es *UserEventstore
|
es *UserEventstore
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
password *model.Password
|
userID string
|
||||||
|
password string
|
||||||
|
authRequest *req_model.AuthRequest
|
||||||
}
|
}
|
||||||
type res struct {
|
type res struct {
|
||||||
password *model.Password
|
errFunc func(err error) bool
|
||||||
errFunc func(err error) bool
|
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -1042,22 +1048,40 @@ func TestSetPassword(t *testing.T) {
|
|||||||
res res
|
res res
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "create pw",
|
name: "check pw ok",
|
||||||
args: args{
|
args: args{
|
||||||
es: GetMockManipulateUserWithPasswordCodeGen(ctrl, repo_model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}}),
|
es: GetMockManipulateUserWithPasswordAndEmailCodeGen(ctrl,
|
||||||
|
repo_model.User{
|
||||||
|
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"},
|
||||||
|
Password: &repo_model.Password{Secret: &crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeHash,
|
||||||
|
Algorithm: "hash",
|
||||||
|
Crypted: []byte("password"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
),
|
||||||
ctx: auth.NewMockContext("orgID", "userID"),
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
password: &model.Password{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SecretString: "Password"},
|
userID: "userID",
|
||||||
},
|
password: "password",
|
||||||
res: res{
|
authRequest: &req_model.AuthRequest{
|
||||||
password: &model.Password{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, ChangeRequired: false},
|
ID: "id",
|
||||||
|
AgentID: "agentID",
|
||||||
|
BrowserInfo: &req_model.BrowserInfo{
|
||||||
|
UserAgent: "user agent",
|
||||||
|
AcceptLanguage: "accept langugage",
|
||||||
|
RemoteIP: net.IPv4(29, 4, 20, 19),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
res: res{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty userid",
|
name: "empty userid",
|
||||||
args: args{
|
args: args{
|
||||||
es: GetMockManipulateUser(ctrl),
|
es: GetMockManipulateUser(ctrl),
|
||||||
ctx: auth.NewMockContext("orgID", "userID"),
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
password: &model.Password{ObjectRoot: es_models.ObjectRoot{AggregateID: ""}, SecretString: "Password"},
|
userID: "",
|
||||||
|
password: "password",
|
||||||
},
|
},
|
||||||
res: res{
|
res: res{
|
||||||
errFunc: caos_errs.IsPreconditionFailed,
|
errFunc: caos_errs.IsPreconditionFailed,
|
||||||
@@ -1068,25 +1092,311 @@ func TestSetPassword(t *testing.T) {
|
|||||||
args: args{
|
args: args{
|
||||||
es: GetMockManipulateUserNoEvents(ctrl),
|
es: GetMockManipulateUserNoEvents(ctrl),
|
||||||
ctx: auth.NewMockContext("orgID", "userID"),
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
password: &model.Password{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SecretString: "Password"},
|
userID: "userID",
|
||||||
|
password: "password",
|
||||||
},
|
},
|
||||||
res: res{
|
res: res{
|
||||||
errFunc: caos_errs.IsNotFound,
|
errFunc: caos_errs.IsNotFound,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "no password",
|
||||||
|
args: args{
|
||||||
|
es: GetMockUserByIDOK(ctrl,
|
||||||
|
repo_model.User{
|
||||||
|
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "userID",
|
||||||
|
password: "password",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid password",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUserWithPasswordCodeGen(ctrl,
|
||||||
|
repo_model.User{
|
||||||
|
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"},
|
||||||
|
Password: &repo_model.Password{Secret: &crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeHash,
|
||||||
|
Algorithm: "hash",
|
||||||
|
Crypted: []byte("password"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "userID",
|
||||||
|
password: "wrong password",
|
||||||
|
authRequest: &req_model.AuthRequest{
|
||||||
|
ID: "id",
|
||||||
|
AgentID: "agentID",
|
||||||
|
BrowserInfo: &req_model.BrowserInfo{
|
||||||
|
UserAgent: "user agent",
|
||||||
|
AcceptLanguage: "accept langugage",
|
||||||
|
RemoteIP: net.IPv4(29, 4, 20, 19),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
result, err := tt.args.es.SetPassword(tt.args.ctx, tt.args.password)
|
err := tt.args.es.CheckPassword(tt.args.ctx, tt.args.userID, tt.args.password, tt.args.authRequest)
|
||||||
|
|
||||||
|
if tt.res.errFunc == nil && err != nil {
|
||||||
|
t.Errorf("result has error: %v", err)
|
||||||
|
}
|
||||||
|
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
|
||||||
|
t.Errorf("got wrong err: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetPassword(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
type args struct {
|
||||||
|
es *UserEventstore
|
||||||
|
ctx context.Context
|
||||||
|
userID string
|
||||||
|
code string
|
||||||
|
password string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
errFunc func(err error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "create pw",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUserWithPasswordCodeGen(ctrl,
|
||||||
|
repo_model.User{
|
||||||
|
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"},
|
||||||
|
PasswordCode: &repo_model.PasswordCode{Code: &crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("code"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "userID",
|
||||||
|
code: "code",
|
||||||
|
password: "password",
|
||||||
|
},
|
||||||
|
res: res{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty userid",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUser(ctrl),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "",
|
||||||
|
code: "code",
|
||||||
|
password: "password",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "existing user not found",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUserNoEvents(ctrl),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "userID",
|
||||||
|
code: "code",
|
||||||
|
password: "password",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no passcode",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUserWithPasswordCodeGen(ctrl,
|
||||||
|
repo_model.User{
|
||||||
|
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "userID",
|
||||||
|
code: "code",
|
||||||
|
password: "password",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid passcode",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUserWithPasswordCodeGen(ctrl,
|
||||||
|
repo_model.User{
|
||||||
|
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"},
|
||||||
|
PasswordCode: &repo_model.PasswordCode{Code: &crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc2",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("code2"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "userID",
|
||||||
|
code: "code",
|
||||||
|
password: "password",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
err := tt.args.es.SetPassword(tt.args.ctx, tt.args.userID, tt.args.code, tt.args.password)
|
||||||
|
|
||||||
|
if tt.res.errFunc == nil && err != nil {
|
||||||
|
t.Errorf("result has error: %v", err)
|
||||||
|
}
|
||||||
|
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
|
||||||
|
t.Errorf("got wrong err: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChangePassword(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
type args struct {
|
||||||
|
es *UserEventstore
|
||||||
|
ctx context.Context
|
||||||
|
userID string
|
||||||
|
old string
|
||||||
|
new string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
password string
|
||||||
|
errFunc func(err error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "change pw",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUserWithPasswordAndEmailCodeGen(ctrl,
|
||||||
|
repo_model.User{
|
||||||
|
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"},
|
||||||
|
Password: &repo_model.Password{Secret: &crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeHash,
|
||||||
|
Algorithm: "hash",
|
||||||
|
Crypted: []byte("old"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "userID",
|
||||||
|
old: "old",
|
||||||
|
new: "new",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
password: "new",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty userid",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUser(ctrl),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "",
|
||||||
|
old: "old",
|
||||||
|
new: "new",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "existing user not found",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUserNoEvents(ctrl),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "userID",
|
||||||
|
old: "old",
|
||||||
|
new: "new",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no password",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUserWithPasswordCodeGen(ctrl,
|
||||||
|
repo_model.User{
|
||||||
|
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "userID",
|
||||||
|
old: "old",
|
||||||
|
new: "new",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid password",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUserWithPasswordCodeGen(ctrl,
|
||||||
|
repo_model.User{
|
||||||
|
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"},
|
||||||
|
Password: &repo_model.Password{Secret: &crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeHash,
|
||||||
|
Algorithm: "hash",
|
||||||
|
Crypted: []byte("older"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "userID",
|
||||||
|
old: "old",
|
||||||
|
new: "new",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result, err := tt.args.es.ChangePassword(tt.args.ctx, tt.args.userID, tt.args.old, tt.args.new)
|
||||||
|
|
||||||
if tt.res.errFunc == nil && result.AggregateID == "" {
|
if tt.res.errFunc == nil && result.AggregateID == "" {
|
||||||
t.Errorf("result has no id")
|
t.Errorf("result has no id")
|
||||||
}
|
}
|
||||||
if tt.res.errFunc == nil && result.ChangeRequired != false {
|
if tt.res.errFunc == nil && string(result.SecretCrypto.Crypted) != tt.res.password {
|
||||||
t.Errorf("should not be one time")
|
t.Errorf("got wrong result crypted: expected: %v, actual: %v ", tt.res.password, result.SecretString)
|
||||||
}
|
}
|
||||||
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
|
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
|
||||||
t.Errorf("got wrong err: %v ", err)
|
t.Errorf("got wrong err: %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -2036,69 +2346,6 @@ func TestChangeAddress(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOTPByID(t *testing.T) {
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
type args struct {
|
|
||||||
es *UserEventstore
|
|
||||||
ctx context.Context
|
|
||||||
existing *model.User
|
|
||||||
}
|
|
||||||
type res struct {
|
|
||||||
errFunc func(err error) bool
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
res res
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "get by id, ok",
|
|
||||||
args: args{
|
|
||||||
es: GetMockManipulateUserWithOTP(ctrl),
|
|
||||||
ctx: auth.NewMockContext("orgID", "userID"),
|
|
||||||
existing: &model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty userid",
|
|
||||||
args: args{
|
|
||||||
es: GetMockManipulateUser(ctrl),
|
|
||||||
ctx: auth.NewMockContext("orgID", "userID"),
|
|
||||||
existing: &model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "", Sequence: 1}},
|
|
||||||
},
|
|
||||||
res: res{
|
|
||||||
errFunc: caos_errs.IsPreconditionFailed,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "existing user not found",
|
|
||||||
args: args{
|
|
||||||
es: GetMockManipulateUserNoEvents(ctrl),
|
|
||||||
ctx: auth.NewMockContext("orgID", "userID"),
|
|
||||||
existing: &model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
|
|
||||||
},
|
|
||||||
res: res{
|
|
||||||
errFunc: caos_errs.IsNotFound,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
result, err := tt.args.es.OTPByID(tt.args.ctx, tt.args.existing.AggregateID)
|
|
||||||
|
|
||||||
if tt.res.errFunc == nil && result.AggregateID == "" {
|
|
||||||
t.Errorf("result has no id")
|
|
||||||
}
|
|
||||||
if tt.res.errFunc == nil && result == nil {
|
|
||||||
t.Errorf("got wrong result change required: actual: %v ", result)
|
|
||||||
}
|
|
||||||
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
|
|
||||||
t.Errorf("got wrong err: %v ", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddOTP(t *testing.T) {
|
func TestAddOTP(t *testing.T) {
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
type args struct {
|
type args struct {
|
||||||
@@ -2168,6 +2415,245 @@ func TestAddOTP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCheckMfaOTPSetup(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
type args struct {
|
||||||
|
es *UserEventstore
|
||||||
|
ctx context.Context
|
||||||
|
userID string
|
||||||
|
code string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
errFunc func(err error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "setup ok",
|
||||||
|
args: args{
|
||||||
|
es: func() *UserEventstore {
|
||||||
|
es := GetMockManipulateUserWithOTP(ctrl, true, false)
|
||||||
|
es.validateTOTP = func(string, string) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return es
|
||||||
|
}(),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "id",
|
||||||
|
code: "code",
|
||||||
|
},
|
||||||
|
res: res{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong code",
|
||||||
|
args: args{
|
||||||
|
es: func() *UserEventstore {
|
||||||
|
es := GetMockManipulateUserWithOTP(ctrl, true, false)
|
||||||
|
es.validateTOTP = func(string, string) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return es
|
||||||
|
}(),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "id",
|
||||||
|
code: "code",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty userid",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUser(ctrl),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
code: "code",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty code",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUser(ctrl),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "userID",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "existing user not found",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUserNoEvents(ctrl),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "userID",
|
||||||
|
code: "code",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user has no otp",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUser(ctrl),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "userID",
|
||||||
|
code: "code",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
err := tt.args.es.CheckMfaOTPSetup(tt.args.ctx, tt.args.userID, tt.args.code)
|
||||||
|
|
||||||
|
if tt.res.errFunc == nil && err != nil {
|
||||||
|
t.Errorf("result should not get err")
|
||||||
|
}
|
||||||
|
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckMfaOTP(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
type args struct {
|
||||||
|
es *UserEventstore
|
||||||
|
ctx context.Context
|
||||||
|
userID string
|
||||||
|
code string
|
||||||
|
authRequest *req_model.AuthRequest
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
errFunc func(err error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "check ok",
|
||||||
|
args: args{
|
||||||
|
es: func() *UserEventstore {
|
||||||
|
es := GetMockManipulateUserWithOTP(ctrl, true, true)
|
||||||
|
es.validateTOTP = func(string, string) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return es
|
||||||
|
}(),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "id",
|
||||||
|
code: "code",
|
||||||
|
authRequest: &req_model.AuthRequest{
|
||||||
|
ID: "id",
|
||||||
|
AgentID: "agentID",
|
||||||
|
BrowserInfo: &req_model.BrowserInfo{
|
||||||
|
UserAgent: "user agent",
|
||||||
|
AcceptLanguage: "accept langugage",
|
||||||
|
RemoteIP: net.IPv4(29, 4, 20, 19),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong code",
|
||||||
|
args: args{
|
||||||
|
es: func() *UserEventstore {
|
||||||
|
es := GetMockManipulateUserWithOTP(ctrl, true, true)
|
||||||
|
es.validateTOTP = func(string, string) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return es
|
||||||
|
}(),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "id",
|
||||||
|
code: "code",
|
||||||
|
authRequest: &req_model.AuthRequest{
|
||||||
|
ID: "id",
|
||||||
|
AgentID: "agentID",
|
||||||
|
BrowserInfo: &req_model.BrowserInfo{
|
||||||
|
UserAgent: "user agent",
|
||||||
|
AcceptLanguage: "accept langugage",
|
||||||
|
RemoteIP: net.IPv4(29, 4, 20, 19),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty userid",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUser(ctrl),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
code: "code",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty code",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUser(ctrl),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "userID",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "existing user not found",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUserNoEvents(ctrl),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "userID",
|
||||||
|
code: "code",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user has no otp",
|
||||||
|
args: args{
|
||||||
|
es: GetMockManipulateUser(ctrl),
|
||||||
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
|
userID: "userID",
|
||||||
|
code: "code",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
errFunc: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
err := tt.args.es.CheckMfaOTP(tt.args.ctx, tt.args.userID, tt.args.code, tt.args.authRequest)
|
||||||
|
|
||||||
|
if tt.res.errFunc == nil && err != nil {
|
||||||
|
t.Errorf("result should not get err, got : %v", err)
|
||||||
|
}
|
||||||
|
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRemoveOTP(t *testing.T) {
|
func TestRemoveOTP(t *testing.T) {
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
type args struct {
|
type args struct {
|
||||||
@@ -2186,7 +2672,7 @@ func TestRemoveOTP(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "remove ok",
|
name: "remove ok",
|
||||||
args: args{
|
args: args{
|
||||||
es: GetMockManipulateUserWithOTP(ctrl),
|
es: GetMockManipulateUserWithOTP(ctrl, false, true),
|
||||||
ctx: auth.NewMockContext("orgID", "userID"),
|
ctx: auth.NewMockContext("orgID", "userID"),
|
||||||
existing: &model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
|
existing: &model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
|
||||||
},
|
},
|
||||||
@@ -2238,80 +2724,3 @@ func TestRemoveOTP(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckOTP(t *testing.T) {
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
type args struct {
|
|
||||||
es *UserEventstore
|
|
||||||
ctx context.Context
|
|
||||||
userID string
|
|
||||||
code string
|
|
||||||
}
|
|
||||||
type res struct {
|
|
||||||
errFunc func(err error) bool
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
res res
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "empty userid",
|
|
||||||
args: args{
|
|
||||||
es: GetMockManipulateUser(ctrl),
|
|
||||||
ctx: auth.NewMockContext("orgID", "userID"),
|
|
||||||
code: "code",
|
|
||||||
},
|
|
||||||
res: res{
|
|
||||||
errFunc: caos_errs.IsPreconditionFailed,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty code",
|
|
||||||
args: args{
|
|
||||||
es: GetMockManipulateUser(ctrl),
|
|
||||||
ctx: auth.NewMockContext("orgID", "userID"),
|
|
||||||
userID: "userID",
|
|
||||||
},
|
|
||||||
res: res{
|
|
||||||
errFunc: caos_errs.IsPreconditionFailed,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "existing user not found",
|
|
||||||
args: args{
|
|
||||||
es: GetMockManipulateUserNoEvents(ctrl),
|
|
||||||
ctx: auth.NewMockContext("orgID", "userID"),
|
|
||||||
userID: "userID",
|
|
||||||
code: "code",
|
|
||||||
},
|
|
||||||
res: res{
|
|
||||||
errFunc: caos_errs.IsNotFound,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "user has no otp",
|
|
||||||
args: args{
|
|
||||||
es: GetMockManipulateUser(ctrl),
|
|
||||||
ctx: auth.NewMockContext("orgID", "userID"),
|
|
||||||
userID: "userID",
|
|
||||||
code: "code",
|
|
||||||
},
|
|
||||||
res: res{
|
|
||||||
errFunc: caos_errs.IsPreconditionFailed,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
err := tt.args.es.CheckMfaOTP(tt.args.ctx, tt.args.userID, tt.args.code)
|
|
||||||
|
|
||||||
if tt.res.errFunc == nil && err != nil {
|
|
||||||
t.Errorf("result should not get err")
|
|
||||||
}
|
|
||||||
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
|
|
||||||
t.Errorf("got wrong err: %v ", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
35
internal/user/repository/eventsourcing/model/auth_request.go
Normal file
35
internal/user/repository/eventsourcing/model/auth_request.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/auth_request/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthRequest struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
UserAgentID string `json:"userAgentID,omitempty"`
|
||||||
|
*BrowserInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func AuthRequestFromModel(request *model.AuthRequest) *AuthRequest {
|
||||||
|
return &AuthRequest{
|
||||||
|
ID: request.ID,
|
||||||
|
UserAgentID: request.AgentID,
|
||||||
|
BrowserInfo: BrowserInfoFromModel(request.BrowserInfo),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type BrowserInfo struct {
|
||||||
|
UserAgent string `json:"userAgent,omitempty"`
|
||||||
|
AcceptLanguage string `json:"acceptLanguage,omitempty"`
|
||||||
|
RemoteIP net.IP `json:"remoteIP,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func BrowserInfoFromModel(info *model.BrowserInfo) *BrowserInfo {
|
||||||
|
return &BrowserInfo{
|
||||||
|
UserAgent: info.UserAgent,
|
||||||
|
AcceptLanguage: info.AcceptLanguage,
|
||||||
|
RemoteIP: info.RemoteIP,
|
||||||
|
}
|
||||||
|
}
|
@@ -23,9 +23,11 @@ const (
|
|||||||
UserReactivated models.EventType = "user.reactivated"
|
UserReactivated models.EventType = "user.reactivated"
|
||||||
UserDeleted models.EventType = "user.deleted"
|
UserDeleted models.EventType = "user.deleted"
|
||||||
|
|
||||||
UserPasswordChanged models.EventType = "user.password.changed"
|
UserPasswordChanged models.EventType = "user.password.changed"
|
||||||
UserPasswordCodeAdded models.EventType = "user.password.code.added"
|
UserPasswordCodeAdded models.EventType = "user.password.code.added"
|
||||||
UserPasswordCodeSent models.EventType = "user.password.code.sent"
|
UserPasswordCodeSent models.EventType = "user.password.code.sent"
|
||||||
|
UserPasswordCheckSucceeded models.EventType = "user.password.check.succeeded"
|
||||||
|
UserPasswordCheckFailed models.EventType = "user.password.check.failed"
|
||||||
|
|
||||||
UserEmailChanged models.EventType = "user.email.changed"
|
UserEmailChanged models.EventType = "user.email.changed"
|
||||||
UserEmailVerified models.EventType = "user.email.verified"
|
UserEmailVerified models.EventType = "user.email.verified"
|
||||||
@@ -40,8 +42,12 @@ const (
|
|||||||
UserProfileChanged models.EventType = "user.profile.changed"
|
UserProfileChanged models.EventType = "user.profile.changed"
|
||||||
UserAddressChanged models.EventType = "user.address.changed"
|
UserAddressChanged models.EventType = "user.address.changed"
|
||||||
|
|
||||||
MfaOtpAdded models.EventType = "user.mfa.otp.added"
|
MfaOtpAdded models.EventType = "user.mfa.otp.added"
|
||||||
MfaOtpVerified models.EventType = "user.mfa.otp.verified"
|
MfaOtpVerified models.EventType = "user.mfa.otp.verified"
|
||||||
MfaOtpRemoved models.EventType = "user.mfa.otp.removed"
|
MfaOtpRemoved models.EventType = "user.mfa.otp.removed"
|
||||||
MfaInitSkipped models.EventType = "user.mfa.init.skipped"
|
MfaOtpCheckSucceeded models.EventType = "user.mfa.otp.check.succeeded"
|
||||||
|
MfaOtpCheckFailed models.EventType = "user.mfa.otp.check.failed"
|
||||||
|
MfaInitSkipped models.EventType = "user.mfa.init.skipped"
|
||||||
|
|
||||||
|
SignedOut models.EventType = "user.signed.out"
|
||||||
)
|
)
|
||||||
|
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/caos/zitadel/internal/errors"
|
"github.com/caos/zitadel/internal/errors"
|
||||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||||
|
es_sdk "github.com/caos/zitadel/internal/eventstore/sdk"
|
||||||
"github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
"github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -167,6 +168,25 @@ func PasswordChangeAggregate(aggCreator *es_models.AggregateCreator, existing *m
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PasswordCheckSucceededAggregate(aggCreator *es_models.AggregateCreator, existing *model.User, check *model.AuthRequest) es_sdk.AggregateFunc {
|
||||||
|
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||||
|
agg, err := UserAggregate(ctx, aggCreator, existing)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return agg.AppendEvent(model.UserPasswordCheckSucceeded, check)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func PasswordCheckFailedAggregate(aggCreator *es_models.AggregateCreator, existing *model.User, check *model.AuthRequest) es_sdk.AggregateFunc {
|
||||||
|
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||||
|
agg, err := UserAggregate(ctx, aggCreator, existing)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return agg.AppendEvent(model.UserPasswordCheckFailed, check)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func RequestSetPassword(aggCreator *es_models.AggregateCreator, existing *model.User, request *model.PasswordCode) func(ctx context.Context) (*es_models.Aggregate, error) {
|
func RequestSetPassword(aggCreator *es_models.AggregateCreator, existing *model.User, request *model.PasswordCode) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||||
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||||
if request == nil {
|
if request == nil {
|
||||||
@@ -338,6 +358,32 @@ func MfaOTPVerifyAggregate(aggCreator *es_models.AggregateCreator, existing *mod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MfaOTPCheckSucceededAggregate(aggCreator *es_models.AggregateCreator, existing *model.User, authReq *model.AuthRequest) es_sdk.AggregateFunc {
|
||||||
|
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||||
|
if authReq == nil {
|
||||||
|
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-sd5DA", "authReq must not be nil")
|
||||||
|
}
|
||||||
|
agg, err := UserAggregate(ctx, aggCreator, existing)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return agg.AppendEvent(model.MfaOtpCheckSucceeded, authReq)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MfaOTPCheckFailedAggregate(aggCreator *es_models.AggregateCreator, existing *model.User, authReq *model.AuthRequest) es_sdk.AggregateFunc {
|
||||||
|
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||||
|
if authReq == nil {
|
||||||
|
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-64sd6", "authReq must not be nil")
|
||||||
|
}
|
||||||
|
agg, err := UserAggregate(ctx, aggCreator, existing)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return agg.AppendEvent(model.MfaOtpCheckFailed, authReq)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func MfaOTPRemoveAggregate(aggCreator *es_models.AggregateCreator, existing *model.User) func(ctx context.Context) (*es_models.Aggregate, error) {
|
func MfaOTPRemoveAggregate(aggCreator *es_models.AggregateCreator, existing *model.User) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||||
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||||
agg, err := UserAggregate(ctx, aggCreator, existing)
|
agg, err := UserAggregate(ctx, aggCreator, existing)
|
||||||
@@ -347,3 +393,13 @@ func MfaOTPRemoveAggregate(aggCreator *es_models.AggregateCreator, existing *mod
|
|||||||
return agg.AppendEvent(model.MfaOtpRemoved, nil)
|
return agg.AppendEvent(model.MfaOtpRemoved, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SignOutAggregate(aggCreator *es_models.AggregateCreator, existing *model.User, agentID string) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||||
|
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||||
|
agg, err := UserAggregate(ctx, aggCreator, existing)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return agg.AppendEvent(model.SignedOut, map[string]interface{}{"agentID": agentID})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -2,12 +2,15 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
"github.com/caos/logging"
|
||||||
|
|
||||||
|
req_model "github.com/caos/zitadel/internal/auth_request/model"
|
||||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
"github.com/caos/zitadel/internal/eventstore/models"
|
"github.com/caos/zitadel/internal/eventstore/models"
|
||||||
"github.com/caos/zitadel/internal/user/model"
|
"github.com/caos/zitadel/internal/user/model"
|
||||||
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -23,90 +26,102 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type UserView struct {
|
type UserView struct {
|
||||||
ID string `json:"-" gorm:"column:id;primary_key"`
|
ID string `json:"-" gorm:"column:id;primary_key"`
|
||||||
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
|
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
|
||||||
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
|
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
|
||||||
ResourceOwner string `json:"-" gorm:"column:resource_owner"`
|
ResourceOwner string `json:"-" gorm:"column:resource_owner"`
|
||||||
State int32 `json:"-" gorm:"column:user_state"`
|
State int32 `json:"-" gorm:"column:user_state"`
|
||||||
PasswordChanged time.Time `json:"-" gorm:"column:password_change"`
|
PasswordSet bool `json:"-" gorm:"column:password_set"`
|
||||||
LastLogin time.Time `json:"-" gorm:"column:last_login"`
|
PasswordChangeRequired bool `json:"-" gorm:"column:password_change_required"`
|
||||||
UserName string `json:"userName" gorm:"column:user_name"`
|
PasswordChanged time.Time `json:"-" gorm:"column:password_change"`
|
||||||
FirstName string `json:"firstName" gorm:"column:first_name"`
|
LastLogin time.Time `json:"-" gorm:"column:last_login"`
|
||||||
LastName string `json:"lastName" gorm:"column:last_name"`
|
UserName string `json:"userName" gorm:"column:user_name"`
|
||||||
NickName string `json:"nickName" gorm:"column:nick_name"`
|
FirstName string `json:"firstName" gorm:"column:first_name"`
|
||||||
DisplayName string `json:"displayName" gorm:"column:display_name"`
|
LastName string `json:"lastName" gorm:"column:last_name"`
|
||||||
PreferredLanguage string `json:"preferredLanguage" gorm:"column:preferred_language"`
|
NickName string `json:"nickName" gorm:"column:nick_name"`
|
||||||
Gender int32 `json:"gender" gorm:"column:gender"`
|
DisplayName string `json:"displayName" gorm:"column:display_name"`
|
||||||
Email string `json:"email" gorm:"column:email"`
|
PreferredLanguage string `json:"preferredLanguage" gorm:"column:preferred_language"`
|
||||||
IsEmailVerified bool `json:"-" gorm:"column:is_email_verified"`
|
Gender int32 `json:"gender" gorm:"column:gender"`
|
||||||
Phone string `json:"phone" gorm:"column:phone"`
|
Email string `json:"email" gorm:"column:email"`
|
||||||
IsPhoneVerified bool `json:"-" gorm:"column:is_phone_verified"`
|
IsEmailVerified bool `json:"-" gorm:"column:is_email_verified"`
|
||||||
Country string `json:"country" gorm:"column:country"`
|
Phone string `json:"phone" gorm:"column:phone"`
|
||||||
Locality string `json:"locality" gorm:"column:locality"`
|
IsPhoneVerified bool `json:"-" gorm:"column:is_phone_verified"`
|
||||||
PostalCode string `json:"postalCode" gorm:"column:postal_code"`
|
Country string `json:"country" gorm:"column:country"`
|
||||||
Region string `json:"region" gorm:"column:region"`
|
Locality string `json:"locality" gorm:"column:locality"`
|
||||||
StreetAddress string `json:"streetAddress" gorm:"column:street_address"`
|
PostalCode string `json:"postalCode" gorm:"column:postal_code"`
|
||||||
OTPState int32 `json:"-" gorm:"column:otp_state"`
|
Region string `json:"region" gorm:"column:region"`
|
||||||
Sequence uint64 `json:"-" gorm:"column:sequence"`
|
StreetAddress string `json:"streetAddress" gorm:"column:street_address"`
|
||||||
|
OTPState int32 `json:"-" gorm:"column:otp_state"`
|
||||||
|
MfaMaxSetUp int32 `json:"-" gorm:"column:mfa_max_set_up"`
|
||||||
|
MfaInitSkipped time.Time `json:"-" gorm:"column:mfa_init_skipped"`
|
||||||
|
Sequence uint64 `json:"-" gorm:"column:sequence"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserFromModel(user *model.UserView) *UserView {
|
func UserFromModel(user *model.UserView) *UserView {
|
||||||
return &UserView{
|
return &UserView{
|
||||||
ID: user.ID,
|
ID: user.ID,
|
||||||
ChangeDate: user.ChangeDate,
|
ChangeDate: user.ChangeDate,
|
||||||
CreationDate: user.CreationDate,
|
CreationDate: user.CreationDate,
|
||||||
ResourceOwner: user.ResourceOwner,
|
ResourceOwner: user.ResourceOwner,
|
||||||
State: int32(user.State),
|
State: int32(user.State),
|
||||||
PasswordChanged: user.PasswordChanged,
|
PasswordSet: user.PasswordSet,
|
||||||
LastLogin: user.LastLogin,
|
PasswordChangeRequired: user.PasswordChangeRequired,
|
||||||
UserName: user.UserName,
|
PasswordChanged: user.PasswordChanged,
|
||||||
FirstName: user.FirstName,
|
LastLogin: user.LastLogin,
|
||||||
LastName: user.LastName,
|
UserName: user.UserName,
|
||||||
NickName: user.NickName,
|
FirstName: user.FirstName,
|
||||||
DisplayName: user.DisplayName,
|
LastName: user.LastName,
|
||||||
PreferredLanguage: user.PreferredLanguage,
|
NickName: user.NickName,
|
||||||
Gender: int32(user.Gender),
|
DisplayName: user.DisplayName,
|
||||||
Email: user.Email,
|
PreferredLanguage: user.PreferredLanguage,
|
||||||
IsEmailVerified: user.IsEmailVerified,
|
Gender: int32(user.Gender),
|
||||||
Phone: user.Phone,
|
Email: user.Email,
|
||||||
IsPhoneVerified: user.IsPhoneVerified,
|
IsEmailVerified: user.IsEmailVerified,
|
||||||
Country: user.Country,
|
Phone: user.Phone,
|
||||||
Locality: user.Locality,
|
IsPhoneVerified: user.IsPhoneVerified,
|
||||||
PostalCode: user.PostalCode,
|
Country: user.Country,
|
||||||
Region: user.Region,
|
Locality: user.Locality,
|
||||||
StreetAddress: user.StreetAddress,
|
PostalCode: user.PostalCode,
|
||||||
OTPState: int32(user.OTPState),
|
Region: user.Region,
|
||||||
Sequence: user.Sequence,
|
StreetAddress: user.StreetAddress,
|
||||||
|
OTPState: int32(user.OTPState),
|
||||||
|
MfaMaxSetUp: int32(user.MfaMaxSetUp),
|
||||||
|
MfaInitSkipped: user.MfaInitSkipped,
|
||||||
|
Sequence: user.Sequence,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserToModel(user *UserView) *model.UserView {
|
func UserToModel(user *UserView) *model.UserView {
|
||||||
return &model.UserView{
|
return &model.UserView{
|
||||||
ID: user.ID,
|
ID: user.ID,
|
||||||
ChangeDate: user.ChangeDate,
|
ChangeDate: user.ChangeDate,
|
||||||
CreationDate: user.CreationDate,
|
CreationDate: user.CreationDate,
|
||||||
ResourceOwner: user.ResourceOwner,
|
ResourceOwner: user.ResourceOwner,
|
||||||
State: model.UserState(user.State),
|
State: model.UserState(user.State),
|
||||||
PasswordChanged: user.PasswordChanged,
|
PasswordSet: user.PasswordSet,
|
||||||
LastLogin: user.LastLogin,
|
PasswordChangeRequired: user.PasswordChangeRequired,
|
||||||
UserName: user.UserName,
|
PasswordChanged: user.PasswordChanged,
|
||||||
FirstName: user.FirstName,
|
LastLogin: user.LastLogin,
|
||||||
LastName: user.LastName,
|
UserName: user.UserName,
|
||||||
NickName: user.NickName,
|
FirstName: user.FirstName,
|
||||||
DisplayName: user.DisplayName,
|
LastName: user.LastName,
|
||||||
PreferredLanguage: user.PreferredLanguage,
|
NickName: user.NickName,
|
||||||
Gender: model.Gender(user.Gender),
|
DisplayName: user.DisplayName,
|
||||||
Email: user.Email,
|
PreferredLanguage: user.PreferredLanguage,
|
||||||
IsEmailVerified: user.IsEmailVerified,
|
Gender: model.Gender(user.Gender),
|
||||||
Phone: user.Phone,
|
Email: user.Email,
|
||||||
IsPhoneVerified: user.IsPhoneVerified,
|
IsEmailVerified: user.IsEmailVerified,
|
||||||
Country: user.Country,
|
Phone: user.Phone,
|
||||||
Locality: user.Locality,
|
IsPhoneVerified: user.IsPhoneVerified,
|
||||||
PostalCode: user.PostalCode,
|
Country: user.Country,
|
||||||
Region: user.Region,
|
Locality: user.Locality,
|
||||||
StreetAddress: user.StreetAddress,
|
PostalCode: user.PostalCode,
|
||||||
OTPState: model.MfaState(user.OTPState),
|
Region: user.Region,
|
||||||
Sequence: user.Sequence,
|
StreetAddress: user.StreetAddress,
|
||||||
|
OTPState: model.MfaState(user.OTPState),
|
||||||
|
MfaMaxSetUp: req_model.MfaLevel(user.MfaMaxSetUp),
|
||||||
|
MfaInitSkipped: user.MfaInitSkipped,
|
||||||
|
Sequence: user.Sequence,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,43 +133,52 @@ func UsersToModel(users []*UserView) []*model.UserView {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *UserView) AppendEvent(event *models.Event) (err error) {
|
func (u *UserView) AppendEvent(event *models.Event) (err error) {
|
||||||
p.ChangeDate = event.CreationDate
|
u.ChangeDate = event.CreationDate
|
||||||
p.Sequence = event.Sequence
|
u.Sequence = event.Sequence
|
||||||
switch event.Type {
|
switch event.Type {
|
||||||
case es_model.UserAdded,
|
case es_model.UserAdded,
|
||||||
es_model.UserRegistered:
|
es_model.UserRegistered:
|
||||||
p.CreationDate = event.CreationDate
|
u.CreationDate = event.CreationDate
|
||||||
p.setRootData(event)
|
u.setRootData(event)
|
||||||
err = p.setData(event)
|
err = u.setData(event)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = u.setPasswordData(event)
|
||||||
|
case es_model.UserPasswordChanged:
|
||||||
|
err = u.setPasswordData(event)
|
||||||
case es_model.UserProfileChanged,
|
case es_model.UserProfileChanged,
|
||||||
es_model.UserAddressChanged:
|
es_model.UserAddressChanged:
|
||||||
err = p.setData(event)
|
err = u.setData(event)
|
||||||
case es_model.UserEmailChanged:
|
case es_model.UserEmailChanged:
|
||||||
p.IsEmailVerified = false
|
u.IsEmailVerified = false
|
||||||
err = p.setData(event)
|
err = u.setData(event)
|
||||||
case es_model.UserEmailVerified:
|
case es_model.UserEmailVerified:
|
||||||
p.IsEmailVerified = true
|
u.IsEmailVerified = true
|
||||||
case es_model.UserPhoneChanged:
|
case es_model.UserPhoneChanged:
|
||||||
p.IsPhoneVerified = false
|
u.IsPhoneVerified = false
|
||||||
err = p.setData(event)
|
err = u.setData(event)
|
||||||
case es_model.UserPhoneVerified:
|
case es_model.UserPhoneVerified:
|
||||||
p.IsPhoneVerified = true
|
u.IsPhoneVerified = true
|
||||||
case es_model.UserDeactivated:
|
case es_model.UserDeactivated:
|
||||||
p.State = int32(model.USERSTATE_INACTIVE)
|
u.State = int32(model.USERSTATE_INACTIVE)
|
||||||
case es_model.UserReactivated,
|
case es_model.UserReactivated,
|
||||||
es_model.UserUnlocked:
|
es_model.UserUnlocked:
|
||||||
p.State = int32(model.USERSTATE_ACTIVE)
|
u.State = int32(model.USERSTATE_ACTIVE)
|
||||||
case es_model.UserLocked:
|
case es_model.UserLocked:
|
||||||
p.State = int32(model.USERSTATE_LOCKED)
|
u.State = int32(model.USERSTATE_LOCKED)
|
||||||
case es_model.MfaOtpAdded:
|
case es_model.MfaOtpAdded:
|
||||||
p.OTPState = int32(model.MFASTATE_NOTREADY)
|
u.OTPState = int32(model.MFASTATE_NOTREADY)
|
||||||
case es_model.MfaOtpVerified:
|
case es_model.MfaOtpVerified:
|
||||||
p.OTPState = int32(model.MFASTATE_READY)
|
u.OTPState = int32(model.MFASTATE_READY)
|
||||||
|
u.MfaInitSkipped = time.Time{}
|
||||||
case es_model.MfaOtpRemoved:
|
case es_model.MfaOtpRemoved:
|
||||||
p.OTPState = int32(model.MFASTATE_UNSPECIFIED)
|
u.OTPState = int32(model.MFASTATE_UNSPECIFIED)
|
||||||
|
case es_model.MfaInitSkipped:
|
||||||
|
u.MfaInitSkipped = event.CreationDate
|
||||||
}
|
}
|
||||||
p.ComputeObject()
|
u.ComputeObject()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,12 +189,23 @@ func (u *UserView) setRootData(event *models.Event) {
|
|||||||
|
|
||||||
func (u *UserView) setData(event *models.Event) error {
|
func (u *UserView) setData(event *models.Event) error {
|
||||||
if err := json.Unmarshal(event.Data, u); err != nil {
|
if err := json.Unmarshal(event.Data, u); err != nil {
|
||||||
logging.Log("EVEN-lso9e").WithError(err).Error("could not unmarshal event data")
|
logging.Log("MODEL-lso9e").WithError(err).Error("could not unmarshal event data")
|
||||||
return caos_errs.ThrowInternal(nil, "MODEL-8iows", "could not unmarshal data")
|
return caos_errs.ThrowInternal(nil, "MODEL-8iows", "could not unmarshal data")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserView) setPasswordData(event *models.Event) error {
|
||||||
|
password := new(es_model.Password)
|
||||||
|
if err := json.Unmarshal(event.Data, password); err != nil {
|
||||||
|
logging.Log("MODEL-sdw4r").WithError(err).Error("could not unmarshal event data")
|
||||||
|
return caos_errs.ThrowInternal(nil, "MODEL-6jhsw", "could not unmarshal data")
|
||||||
|
}
|
||||||
|
u.PasswordSet = password.Secret != nil
|
||||||
|
u.PasswordChangeRequired = password.ChangeRequired
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (u *UserView) ComputeObject() {
|
func (u *UserView) ComputeObject() {
|
||||||
if u.State == int32(model.USERSTATE_UNSPECIFIED) || u.State == int32(model.USERSTATE_INITIAL) {
|
if u.State == int32(model.USERSTATE_UNSPECIFIED) || u.State == int32(model.USERSTATE_INITIAL) {
|
||||||
if u.IsEmailVerified {
|
if u.IsEmailVerified {
|
||||||
@@ -179,4 +214,7 @@ func (u *UserView) ComputeObject() {
|
|||||||
u.State = int32(model.USERSTATE_INITIAL)
|
u.State = int32(model.USERSTATE_INITIAL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if u.OTPState == int32(model.MFASTATE_READY) {
|
||||||
|
u.MfaMaxSetUp = int32(req_model.MfaLevelSoftware)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
91
internal/user/repository/view/model/user_session.go
Normal file
91
internal/user/repository/view/model/user_session.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/caos/logging"
|
||||||
|
|
||||||
|
req_model "github.com/caos/zitadel/internal/auth_request/model"
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
UserSessionKeySessionID = "id"
|
||||||
|
UserSessionKeyUserAgentID = "user_agent_id"
|
||||||
|
UserSessionKeyUserID = "user_id"
|
||||||
|
UserSessionKeyState = "state"
|
||||||
|
UserSessionKeyResourceOwner = "resource_owner"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserSessionView 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:state"`
|
||||||
|
UserAgentID string `json:"userAgentID" gorm:"column:user_agent_id"`
|
||||||
|
UserID string `json:"userID" gorm:"column:user_id"`
|
||||||
|
UserName string `json:"userName" gorm:"column:user_name"`
|
||||||
|
PasswordVerification time.Time `json:"-" gorm:"column:password_verification"`
|
||||||
|
MfaSoftwareVerification time.Time `json:"-" gorm:"column:mfa_software_verification"`
|
||||||
|
MfaHardwareVerification time.Time `json:"-" gorm:"column:mfa_hardware_verification"`
|
||||||
|
Sequence uint64 `json:"-" gorm:"column:sequence"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserSessionFromEvent(event *models.Event) (*UserSessionView, error) {
|
||||||
|
v := new(UserSessionView)
|
||||||
|
if err := json.Unmarshal(event.Data, v); err != nil {
|
||||||
|
logging.Log("EVEN-lso9e").WithError(err).Error("could not unmarshal event data")
|
||||||
|
return nil, caos_errs.ThrowInternal(nil, "MODEL-sd325", "could not unmarshal data")
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserSessionToModel(userSession *UserSessionView) *model.UserSessionView {
|
||||||
|
return &model.UserSessionView{
|
||||||
|
ID: userSession.ID,
|
||||||
|
ChangeDate: userSession.ChangeDate,
|
||||||
|
CreationDate: userSession.CreationDate,
|
||||||
|
ResourceOwner: userSession.ResourceOwner,
|
||||||
|
State: req_model.UserSessionState(userSession.State),
|
||||||
|
UserAgentID: userSession.UserAgentID,
|
||||||
|
UserID: userSession.UserID,
|
||||||
|
UserName: userSession.UserName,
|
||||||
|
PasswordVerification: userSession.PasswordVerification,
|
||||||
|
MfaSoftwareVerification: userSession.MfaSoftwareVerification,
|
||||||
|
MfaHardwareVerification: userSession.MfaHardwareVerification,
|
||||||
|
Sequence: userSession.Sequence,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserSessionsToModel(userSessions []*UserSessionView) []*model.UserSessionView {
|
||||||
|
result := make([]*model.UserSessionView, len(userSessions))
|
||||||
|
for i, s := range userSessions {
|
||||||
|
result[i] = UserSessionToModel(s)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *UserSessionView) AppendEvent(event *models.Event) {
|
||||||
|
v.ChangeDate = event.CreationDate
|
||||||
|
switch event.Type {
|
||||||
|
case es_model.UserPasswordCheckSucceeded:
|
||||||
|
v.PasswordVerification = event.CreationDate
|
||||||
|
case es_model.UserPasswordCheckFailed,
|
||||||
|
es_model.UserPasswordChanged:
|
||||||
|
v.PasswordVerification = time.Time{}
|
||||||
|
case es_model.MfaOtpCheckSucceeded:
|
||||||
|
v.MfaSoftwareVerification = event.CreationDate
|
||||||
|
case es_model.MfaOtpCheckFailed,
|
||||||
|
es_model.MfaOtpRemoved:
|
||||||
|
v.MfaSoftwareVerification = time.Time{}
|
||||||
|
case es_model.SignedOut:
|
||||||
|
v.PasswordVerification = time.Time{}
|
||||||
|
v.MfaSoftwareVerification = time.Time{}
|
||||||
|
v.State = int32(req_model.UserSessionStateTerminated)
|
||||||
|
}
|
||||||
|
}
|
67
internal/user/repository/view/model/user_session_query.go
Normal file
67
internal/user/repository/view/model/user_session_query.go
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
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 UserSessionSearchRequest usr_model.UserSessionSearchRequest
|
||||||
|
type UserSessionSearchQuery usr_model.UserSessionSearchQuery
|
||||||
|
type UserSessionSearchKey usr_model.UserSessionSearchKey
|
||||||
|
|
||||||
|
func (req UserSessionSearchRequest) GetLimit() uint64 {
|
||||||
|
return req.Limit
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req UserSessionSearchRequest) GetOffset() uint64 {
|
||||||
|
return req.Offset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req UserSessionSearchRequest) GetSortingColumn() view.ColumnKey {
|
||||||
|
if req.SortingColumn == usr_model.USERSESSIONSEARCHKEY_UNSPECIFIED {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return UserSessionSearchKey(req.SortingColumn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req UserSessionSearchRequest) GetAsc() bool {
|
||||||
|
return req.Asc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req UserSessionSearchRequest) GetQueries() []view.SearchQuery {
|
||||||
|
result := make([]view.SearchQuery, len(req.Queries))
|
||||||
|
for i, q := range req.Queries {
|
||||||
|
result[i] = UserSessionSearchQuery{Key: q.Key, Value: q.Value, Method: q.Method}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req UserSessionSearchQuery) GetKey() view.ColumnKey {
|
||||||
|
return UserSessionSearchKey(req.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req UserSessionSearchQuery) GetMethod() global_model.SearchMethod {
|
||||||
|
return req.Method
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req UserSessionSearchQuery) GetValue() interface{} {
|
||||||
|
return req.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key UserSessionSearchKey) ToColumnName() string {
|
||||||
|
switch usr_model.UserSessionSearchKey(key) {
|
||||||
|
case usr_model.USERSESSIONSEARCHKEY_SESSION_ID:
|
||||||
|
return UserSessionKeySessionID
|
||||||
|
case usr_model.USERSESSIONSEARCHKEY_USER_AGENT_ID:
|
||||||
|
return UserSessionKeyUserAgentID
|
||||||
|
case usr_model.USERSESSIONSEARCHKEY_USER_ID:
|
||||||
|
return UserSessionKeyUserID
|
||||||
|
case usr_model.USERSESSIONSEARCHKEY_STATE:
|
||||||
|
return UserSessionKeyState
|
||||||
|
case usr_model.USERSESSIONSEARCHKEY_RESOURCEOWNER:
|
||||||
|
return UserSessionKeyResourceOwner
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
90
internal/user/repository/view/model/user_session_test.go
Normal file
90
internal/user/repository/view/model/user_session_test.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||||
|
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func now() time.Time {
|
||||||
|
return time.Now().UTC().Round(1 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppendEvent(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
event *es_models.Event
|
||||||
|
userView *UserSessionView
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
result *UserSessionView
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "append password check succeeded event",
|
||||||
|
args: args{
|
||||||
|
event: &es_models.Event{CreationDate: now(), Type: es_model.UserPasswordCheckSucceeded},
|
||||||
|
userView: &UserSessionView{},
|
||||||
|
},
|
||||||
|
result: &UserSessionView{ChangeDate: now(), PasswordVerification: now()},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "append password check failed event",
|
||||||
|
args: args{
|
||||||
|
event: &es_models.Event{CreationDate: now(), Type: es_model.UserPasswordCheckFailed},
|
||||||
|
userView: &UserSessionView{PasswordVerification: now()},
|
||||||
|
},
|
||||||
|
result: &UserSessionView{ChangeDate: now(), PasswordVerification: time.Time{}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "append password changed event",
|
||||||
|
args: args{
|
||||||
|
event: &es_models.Event{CreationDate: now(), Type: es_model.UserPasswordChanged},
|
||||||
|
userView: &UserSessionView{PasswordVerification: now()},
|
||||||
|
},
|
||||||
|
result: &UserSessionView{ChangeDate: now(), PasswordVerification: time.Time{}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "append otp check succeeded event",
|
||||||
|
args: args{
|
||||||
|
event: &es_models.Event{CreationDate: now(), Type: es_model.MfaOtpCheckSucceeded},
|
||||||
|
userView: &UserSessionView{},
|
||||||
|
},
|
||||||
|
result: &UserSessionView{ChangeDate: now(), MfaSoftwareVerification: now()},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "append otp check failed event",
|
||||||
|
args: args{
|
||||||
|
event: &es_models.Event{CreationDate: now(), Type: es_model.MfaOtpCheckFailed},
|
||||||
|
userView: &UserSessionView{MfaSoftwareVerification: now()},
|
||||||
|
},
|
||||||
|
result: &UserSessionView{ChangeDate: now(), MfaSoftwareVerification: time.Time{}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "append otp removed event",
|
||||||
|
args: args{
|
||||||
|
event: &es_models.Event{CreationDate: now(), Type: es_model.MfaOtpCheckFailed},
|
||||||
|
userView: &UserSessionView{MfaSoftwareVerification: now()},
|
||||||
|
},
|
||||||
|
result: &UserSessionView{ChangeDate: now(), MfaSoftwareVerification: time.Time{}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "append otp removed event",
|
||||||
|
args: args{
|
||||||
|
event: &es_models.Event{CreationDate: now(), Type: es_model.SignedOut},
|
||||||
|
userView: &UserSessionView{PasswordVerification: now(), MfaSoftwareVerification: now()},
|
||||||
|
},
|
||||||
|
result: &UserSessionView{ChangeDate: now(), PasswordVerification: time.Time{}, MfaSoftwareVerification: time.Time{}, State: 1},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
tt.args.userView.AppendEvent(tt.args.event)
|
||||||
|
assert.Equal(t, tt.result, tt.args.userView)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@@ -2,10 +2,13 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||||
"github.com/caos/zitadel/internal/user/model"
|
"github.com/caos/zitadel/internal/user/model"
|
||||||
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||||
"testing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func mockUserData(user *es_model.User) []byte {
|
func mockUserData(user *es_model.User) []byte {
|
||||||
@@ -13,6 +16,11 @@ func mockUserData(user *es_model.User) []byte {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mockPasswordData(password *es_model.Password) []byte {
|
||||||
|
data, _ := json.Marshal(password)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
func mockProfileData(profile *es_model.Profile) []byte {
|
func mockProfileData(profile *es_model.Profile) []byte {
|
||||||
data, _ := json.Marshal(profile)
|
data, _ := json.Marshal(profile)
|
||||||
return data
|
return data
|
||||||
@@ -33,7 +41,7 @@ func mockAddressData(address *es_model.Address) []byte {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFullUser() *es_model.User {
|
func getFullUser(password *es_model.Password) *es_model.User {
|
||||||
return &es_model.User{
|
return &es_model.User{
|
||||||
Profile: &es_model.Profile{
|
Profile: &es_model.Profile{
|
||||||
UserName: "UserName",
|
UserName: "UserName",
|
||||||
@@ -49,6 +57,7 @@ func getFullUser() *es_model.User {
|
|||||||
Address: &es_model.Address{
|
Address: &es_model.Address{
|
||||||
Country: "Country",
|
Country: "Country",
|
||||||
},
|
},
|
||||||
|
Password: password,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,11 +74,43 @@ func TestUserAppendEvent(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "append added user event",
|
name: "append added user event",
|
||||||
args: args{
|
args: args{
|
||||||
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.UserAdded, ResourceOwner: "OrgID", Data: mockUserData(getFullUser())},
|
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.UserAdded, ResourceOwner: "OrgID", Data: mockUserData(getFullUser(nil))},
|
||||||
user: &UserView{},
|
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)},
|
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 added user with password event",
|
||||||
|
args: args{
|
||||||
|
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.UserAdded, ResourceOwner: "OrgID", Data: mockUserData(getFullUser(&es_model.Password{Secret: &crypto.CryptoValue{}}))},
|
||||||
|
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), PasswordSet: true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "append added user with password but change required event",
|
||||||
|
args: args{
|
||||||
|
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.UserAdded, ResourceOwner: "OrgID", Data: mockUserData(getFullUser(&es_model.Password{ChangeRequired: true, Secret: &crypto.CryptoValue{}}))},
|
||||||
|
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), PasswordSet: true, PasswordChangeRequired: true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "append password change event",
|
||||||
|
args: args{
|
||||||
|
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.UserPasswordChanged, ResourceOwner: "OrgID", Data: mockPasswordData(&es_model.Password{Secret: &crypto.CryptoValue{}})},
|
||||||
|
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: "Country", State: int32(model.USERSTATE_ACTIVE), PasswordSet: true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "append password change with change required event",
|
||||||
|
args: args{
|
||||||
|
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_model.UserPasswordChanged, ResourceOwner: "OrgID", Data: mockPasswordData(&es_model.Password{ChangeRequired: true, Secret: &crypto.CryptoValue{}})},
|
||||||
|
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: "Country", State: int32(model.USERSTATE_ACTIVE), PasswordSet: true, PasswordChangeRequired: true},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "append change user profile event",
|
name: "append change user profile event",
|
||||||
args: args{
|
args: args{
|
||||||
@@ -174,6 +215,14 @@ func TestUserAppendEvent(t *testing.T) {
|
|||||||
},
|
},
|
||||||
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)},
|
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)},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "append mfa init skipped event",
|
||||||
|
args: args{
|
||||||
|
event: &es_models.Event{Sequence: 1, CreationDate: time.Now().UTC(), Type: es_model.MfaInitSkipped, AggregateID: "AggregateID", 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), MfaInitSkipped: time.Now().UTC()},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
@@ -211,6 +260,15 @@ func TestUserAppendEvent(t *testing.T) {
|
|||||||
if tt.args.user.OTPState != tt.result.OTPState {
|
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)
|
t.Errorf("got wrong result OTPState: expected: %v, actual: %v ", tt.result.OTPState, tt.args.user.OTPState)
|
||||||
}
|
}
|
||||||
|
if tt.args.user.MfaInitSkipped.Round(1*time.Second) != tt.result.MfaInitSkipped.Round(1*time.Second) {
|
||||||
|
t.Errorf("got wrong result MfaInitSkipped: expected: %v, actual: %v ", tt.result.MfaInitSkipped.Round(1*time.Second), tt.args.user.MfaInitSkipped.Round(1*time.Second))
|
||||||
|
}
|
||||||
|
if tt.args.user.PasswordSet != tt.result.PasswordSet {
|
||||||
|
t.Errorf("got wrong result PasswordSet: expected: %v, actual: %v ", tt.result.PasswordSet, tt.args.user.PasswordSet)
|
||||||
|
}
|
||||||
|
if tt.args.user.PasswordChangeRequired != tt.result.PasswordChangeRequired {
|
||||||
|
t.Errorf("got wrong result PasswordChangeRequired: expected: %v, actual: %v ", tt.result.PasswordChangeRequired, tt.args.user.PasswordChangeRequired)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
58
internal/user/repository/view/user_session_view.go
Normal file
58
internal/user/repository/view/user_session_view.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package view
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
|
||||||
|
global_model "github.com/caos/zitadel/internal/model"
|
||||||
|
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||||
|
"github.com/caos/zitadel/internal/user/repository/view/model"
|
||||||
|
"github.com/caos/zitadel/internal/view"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UserSessionByID(db *gorm.DB, table, sessionID string) (*model.UserSessionView, error) {
|
||||||
|
userSession := new(model.UserSessionView)
|
||||||
|
query := view.PrepareGetByKey(table, model.UserSessionSearchKey(usr_model.USERSESSIONSEARCHKEY_SESSION_ID), sessionID)
|
||||||
|
err := query(db, userSession)
|
||||||
|
return userSession, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserSessionByIDs(db *gorm.DB, table, agentID, userID string) (*model.UserSessionView, error) {
|
||||||
|
userSession := new(model.UserSessionView)
|
||||||
|
userAgentQuery := model.UserSessionSearchQuery{
|
||||||
|
Key: usr_model.USERSESSIONSEARCHKEY_USER_AGENT_ID,
|
||||||
|
Method: global_model.SEARCHMETHOD_EQUALS,
|
||||||
|
Value: agentID,
|
||||||
|
}
|
||||||
|
userQuery := model.UserSessionSearchQuery{
|
||||||
|
Key: usr_model.USERSESSIONSEARCHKEY_USER_ID,
|
||||||
|
Method: global_model.SEARCHMETHOD_EQUALS,
|
||||||
|
Value: userID,
|
||||||
|
}
|
||||||
|
query := view.PrepareGetByQuery(table, userAgentQuery, userQuery)
|
||||||
|
err := query(db, userSession)
|
||||||
|
return userSession, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserSessionsByAgentID(db *gorm.DB, table, agentID string) ([]*model.UserSessionView, error) {
|
||||||
|
userSessions := make([]*model.UserSessionView, 0)
|
||||||
|
userAgentQuery := &usr_model.UserSessionSearchQuery{
|
||||||
|
Key: usr_model.USERSESSIONSEARCHKEY_USER_AGENT_ID,
|
||||||
|
Method: global_model.SEARCHMETHOD_EQUALS,
|
||||||
|
Value: agentID,
|
||||||
|
}
|
||||||
|
query := view.PrepareSearchQuery(table, model.UserSessionSearchRequest{
|
||||||
|
Queries: []*usr_model.UserSessionSearchQuery{userAgentQuery},
|
||||||
|
})
|
||||||
|
_, err := query(db, userSessions)
|
||||||
|
return userSessions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func PutUserSession(db *gorm.DB, table string, session *model.UserSessionView) error {
|
||||||
|
save := view.PrepareSave(table)
|
||||||
|
return save(db, session)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteUserSession(db *gorm.DB, table, sessionID string) error {
|
||||||
|
delete := view.PrepareDeleteByKey(table, model.UserSessionSearchKey(usr_model.USERSESSIONSEARCHKEY_USER_ID), sessionID)
|
||||||
|
return delete(db)
|
||||||
|
}
|
@@ -1,19 +1,31 @@
|
|||||||
package view
|
package view
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/DATA-DOG/go-sqlmock"
|
"strconv"
|
||||||
"github.com/caos/zitadel/internal/model"
|
|
||||||
"github.com/jinzhu/gorm"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
expectedGetByID = `SELECT \* FROM "%s" WHERE \(%s = \$1\) LIMIT 1`
|
expectedGetByID = `SELECT \* FROM "%s" WHERE \(%s = \$1\) LIMIT 1`
|
||||||
expectedGetByQuery = `SELECT \* FROM "%s" WHERE \(LOWER\(%s\) %s LOWER\(\$1\)\) LIMIT 1`
|
expectedGetByQuery = `SELECT \* FROM "%s" WHERE \(LOWER\(%s\) %s LOWER\(\$1\)\) LIMIT 1`
|
||||||
expectedGetByQueryCaseSensitive = `SELECT \* FROM "%s" WHERE \(%s %s \$1\) LIMIT 1`
|
expectedGetByQueryCaseSensitive = `SELECT \* FROM "%s" WHERE \(%s %s \$1\) LIMIT 1`
|
||||||
expectedSave = `UPDATE "%s" SET "test" = \$1 WHERE "%s"."%s" = \$2`
|
expectedSave = `UPDATE "%s" SET "test" = \$1 WHERE "%s"."%s" = \$2`
|
||||||
expectedRemove = `DELETE FROM "%s" WHERE \(%s = \$1\)`
|
expectedRemove = `DELETE FROM "%s" WHERE \(%s = \$1\)`
|
||||||
|
expectedRemoveByKeys = func(i int, table string) string {
|
||||||
|
sql := fmt.Sprintf(`DELETE FROM "%s"`, table)
|
||||||
|
sql += ` WHERE \(%s = \$1\)`
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
sql = sql + ` AND \(%s = \$` + strconv.Itoa(j+1) + `\)`
|
||||||
|
}
|
||||||
|
return sql
|
||||||
|
}
|
||||||
expectedRemoveByObject = `DELETE FROM "%s" WHERE "%s"."%s" = \$1`
|
expectedRemoveByObject = `DELETE FROM "%s" WHERE "%s"."%s" = \$1`
|
||||||
expectedRemoveByObjectMultiplePK = `DELETE FROM "%s" WHERE "%s"."%s" = \$1 AND "%s"."%s" = \$2`
|
expectedRemoveByObjectMultiplePK = `DELETE FROM "%s" WHERE "%s"."%s" = \$1 AND "%s"."%s" = \$2`
|
||||||
expectedSearch = `SELECT \* FROM "%s" OFFSET 0`
|
expectedSearch = `SELECT \* FROM "%s" OFFSET 0`
|
||||||
@@ -235,6 +247,21 @@ func (db *dbMock) expectRemove(table, key, value string) *dbMock {
|
|||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *dbMock) expectRemoveKeys(table string, keys ...Key) *dbMock {
|
||||||
|
keynames := make([]interface{}, len(keys))
|
||||||
|
keyvalues := make([]driver.Value, len(keys))
|
||||||
|
for i, key := range keys {
|
||||||
|
keynames[i] = key.Key.ToColumnName()
|
||||||
|
keyvalues[i] = key.Value
|
||||||
|
}
|
||||||
|
query := fmt.Sprintf(expectedRemoveByKeys(len(keys), table), keynames...)
|
||||||
|
db.mock.ExpectExec(query).
|
||||||
|
WithArgs(keyvalues...).
|
||||||
|
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||||
|
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
func (db *dbMock) expectRemoveByObject(table string, object Test) *dbMock {
|
func (db *dbMock) expectRemoveByObject(table string, object Test) *dbMock {
|
||||||
query := fmt.Sprintf(expectedRemoveByObject, table, table, "primary_id")
|
query := fmt.Sprintf(expectedRemoveByObject, table, table, "primary_id")
|
||||||
db.mock.ExpectExec(query).
|
db.mock.ExpectExec(query).
|
||||||
|
@@ -3,9 +3,11 @@ package view
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
"github.com/caos/logging"
|
||||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
|
||||||
"github.com/jinzhu/gorm"
|
"github.com/jinzhu/gorm"
|
||||||
|
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func PrepareGetByKey(table string, key ColumnKey, id string) func(db *gorm.DB, res interface{}) error {
|
func PrepareGetByKey(table string, key ColumnKey, id string) func(db *gorm.DB, res interface{}) error {
|
||||||
@@ -71,6 +73,27 @@ func PrepareDeleteByKey(table string, key ColumnKey, id string) func(db *gorm.DB
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Key struct {
|
||||||
|
Key ColumnKey
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrepareDeleteByKeys(table string, keys ...Key) func(db *gorm.DB) error {
|
||||||
|
return func(db *gorm.DB) error {
|
||||||
|
for _, key := range keys {
|
||||||
|
db = db.Table(table).
|
||||||
|
Where(fmt.Sprintf("%s = ?", key.Key.ToColumnName()), key.Value)
|
||||||
|
}
|
||||||
|
err := db.
|
||||||
|
Delete(nil).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
return caos_errs.ThrowInternal(err, "VIEW-die73", "could not delete object")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func PrepareDeleteByObject(table string, object interface{}) func(db *gorm.DB) error {
|
func PrepareDeleteByObject(table string, object interface{}) func(db *gorm.DB) error {
|
||||||
return func(db *gorm.DB) error {
|
return func(db *gorm.DB) error {
|
||||||
err := db.Table(table).
|
err := db.Table(table).
|
||||||
|
@@ -391,6 +391,97 @@ func TestPrepareDelete(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrepareDeleteByKeys(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
table string
|
||||||
|
keys []Key
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
result Test
|
||||||
|
wantErr bool
|
||||||
|
errFunc func(err error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
db *dbMock
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"delete single key",
|
||||||
|
mockDB(t).
|
||||||
|
expectBegin(nil).
|
||||||
|
expectRemoveKeys("TESTTABLE", Key{Key: TestSearchKey_ID, Value: "VALUE"}).
|
||||||
|
expectCommit(nil),
|
||||||
|
args{
|
||||||
|
table: "TESTTABLE",
|
||||||
|
keys: []Key{
|
||||||
|
{Key: TestSearchKey_ID, Value: "VALUE"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
result: Test{ID: "VALUE"},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"delete multiple keys",
|
||||||
|
mockDB(t).
|
||||||
|
expectBegin(nil).
|
||||||
|
expectRemoveKeys("TESTTABLE", Key{Key: TestSearchKey_ID, Value: "VALUE"}, Key{Key: TestSearchKey_TEST, Value: "VALUE2"}).
|
||||||
|
expectCommit(nil),
|
||||||
|
args{
|
||||||
|
table: "TESTTABLE",
|
||||||
|
keys: []Key{
|
||||||
|
{Key: TestSearchKey_ID, Value: "VALUE"},
|
||||||
|
{Key: TestSearchKey_TEST, Value: "VALUE2"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
result: Test{ID: "VALUE"},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"db error",
|
||||||
|
mockDB(t).
|
||||||
|
expectBegin(nil).
|
||||||
|
expectRemoveErr("TESTTABLE", "id", "VALUE", gorm.ErrUnaddressable).
|
||||||
|
expectCommit(nil),
|
||||||
|
args{
|
||||||
|
table: "TESTTABLE",
|
||||||
|
keys: []Key{
|
||||||
|
{Key: TestSearchKey_ID, Value: "VALUE"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
result: Test{ID: "VALUE"},
|
||||||
|
wantErr: true,
|
||||||
|
errFunc: caos_errs.IsInternal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
getDelete := PrepareDeleteByKeys(tt.args.table, tt.args.keys...)
|
||||||
|
err := getDelete(tt.db.db)
|
||||||
|
|
||||||
|
if !tt.res.wantErr && err != nil {
|
||||||
|
t.Errorf("got wrong err should be nil: %v ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.res.wantErr && !tt.res.errFunc(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if err := tt.db.mock.ExpectationsWereMet(); !tt.res.wantErr && err != nil {
|
||||||
|
t.Errorf("there were unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tt.db.close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPrepareDeleteByObject(t *testing.T) {
|
func TestPrepareDeleteByObject(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
table string
|
table string
|
||||||
|
121
migrations/cockroach/V1.5__auth.sql
Normal file
121
migrations/cockroach/V1.5__auth.sql
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE DATABASE auth;
|
||||||
|
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE USER auth;
|
||||||
|
|
||||||
|
GRANT SELECT, INSERT, UPDATE, DELETE ON DATABASE auth TO auth;
|
||||||
|
GRANT SELECT, INSERT, UPDATE ON DATABASE eventstore TO auth;
|
||||||
|
GRANT SELECT, INSERT, UPDATE ON TABLE eventstore.* TO auth;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE TABLE auth.locks (
|
||||||
|
locker_id TEXT,
|
||||||
|
locked_until TIMESTAMPTZ,
|
||||||
|
object_type TEXT,
|
||||||
|
|
||||||
|
PRIMARY KEY (object_type)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE auth.current_sequences (
|
||||||
|
view_name TEXT,
|
||||||
|
|
||||||
|
current_sequence BIGINT,
|
||||||
|
|
||||||
|
PRIMARY KEY (view_name)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE auth.failed_event (
|
||||||
|
view_name TEXT,
|
||||||
|
failed_sequence BIGINT,
|
||||||
|
failure_count SMALLINT,
|
||||||
|
err_msg TEXT,
|
||||||
|
|
||||||
|
PRIMARY KEY (view_name, failed_sequence)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE auth.auth_requests (
|
||||||
|
id TEXT,
|
||||||
|
request JSONB,
|
||||||
|
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE auth.users (
|
||||||
|
id TEXT,
|
||||||
|
|
||||||
|
creation_date TIMESTAMPTZ,
|
||||||
|
change_date TIMESTAMPTZ,
|
||||||
|
|
||||||
|
resource_owner TEXT,
|
||||||
|
user_state SMALLINT,
|
||||||
|
password_set BOOLEAN,
|
||||||
|
password_change_required BOOLEAN,
|
||||||
|
password_change TIMESTAMPTZ,
|
||||||
|
last_login TIMESTAMPTZ,
|
||||||
|
user_name TEXT,
|
||||||
|
first_name TEXT,
|
||||||
|
last_name TEXT,
|
||||||
|
nick_name TEXT,
|
||||||
|
display_name TEXT,
|
||||||
|
preferred_language TEXT,
|
||||||
|
gender SMALLINT,
|
||||||
|
email TEXT,
|
||||||
|
is_email_verified BOOLEAN,
|
||||||
|
phone TEXT,
|
||||||
|
is_phone_verified BOOLEAN,
|
||||||
|
country TEXT,
|
||||||
|
locality TEXT,
|
||||||
|
postal_code TEXT,
|
||||||
|
region TEXT,
|
||||||
|
street_address TEXT,
|
||||||
|
otp_state SMALLINT,
|
||||||
|
mfa_max_set_up SMALLINT,
|
||||||
|
mfa_init_skipped TIMESTAMPTZ,
|
||||||
|
sequence BIGINT,
|
||||||
|
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE auth.user_sessions (
|
||||||
|
creation_date TIMESTAMPTZ,
|
||||||
|
change_date TIMESTAMPTZ,
|
||||||
|
|
||||||
|
resource_owner TEXT,
|
||||||
|
state SMALLINT,
|
||||||
|
user_agent_id TEXT,
|
||||||
|
user_id TEXT,
|
||||||
|
user_name TEXT,
|
||||||
|
password_verification TIMESTAMPTZ,
|
||||||
|
mfa_software_verification TIMESTAMPTZ,
|
||||||
|
mfa_hardware_verification TIMESTAMPTZ,
|
||||||
|
sequence BIGINT,
|
||||||
|
|
||||||
|
PRIMARY KEY (user_agent_id, user_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE auth.tokens (
|
||||||
|
id TEXT,
|
||||||
|
|
||||||
|
creation_date TIMESTAMPTZ,
|
||||||
|
change_date TIMESTAMPTZ,
|
||||||
|
|
||||||
|
resource_owner TEXT,
|
||||||
|
application_id TEXT,
|
||||||
|
user_agent_id TEXT,
|
||||||
|
user_id TEXT,
|
||||||
|
expiration TIMESTAMPTZ,
|
||||||
|
sequence BIGINT,
|
||||||
|
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMIT;
|
@@ -3,8 +3,10 @@ package api
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
auth_util "github.com/caos/zitadel/internal/api/auth"
|
||||||
grpc_util "github.com/caos/zitadel/internal/api/grpc"
|
grpc_util "github.com/caos/zitadel/internal/api/grpc"
|
||||||
"github.com/caos/zitadel/internal/api/grpc/server"
|
"github.com/caos/zitadel/internal/api/grpc/server"
|
||||||
|
"github.com/caos/zitadel/internal/auth/repository"
|
||||||
"github.com/caos/zitadel/pkg/auth/api/grpc"
|
"github.com/caos/zitadel/pkg/auth/api/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -12,8 +14,8 @@ type Config struct {
|
|||||||
GRPC grpc_util.Config
|
GRPC grpc_util.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start(ctx context.Context, conf Config) {
|
func Start(ctx context.Context, conf Config, authZ auth_util.Config, repo repository.Repository) {
|
||||||
grpcServer := grpc.StartServer(conf.GRPC.ToServerConfig())
|
grpcServer := grpc.StartServer(conf.GRPC.ToServerConfig(), authZ, repo)
|
||||||
grpcGateway := grpc.StartGateway(conf.GRPC.ToGatewayConfig())
|
grpcGateway := grpc.StartGateway(conf.GRPC.ToGatewayConfig())
|
||||||
|
|
||||||
server.StartServer(ctx, grpcServer)
|
server.StartServer(ctx, grpcServer)
|
||||||
|
@@ -85,11 +85,6 @@ var AuthService_AuthMethods = utils_auth.MethodMapping{
|
|||||||
CheckParam: "",
|
CheckParam: "",
|
||||||
},
|
},
|
||||||
|
|
||||||
"/zitadel.auth.api.v1.AuthService/SetMyPassword": utils_auth.Option{
|
|
||||||
Permission: "authenticated",
|
|
||||||
CheckParam: "",
|
|
||||||
},
|
|
||||||
|
|
||||||
"/zitadel.auth.api.v1.AuthService/ChangeMyPassword": utils_auth.Option{
|
"/zitadel.auth.api.v1.AuthService/ChangeMyPassword": utils_auth.Option{
|
||||||
Permission: "authenticated",
|
Permission: "authenticated",
|
||||||
CheckParam: "",
|
CheckParam: "",
|
||||||
|
@@ -620,18 +620,20 @@ func (m *User) GetSequence() uint64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserProfile struct {
|
type UserProfile struct {
|
||||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
UserName string `protobuf:"bytes,2,opt,name=user_name,json=userName,proto3" json:"user_name,omitempty"`
|
UserName string `protobuf:"bytes,2,opt,name=user_name,json=userName,proto3" json:"user_name,omitempty"`
|
||||||
FirstName string `protobuf:"bytes,3,opt,name=first_name,json=firstName,proto3" json:"first_name,omitempty"`
|
FirstName string `protobuf:"bytes,3,opt,name=first_name,json=firstName,proto3" json:"first_name,omitempty"`
|
||||||
LastName string `protobuf:"bytes,4,opt,name=last_name,json=lastName,proto3" json:"last_name,omitempty"`
|
LastName string `protobuf:"bytes,4,opt,name=last_name,json=lastName,proto3" json:"last_name,omitempty"`
|
||||||
NickName string `protobuf:"bytes,5,opt,name=nick_name,json=nickName,proto3" json:"nick_name,omitempty"`
|
NickName string `protobuf:"bytes,5,opt,name=nick_name,json=nickName,proto3" json:"nick_name,omitempty"`
|
||||||
DisplayName string `protobuf:"bytes,6,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"`
|
DisplayName string `protobuf:"bytes,6,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"`
|
||||||
PreferredLanguage string `protobuf:"bytes,7,opt,name=preferred_language,json=preferredLanguage,proto3" json:"preferred_language,omitempty"`
|
PreferredLanguage string `protobuf:"bytes,7,opt,name=preferred_language,json=preferredLanguage,proto3" json:"preferred_language,omitempty"`
|
||||||
Gender Gender `protobuf:"varint,8,opt,name=gender,proto3,enum=zitadel.auth.api.v1.Gender" json:"gender,omitempty"`
|
Gender Gender `protobuf:"varint,8,opt,name=gender,proto3,enum=zitadel.auth.api.v1.Gender" json:"gender,omitempty"`
|
||||||
Sequence uint64 `protobuf:"varint,26,opt,name=sequence,proto3" json:"sequence,omitempty"`
|
Sequence uint64 `protobuf:"varint,9,opt,name=sequence,proto3" json:"sequence,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
CreationDate *timestamp.Timestamp `protobuf:"bytes,10,opt,name=creation_date,json=creationDate,proto3" json:"creation_date,omitempty"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
ChangeDate *timestamp.Timestamp `protobuf:"bytes,11,opt,name=change_date,json=changeDate,proto3" json:"change_date,omitempty"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *UserProfile) Reset() { *m = UserProfile{} }
|
func (m *UserProfile) Reset() { *m = UserProfile{} }
|
||||||
@@ -722,6 +724,20 @@ func (m *UserProfile) GetSequence() uint64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *UserProfile) GetCreationDate() *timestamp.Timestamp {
|
||||||
|
if m != nil {
|
||||||
|
return m.CreationDate
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserProfile) GetChangeDate() *timestamp.Timestamp {
|
||||||
|
if m != nil {
|
||||||
|
return m.ChangeDate
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateUserProfileRequest struct {
|
type UpdateUserProfileRequest struct {
|
||||||
FirstName string `protobuf:"bytes,1,opt,name=first_name,json=firstName,proto3" json:"first_name,omitempty"`
|
FirstName string `protobuf:"bytes,1,opt,name=first_name,json=firstName,proto3" json:"first_name,omitempty"`
|
||||||
LastName string `protobuf:"bytes,2,opt,name=last_name,json=lastName,proto3" json:"last_name,omitempty"`
|
LastName string `protobuf:"bytes,2,opt,name=last_name,json=lastName,proto3" json:"last_name,omitempty"`
|
||||||
@@ -802,13 +818,15 @@ func (m *UpdateUserProfileRequest) GetGender() Gender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserEmail struct {
|
type UserEmail struct {
|
||||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"`
|
Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"`
|
||||||
IsEmailVerified bool `protobuf:"varint,3,opt,name=isEmailVerified,proto3" json:"isEmailVerified,omitempty"`
|
IsEmailVerified bool `protobuf:"varint,3,opt,name=isEmailVerified,proto3" json:"isEmailVerified,omitempty"`
|
||||||
Sequence uint64 `protobuf:"varint,4,opt,name=sequence,proto3" json:"sequence,omitempty"`
|
Sequence uint64 `protobuf:"varint,4,opt,name=sequence,proto3" json:"sequence,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
CreationDate *timestamp.Timestamp `protobuf:"bytes,5,opt,name=creation_date,json=creationDate,proto3" json:"creation_date,omitempty"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
ChangeDate *timestamp.Timestamp `protobuf:"bytes,6,opt,name=change_date,json=changeDate,proto3" json:"change_date,omitempty"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *UserEmail) Reset() { *m = UserEmail{} }
|
func (m *UserEmail) Reset() { *m = UserEmail{} }
|
||||||
@@ -864,6 +882,20 @@ func (m *UserEmail) GetSequence() uint64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *UserEmail) GetCreationDate() *timestamp.Timestamp {
|
||||||
|
if m != nil {
|
||||||
|
return m.CreationDate
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserEmail) GetChangeDate() *timestamp.Timestamp {
|
||||||
|
if m != nil {
|
||||||
|
return m.ChangeDate
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type VerifyMyUserEmailRequest struct {
|
type VerifyMyUserEmailRequest struct {
|
||||||
Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"`
|
Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
@@ -990,13 +1022,15 @@ func (m *UpdateUserEmailRequest) GetEmail() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserPhone struct {
|
type UserPhone struct {
|
||||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
Phone string `protobuf:"bytes,2,opt,name=phone,proto3" json:"phone,omitempty"`
|
Phone string `protobuf:"bytes,2,opt,name=phone,proto3" json:"phone,omitempty"`
|
||||||
IsPhoneVerified bool `protobuf:"varint,3,opt,name=is_phone_verified,json=isPhoneVerified,proto3" json:"is_phone_verified,omitempty"`
|
IsPhoneVerified bool `protobuf:"varint,3,opt,name=is_phone_verified,json=isPhoneVerified,proto3" json:"is_phone_verified,omitempty"`
|
||||||
Sequence uint64 `protobuf:"varint,4,opt,name=sequence,proto3" json:"sequence,omitempty"`
|
Sequence uint64 `protobuf:"varint,4,opt,name=sequence,proto3" json:"sequence,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
CreationDate *timestamp.Timestamp `protobuf:"bytes,5,opt,name=creation_date,json=creationDate,proto3" json:"creation_date,omitempty"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
ChangeDate *timestamp.Timestamp `protobuf:"bytes,6,opt,name=change_date,json=changeDate,proto3" json:"change_date,omitempty"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *UserPhone) Reset() { *m = UserPhone{} }
|
func (m *UserPhone) Reset() { *m = UserPhone{} }
|
||||||
@@ -1052,6 +1086,20 @@ func (m *UserPhone) GetSequence() uint64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *UserPhone) GetCreationDate() *timestamp.Timestamp {
|
||||||
|
if m != nil {
|
||||||
|
return m.CreationDate
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserPhone) GetChangeDate() *timestamp.Timestamp {
|
||||||
|
if m != nil {
|
||||||
|
return m.ChangeDate
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateUserPhoneRequest struct {
|
type UpdateUserPhoneRequest struct {
|
||||||
Phone string `protobuf:"bytes,1,opt,name=phone,proto3" json:"phone,omitempty"`
|
Phone string `protobuf:"bytes,1,opt,name=phone,proto3" json:"phone,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
@@ -1131,16 +1179,18 @@ func (m *VerifyUserPhoneRequest) GetCode() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserAddress struct {
|
type UserAddress struct {
|
||||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
Country string `protobuf:"bytes,2,opt,name=country,proto3" json:"country,omitempty"`
|
Country string `protobuf:"bytes,2,opt,name=country,proto3" json:"country,omitempty"`
|
||||||
Locality string `protobuf:"bytes,3,opt,name=locality,proto3" json:"locality,omitempty"`
|
Locality string `protobuf:"bytes,3,opt,name=locality,proto3" json:"locality,omitempty"`
|
||||||
PostalCode string `protobuf:"bytes,4,opt,name=postal_code,json=postalCode,proto3" json:"postal_code,omitempty"`
|
PostalCode string `protobuf:"bytes,4,opt,name=postal_code,json=postalCode,proto3" json:"postal_code,omitempty"`
|
||||||
Region string `protobuf:"bytes,5,opt,name=region,proto3" json:"region,omitempty"`
|
Region string `protobuf:"bytes,5,opt,name=region,proto3" json:"region,omitempty"`
|
||||||
StreetAddress string `protobuf:"bytes,6,opt,name=street_address,json=streetAddress,proto3" json:"street_address,omitempty"`
|
StreetAddress string `protobuf:"bytes,6,opt,name=street_address,json=streetAddress,proto3" json:"street_address,omitempty"`
|
||||||
Sequence uint64 `protobuf:"varint,7,opt,name=sequence,proto3" json:"sequence,omitempty"`
|
Sequence uint64 `protobuf:"varint,7,opt,name=sequence,proto3" json:"sequence,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
CreationDate *timestamp.Timestamp `protobuf:"bytes,8,opt,name=creation_date,json=creationDate,proto3" json:"creation_date,omitempty"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
ChangeDate *timestamp.Timestamp `protobuf:"bytes,9,opt,name=change_date,json=changeDate,proto3" json:"change_date,omitempty"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *UserAddress) Reset() { *m = UserAddress{} }
|
func (m *UserAddress) Reset() { *m = UserAddress{} }
|
||||||
@@ -1217,6 +1267,20 @@ func (m *UserAddress) GetSequence() uint64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *UserAddress) GetCreationDate() *timestamp.Timestamp {
|
||||||
|
if m != nil {
|
||||||
|
return m.CreationDate
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserAddress) GetChangeDate() *timestamp.Timestamp {
|
||||||
|
if m != nil {
|
||||||
|
return m.ChangeDate
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateUserAddressRequest struct {
|
type UpdateUserAddressRequest struct {
|
||||||
Country string `protobuf:"bytes,1,opt,name=country,proto3" json:"country,omitempty"`
|
Country string `protobuf:"bytes,1,opt,name=country,proto3" json:"country,omitempty"`
|
||||||
Locality string `protobuf:"bytes,2,opt,name=locality,proto3" json:"locality,omitempty"`
|
Locality string `protobuf:"bytes,2,opt,name=locality,proto3" json:"locality,omitempty"`
|
||||||
@@ -1996,189 +2060,190 @@ func init() {
|
|||||||
func init() { proto.RegisterFile("auth.proto", fileDescriptor_8bbd6f3875b0e874) }
|
func init() { proto.RegisterFile("auth.proto", fileDescriptor_8bbd6f3875b0e874) }
|
||||||
|
|
||||||
var fileDescriptor_8bbd6f3875b0e874 = []byte{
|
var fileDescriptor_8bbd6f3875b0e874 = []byte{
|
||||||
// 2761 bytes of a gzipped FileDescriptorProto
|
// 2779 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x5a, 0x4d, 0x6f, 0xdb, 0xc8,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0x4b, 0x6f, 0x1b, 0xd7,
|
||||||
0x19, 0x0e, 0x25, 0x59, 0x96, 0x5f, 0xf9, 0x83, 0x1e, 0x27, 0x36, 0x2d, 0x3b, 0x89, 0xc2, 0x6c,
|
0x15, 0xf6, 0x90, 0x14, 0x45, 0x1e, 0x4a, 0xd4, 0xe8, 0xca, 0x96, 0x46, 0x0f, 0xdb, 0xf4, 0x38,
|
||||||
0xb0, 0x8e, 0x36, 0xb1, 0x36, 0xde, 0x2d, 0xba, 0xc9, 0x1e, 0x0a, 0xc5, 0xa2, 0x13, 0x35, 0xd6,
|
0x46, 0x64, 0xc6, 0x16, 0x13, 0x25, 0x45, 0x63, 0x67, 0x51, 0xd0, 0xe2, 0xc8, 0x66, 0x2d, 0x3e,
|
||||||
0xc7, 0x52, 0x4a, 0xda, 0x2c, 0x50, 0xa8, 0x8c, 0x38, 0x92, 0xb9, 0x2b, 0x89, 0x0c, 0x49, 0x39,
|
0x32, 0xa4, 0xdd, 0x3a, 0x40, 0xc1, 0x8e, 0x39, 0x57, 0xd4, 0x24, 0x24, 0x67, 0x3c, 0x33, 0x94,
|
||||||
0xd0, 0x5e, 0x5a, 0xec, 0x69, 0xdb, 0xcb, 0xa2, 0xdb, 0x43, 0x8f, 0x3d, 0xf4, 0x0f, 0xb4, 0x05,
|
0xc1, 0x6c, 0x0a, 0x78, 0x95, 0x76, 0x13, 0x34, 0x5d, 0x74, 0xd9, 0x45, 0x77, 0x5d, 0xb5, 0x45,
|
||||||
0xda, 0x02, 0xfd, 0x05, 0xbd, 0xf5, 0xd0, 0xfe, 0x82, 0xa2, 0xbf, 0x22, 0x87, 0xa2, 0x98, 0x0f,
|
0x1f, 0x7f, 0xa1, 0x9b, 0xae, 0xda, 0x5f, 0x50, 0xf4, 0x2f, 0x14, 0x05, 0xb2, 0x28, 0x8a, 0xfb,
|
||||||
0x4a, 0xfc, 0x10, 0x25, 0x05, 0x45, 0x4f, 0xe1, 0xbc, 0x5f, 0xf3, 0xbc, 0xcf, 0xbc, 0xf3, 0x6a,
|
0x18, 0x72, 0x1e, 0x1c, 0x91, 0x82, 0x51, 0xa0, 0x2b, 0xf3, 0x9e, 0xe7, 0x77, 0xcf, 0x39, 0xf7,
|
||||||
0x66, 0x1c, 0x00, 0x6d, 0xe4, 0x5e, 0x1c, 0x5b, 0xb6, 0xe9, 0x9a, 0x68, 0xe7, 0x2b, 0xc3, 0xd5,
|
0xcc, 0xbd, 0xc7, 0x02, 0xd0, 0x46, 0xee, 0xd9, 0x81, 0x65, 0x9b, 0xae, 0x89, 0x36, 0xbe, 0x34,
|
||||||
0x74, 0xdc, 0x3f, 0xa6, 0x32, 0xcd, 0x32, 0x8e, 0x2f, 0x1f, 0xe4, 0x0e, 0x7b, 0xa6, 0xd9, 0xeb,
|
0x5c, 0x4d, 0xc7, 0xfd, 0x03, 0x4a, 0xd3, 0x2c, 0xe3, 0xe0, 0xfc, 0x83, 0x9d, 0xbd, 0x9e, 0x69,
|
||||||
0xe3, 0xa2, 0x66, 0x19, 0x45, 0x6d, 0x38, 0x34, 0x5d, 0xcd, 0x35, 0xcc, 0xa1, 0xc3, 0x5c, 0x72,
|
0xf6, 0xfa, 0xb8, 0xa4, 0x59, 0x46, 0x49, 0x1b, 0x0e, 0x4d, 0x57, 0x73, 0x0d, 0x73, 0xe8, 0x30,
|
||||||
0x07, 0x5c, 0x4b, 0x47, 0xaf, 0x46, 0xdd, 0x22, 0x1e, 0x58, 0xee, 0x98, 0x2b, 0x0f, 0xc3, 0x4a,
|
0x95, 0x9d, 0x5d, 0xce, 0xa5, 0xab, 0x97, 0xa3, 0xd3, 0x12, 0x1e, 0x58, 0xee, 0x98, 0x33, 0xf7,
|
||||||
0xc7, 0xb5, 0x47, 0x1d, 0x97, 0x6b, 0x6f, 0x86, 0xb5, 0xae, 0x31, 0xc0, 0x8e, 0xab, 0x0d, 0x2c,
|
0xc2, 0x4c, 0xc7, 0xb5, 0x47, 0x5d, 0x97, 0x73, 0x6f, 0x86, 0xb9, 0xae, 0x31, 0xc0, 0x8e, 0xab,
|
||||||
0x6e, 0xb0, 0x77, 0xa9, 0xf5, 0x0d, 0x5d, 0x73, 0x71, 0xd1, 0xfb, 0xe0, 0x8a, 0x7b, 0xf4, 0x9f,
|
0x0d, 0x2c, 0x2e, 0xb0, 0x75, 0xae, 0xf5, 0x0d, 0x5d, 0x73, 0x71, 0xc9, 0xfb, 0xc1, 0x19, 0xf7,
|
||||||
0xce, 0xfd, 0x1e, 0x1e, 0xde, 0x77, 0xde, 0x68, 0xbd, 0x1e, 0xb6, 0x8b, 0xa6, 0x45, 0x61, 0xcd,
|
0xe8, 0x3f, 0xdd, 0xfb, 0x3d, 0x3c, 0xbc, 0xef, 0xbc, 0xd6, 0x7a, 0x3d, 0x6c, 0x97, 0x4c, 0x8b,
|
||||||
0x80, 0x28, 0x91, 0x6c, 0x98, 0xda, 0xb3, 0x62, 0x1a, 0xf9, 0x27, 0x20, 0x3e, 0x77, 0xb0, 0xdd,
|
0xc2, 0x9a, 0x01, 0x51, 0x22, 0xbb, 0x61, 0x6c, 0x4f, 0x8a, 0x71, 0xe4, 0x1f, 0x81, 0xf8, 0xcc,
|
||||||
0xc4, 0x8e, 0x63, 0x98, 0xc3, 0x17, 0x06, 0x7e, 0xe3, 0xa0, 0x0a, 0x6c, 0x8c, 0x1c, 0x6c, 0xb7,
|
0xc1, 0x76, 0x0b, 0x3b, 0x8e, 0x61, 0x0e, 0x9f, 0x1b, 0xf8, 0xb5, 0x83, 0xaa, 0xb0, 0x3a, 0x72,
|
||||||
0x1d, 0x26, 0x74, 0x24, 0x21, 0x9f, 0x3c, 0xca, 0x9e, 0xbc, 0x77, 0x3c, 0x83, 0x9b, 0xe3, 0x90,
|
0xb0, 0xdd, 0x71, 0x18, 0xd1, 0x91, 0x84, 0x42, 0x72, 0x3f, 0x77, 0xf8, 0xce, 0xc1, 0x8c, 0xd8,
|
||||||
0xb7, 0xba, 0x3e, 0x9a, 0x0a, 0x1c, 0xf9, 0x9f, 0x02, 0x6c, 0x85, 0x2c, 0xd0, 0x26, 0x24, 0x0c,
|
0x1c, 0x84, 0xb4, 0xd5, 0x95, 0xd1, 0x94, 0xe0, 0xc8, 0x7f, 0x17, 0x60, 0x2d, 0x24, 0x81, 0xf2,
|
||||||
0x5d, 0x12, 0xf2, 0xc2, 0xd1, 0x9a, 0x9a, 0x30, 0x74, 0xb4, 0x0f, 0x19, 0xad, 0x87, 0x87, 0x6e,
|
0x90, 0x30, 0x74, 0x49, 0x28, 0x08, 0xfb, 0x59, 0x35, 0x61, 0xe8, 0x68, 0x1b, 0x32, 0x5a, 0x0f,
|
||||||
0xdb, 0xd0, 0xa5, 0x04, 0x95, 0xae, 0xd2, 0x71, 0x45, 0x47, 0x65, 0xb6, 0x36, 0x6d, 0xc7, 0xd5,
|
0x0f, 0xdd, 0x8e, 0xa1, 0x4b, 0x09, 0x4a, 0x5d, 0xa6, 0xeb, 0xaa, 0x8e, 0x2a, 0x2c, 0x37, 0x1d,
|
||||||
0x5c, 0x2c, 0x25, 0xf3, 0xc2, 0xd1, 0xe6, 0xc9, 0x9d, 0x45, 0x30, 0x9a, 0xc4, 0x58, 0x5d, 0x23,
|
0xc7, 0xd5, 0x5c, 0x2c, 0x25, 0x0b, 0xc2, 0x7e, 0xfe, 0xf0, 0xce, 0x3c, 0x18, 0x2d, 0x22, 0xac,
|
||||||
0x5a, 0xfa, 0x89, 0xf6, 0x60, 0x95, 0xe6, 0x63, 0xe8, 0x52, 0x8a, 0xc6, 0x4f, 0x93, 0x61, 0x45,
|
0x66, 0x09, 0x97, 0xfe, 0x44, 0x5b, 0xb0, 0x4c, 0xf7, 0x63, 0xe8, 0x52, 0x8a, 0xda, 0x4f, 0x93,
|
||||||
0x47, 0x07, 0xb0, 0x46, 0x15, 0x43, 0x6d, 0x80, 0xa5, 0x15, 0xaa, 0xca, 0x10, 0x41, 0x4d, 0x1b,
|
0x65, 0x55, 0x47, 0xbb, 0x90, 0xa5, 0x8c, 0xa1, 0x36, 0xc0, 0xd2, 0x12, 0x65, 0x65, 0x08, 0xa1,
|
||||||
0x60, 0x94, 0x83, 0x8c, 0x83, 0x5f, 0x8f, 0xf0, 0xb0, 0x83, 0xa5, 0x74, 0x5e, 0x38, 0x4a, 0xa9,
|
0xae, 0x0d, 0x30, 0xda, 0x81, 0x8c, 0x83, 0x5f, 0x8d, 0xf0, 0xb0, 0x8b, 0xa5, 0x74, 0x41, 0xd8,
|
||||||
0x93, 0xb1, 0xfc, 0x9f, 0x55, 0x48, 0x91, 0x19, 0x23, 0xb9, 0x7c, 0x0c, 0x2b, 0x0c, 0x6b, 0x82,
|
0x4f, 0xa9, 0x93, 0xb5, 0xfc, 0x9f, 0x65, 0x48, 0x11, 0x8f, 0x91, 0xbd, 0x7c, 0x04, 0x4b, 0x0c,
|
||||||
0x62, 0xbd, 0x11, 0x8f, 0x95, 0x82, 0x64, 0xc6, 0xe8, 0x07, 0xb0, 0xd1, 0xb1, 0x31, 0x5d, 0xb1,
|
0x6b, 0x82, 0x62, 0xbd, 0x11, 0x8f, 0x95, 0x82, 0x64, 0xc2, 0xe8, 0x7b, 0xb0, 0xda, 0xb5, 0x31,
|
||||||
0xb6, 0xee, 0x65, 0x9a, 0x3d, 0xc9, 0x1d, 0xb3, 0xf2, 0x38, 0xf6, 0xca, 0xe3, 0xb8, 0xe5, 0x95,
|
0xcd, 0x58, 0x47, 0xf7, 0x76, 0x9a, 0x3b, 0xdc, 0x39, 0x60, 0xe5, 0x71, 0xe0, 0x95, 0xc7, 0x41,
|
||||||
0x87, 0xba, 0xee, 0x39, 0x94, 0x49, 0x80, 0x53, 0xd8, 0xd2, 0x3a, 0xae, 0x71, 0xe9, 0x0b, 0x91,
|
0xdb, 0x2b, 0x0f, 0x75, 0xc5, 0x53, 0xa8, 0x10, 0x03, 0x47, 0xb0, 0xa6, 0x75, 0x5d, 0xe3, 0xdc,
|
||||||
0x5a, 0x18, 0x62, 0x73, 0xea, 0x42, 0x83, 0x7c, 0x0a, 0xd9, 0xce, 0x85, 0x36, 0xec, 0x61, 0x16,
|
0x67, 0x22, 0x35, 0xd7, 0x44, 0x7e, 0xaa, 0x42, 0x8d, 0x7c, 0x02, 0xb9, 0xee, 0x99, 0x36, 0xec,
|
||||||
0x60, 0x65, 0x61, 0x00, 0x60, 0xe6, 0xd4, 0xf9, 0x21, 0x40, 0x5f, 0x73, 0xdc, 0x76, 0xdf, 0xec,
|
0x61, 0x66, 0x60, 0x69, 0xae, 0x01, 0x60, 0xe2, 0x54, 0xf9, 0x01, 0x40, 0x5f, 0x73, 0xdc, 0x4e,
|
||||||
0x19, 0x43, 0xca, 0xd7, 0x7c, 0xdf, 0x35, 0x62, 0x7d, 0x4e, 0x8c, 0x91, 0x02, 0xa2, 0xa5, 0x39,
|
0xdf, 0xec, 0x19, 0x43, 0x1a, 0xaf, 0x8b, 0x75, 0xb3, 0x44, 0xfa, 0x84, 0x08, 0x23, 0x05, 0x44,
|
||||||
0xce, 0x1b, 0xd3, 0xd6, 0xdb, 0x2c, 0xa2, 0x2e, 0xad, 0x2e, 0x0c, 0xb0, 0xe5, 0xf9, 0x9c, 0x32,
|
0x4b, 0x73, 0x9c, 0xd7, 0xa6, 0xad, 0x77, 0x98, 0x45, 0x5d, 0x5a, 0x9e, 0x6b, 0x60, 0xcd, 0xd3,
|
||||||
0x97, 0xe0, 0x62, 0x66, 0x42, 0x8b, 0x79, 0x1d, 0xa0, 0x6b, 0xd8, 0x8e, 0xcb, 0xb4, 0x6b, 0x54,
|
0x39, 0x62, 0x2a, 0xc1, 0x64, 0x66, 0x42, 0xc9, 0xbc, 0x0e, 0x70, 0x6a, 0xd8, 0x8e, 0xcb, 0xb8,
|
||||||
0xbb, 0x46, 0x25, 0x54, 0x7d, 0x00, 0x14, 0x0f, 0xd3, 0x02, 0xf3, 0x25, 0x02, 0x4f, 0x39, 0x34,
|
0x59, 0xca, 0xcd, 0x52, 0x0a, 0x65, 0xef, 0x02, 0xc5, 0xc3, 0xb8, 0xc0, 0x74, 0x09, 0xc1, 0x63,
|
||||||
0x3a, 0x5f, 0x32, 0x65, 0x96, 0x29, 0x89, 0x80, 0x2a, 0x6f, 0xc1, 0xba, 0x6e, 0x38, 0x56, 0x5f,
|
0x0e, 0x8d, 0xee, 0x17, 0x8c, 0x99, 0x63, 0x4c, 0x42, 0xa0, 0xcc, 0x5b, 0xb0, 0xa2, 0x1b, 0x8e,
|
||||||
0x1b, 0x33, 0xfd, 0x3a, 0xd5, 0x67, 0xb9, 0x8c, 0x9a, 0xdc, 0x07, 0x64, 0xd9, 0xb8, 0x8b, 0x6d,
|
0xd5, 0xd7, 0xc6, 0x8c, 0xbf, 0x42, 0xf9, 0x39, 0x4e, 0xa3, 0x22, 0xf7, 0x01, 0x59, 0x36, 0x3e,
|
||||||
0x1b, 0xeb, 0xed, 0xbe, 0x36, 0xec, 0x8d, 0xb4, 0x1e, 0x96, 0x36, 0xa8, 0xe1, 0xf6, 0x44, 0x73,
|
0xc5, 0xb6, 0x8d, 0xf5, 0x4e, 0x5f, 0x1b, 0xf6, 0x46, 0x5a, 0x0f, 0x4b, 0xab, 0x54, 0x70, 0x7d,
|
||||||
0xce, 0x15, 0xe8, 0x23, 0x48, 0xf7, 0xf0, 0x50, 0xc7, 0xb6, 0xb4, 0x49, 0x6b, 0xe8, 0x60, 0x66,
|
0xc2, 0x39, 0xe1, 0x0c, 0xf4, 0x21, 0xa4, 0x7b, 0x78, 0xa8, 0x63, 0x5b, 0xca, 0xd3, 0x1a, 0xda,
|
||||||
0x0d, 0x3d, 0xa1, 0x26, 0x2a, 0x37, 0x45, 0x57, 0x61, 0x05, 0x0f, 0x34, 0xa3, 0x2f, 0x6d, 0xd1,
|
0x9d, 0x59, 0x43, 0x8f, 0xa9, 0x88, 0xca, 0x45, 0xd1, 0x55, 0x58, 0xc2, 0x03, 0xcd, 0xe8, 0x4b,
|
||||||
0xb0, 0x6c, 0x80, 0x0a, 0xb0, 0x6d, 0x38, 0x6d, 0xfa, 0xdd, 0xbe, 0xc4, 0xb6, 0xd1, 0x35, 0xb0,
|
0x6b, 0xd4, 0x2c, 0x5b, 0xa0, 0x22, 0xac, 0x1b, 0x4e, 0x87, 0xfe, 0xee, 0x9c, 0x63, 0xdb, 0x38,
|
||||||
0x2e, 0x89, 0x79, 0xe1, 0x28, 0xa3, 0x6e, 0x19, 0x8e, 0x42, 0xe4, 0x2f, 0xb8, 0x98, 0x44, 0xb0,
|
0x35, 0xb0, 0x2e, 0x89, 0x05, 0x61, 0x3f, 0xa3, 0xae, 0x19, 0x8e, 0x42, 0xe8, 0xcf, 0x39, 0x99,
|
||||||
0x2e, 0xcc, 0x21, 0x96, 0xb6, 0x59, 0x04, 0x3a, 0xe0, 0x11, 0xe8, 0xf7, 0x34, 0x02, 0xf2, 0x22,
|
0x58, 0xb0, 0xce, 0xcc, 0x21, 0x96, 0xd6, 0x99, 0x05, 0xba, 0xe0, 0x16, 0xe8, 0xef, 0xa9, 0x05,
|
||||||
0x34, 0x88, 0x7c, 0x12, 0x41, 0x82, 0xd5, 0x8e, 0x39, 0x1a, 0xba, 0xf6, 0x58, 0xda, 0x61, 0xdb,
|
0xe4, 0x59, 0x68, 0x12, 0xfa, 0xc4, 0x82, 0x04, 0xcb, 0x5d, 0x73, 0x34, 0x74, 0xed, 0xb1, 0xb4,
|
||||||
0x98, 0x0f, 0xc9, 0x56, 0xea, 0x9b, 0x1d, 0xad, 0x6f, 0xb8, 0x63, 0xe9, 0x2a, 0x67, 0x97, 0x8f,
|
0xc1, 0x8e, 0x31, 0x5f, 0x92, 0xa3, 0xd4, 0x37, 0xbb, 0x5a, 0xdf, 0x70, 0xc7, 0xd2, 0x55, 0x1e,
|
||||||
0xd1, 0x4d, 0xc8, 0x5a, 0xa6, 0xe3, 0x6a, 0xfd, 0x76, 0xc7, 0xd4, 0xb1, 0x74, 0x8d, 0xaa, 0x81,
|
0x5d, 0xbe, 0x46, 0x37, 0x21, 0x67, 0x99, 0x8e, 0xab, 0xf5, 0x3b, 0x5d, 0x53, 0xc7, 0xd2, 0x35,
|
||||||
0x89, 0x4e, 0x4d, 0x1d, 0xa3, 0x5d, 0x48, 0xdb, 0xb8, 0x67, 0x98, 0x43, 0x69, 0x97, 0x6d, 0x5e,
|
0xca, 0x06, 0x46, 0x3a, 0x32, 0x75, 0x8c, 0x36, 0x21, 0x6d, 0xe3, 0x9e, 0x61, 0x0e, 0xa5, 0x4d,
|
||||||
0x36, 0x42, 0x77, 0x60, 0xd3, 0x71, 0x6d, 0x8c, 0xdd, 0xb6, 0xa6, 0xeb, 0x36, 0x76, 0x1c, 0x69,
|
0x76, 0x78, 0xd9, 0x0a, 0xdd, 0x81, 0xbc, 0xe3, 0xda, 0x18, 0xbb, 0x1d, 0x4d, 0xd7, 0x6d, 0xec,
|
||||||
0x8f, 0xea, 0x37, 0x98, 0xb4, 0xc4, 0x84, 0xe8, 0x13, 0x90, 0x42, 0xd5, 0xd5, 0xb6, 0xf1, 0xeb,
|
0x38, 0xd2, 0x16, 0xe5, 0xaf, 0x32, 0x6a, 0x99, 0x11, 0xd1, 0xc7, 0x20, 0x85, 0xaa, 0xab, 0x63,
|
||||||
0x91, 0x61, 0x63, 0x5d, 0x92, 0x68, 0x22, 0xbb, 0xc1, 0x4a, 0x52, 0xb9, 0x36, 0xd0, 0x00, 0xf6,
|
0xe3, 0x57, 0x23, 0xc3, 0xc6, 0xba, 0x24, 0xd1, 0x8d, 0x6c, 0x06, 0x2b, 0x49, 0xe5, 0xdc, 0x40,
|
||||||
0x43, 0x0d, 0xe0, 0xcf, 0x09, 0xc8, 0x92, 0x6d, 0xdc, 0xb0, 0xcd, 0xae, 0xd1, 0xc7, 0x91, 0x3e,
|
0x03, 0xd8, 0x0e, 0x35, 0x80, 0x3f, 0x24, 0x21, 0x47, 0x8e, 0x71, 0xd3, 0x36, 0x4f, 0x8d, 0x3e,
|
||||||
0x10, 0x28, 0xc6, 0xc4, 0xdc, 0x62, 0x4c, 0xce, 0x2d, 0xc6, 0xd4, 0xbc, 0x62, 0x5c, 0x59, 0x50,
|
0x8e, 0xf4, 0x81, 0x40, 0x31, 0x26, 0x2e, 0x2c, 0xc6, 0xe4, 0x85, 0xc5, 0x98, 0xba, 0xa8, 0x18,
|
||||||
0x8c, 0xe9, 0x65, 0x8b, 0x71, 0x75, 0x71, 0x31, 0x66, 0x96, 0x2f, 0x46, 0x3f, 0x71, 0xb9, 0x28,
|
0x97, 0xe6, 0x14, 0x63, 0x7a, 0xd1, 0x62, 0x5c, 0x9e, 0x5f, 0x8c, 0x99, 0xc5, 0x8b, 0xd1, 0x1f,
|
||||||
0x71, 0xd2, 0x73, 0x8b, 0x34, 0x18, 0x1f, 0x7d, 0x84, 0x72, 0xec, 0xb8, 0xe8, 0x6e, 0x80, 0x18,
|
0xb8, 0x6c, 0x30, 0x70, 0xd1, 0x56, 0x07, 0x97, 0x6c, 0x75, 0xa1, 0x2e, 0x95, 0xbb, 0x4c, 0x97,
|
||||||
0xca, 0xe6, 0x63, 0x78, 0xfb, 0x78, 0xd5, 0x5e, 0x11, 0x05, 0xe9, 0x6f, 0x82, 0x9f, 0xa4, 0xf7,
|
0x92, 0xff, 0x94, 0x00, 0xe9, 0x99, 0x45, 0x14, 0x7d, 0xc9, 0x23, 0x09, 0xc7, 0x8e, 0x8b, 0xee,
|
||||||
0xfd, 0x24, 0x25, 0x22, 0x96, 0x53, 0xc2, 0xde, 0xf7, 0x13, 0x96, 0x8c, 0x1a, 0x4e, 0xc8, 0xbb,
|
0x06, 0xd2, 0x42, 0x73, 0xf9, 0x08, 0xbe, 0x7d, 0xb4, 0x6c, 0x2f, 0x89, 0x82, 0xf4, 0x17, 0xc1,
|
||||||
0x1f, 0x22, 0x2f, 0x15, 0xb1, 0x0d, 0x10, 0xf9, 0x70, 0x26, 0x91, 0x2b, 0x11, 0xa7, 0xb9, 0xa4,
|
0x9f, 0xa2, 0x77, 0xfd, 0x29, 0x4a, 0x44, 0x24, 0xa7, 0xe9, 0x7a, 0xd7, 0x9f, 0xae, 0x64, 0x54,
|
||||||
0xa6, 0x97, 0x26, 0x55, 0x7e, 0x03, 0x6b, 0x84, 0x31, 0xba, 0x69, 0x23, 0xe5, 0x36, 0xd9, 0xfe,
|
0x70, 0x92, 0xba, 0xfb, 0xa1, 0xd4, 0xa5, 0x22, 0xb2, 0x81, 0x34, 0x3e, 0x98, 0x99, 0xc6, 0xa5,
|
||||||
0x09, 0xff, 0xf6, 0x3f, 0x82, 0xf0, 0x2e, 0xa7, 0x04, 0xcc, 0xd8, 0xfc, 0xfe, 0x15, 0x4b, 0x85,
|
0x88, 0xd2, 0x85, 0x29, 0x4d, 0x2f, 0x9c, 0x52, 0xf9, 0x5f, 0x02, 0x64, 0x49, 0xc8, 0x68, 0xcf,
|
||||||
0x56, 0xec, 0x11, 0x48, 0xd4, 0x6e, 0x5c, 0x1d, 0x4f, 0x00, 0x78, 0x0b, 0x76, 0x03, 0x52, 0x74,
|
0x88, 0x54, 0xfb, 0xa4, 0xfb, 0x24, 0xfc, 0xdd, 0x67, 0x1f, 0xc2, 0x4d, 0x86, 0x46, 0x60, 0x46,
|
||||||
0xd7, 0x46, 0x97, 0x8a, 0xca, 0xe5, 0xa7, 0xb0, 0xcb, 0x7c, 0x23, 0x9e, 0xe1, 0x0c, 0xbc, 0x48,
|
0xef, 0xf1, 0x17, 0x4c, 0x6a, 0x5e, 0xc1, 0x2c, 0xbd, 0x5d, 0xc1, 0xa4, 0x2f, 0x55, 0x30, 0x0f,
|
||||||
0x89, 0x98, 0x48, 0x8f, 0x60, 0x77, 0x5a, 0x36, 0x81, 0x48, 0x79, 0x2f, 0xf7, 0x28, 0x08, 0xa6,
|
0x41, 0xa2, 0x28, 0xc7, 0xb5, 0xf1, 0x64, 0xfb, 0x5e, 0xbd, 0xdc, 0x80, 0x14, 0x6d, 0x59, 0xd1,
|
||||||
0x90, 0xc7, 0x8c, 0x3a, 0xda, 0xad, 0x66, 0x51, 0xc7, 0xfa, 0x5e, 0x62, 0x61, 0xdf, 0x4b, 0xce,
|
0x4a, 0xa1, 0x74, 0xf9, 0x09, 0x6c, 0x32, 0xdd, 0x88, 0x66, 0x38, 0x7e, 0x9e, 0xa5, 0x44, 0x8c,
|
||||||
0xee, 0x7b, 0xf3, 0xc8, 0x7b, 0xe8, 0x87, 0x4d, 0xdd, 0x3c, 0xd8, 0x37, 0xbd, 0x79, 0x19, 0xec,
|
0xa5, 0x87, 0xb0, 0x39, 0xad, 0xda, 0x80, 0xa5, 0x82, 0x17, 0xf9, 0x28, 0x08, 0xc6, 0x90, 0xff,
|
||||||
0xb5, 0xb7, 0x8f, 0xd3, 0x76, 0x4a, 0x14, 0xa4, 0xab, 0x1c, 0x82, 0xfc, 0x89, 0x9f, 0xbb, 0x80,
|
0xcd, 0x33, 0x47, 0x7b, 0xf5, 0xac, 0xcc, 0xb1, 0xae, 0x9f, 0x98, 0xdb, 0xf5, 0x93, 0xb3, 0xbb,
|
||||||
0xeb, 0x22, 0xd6, 0xff, 0x2e, 0xb0, 0xe6, 0xe4, 0xb5, 0xc0, 0x70, 0xca, 0xbe, 0x46, 0x9d, 0x88,
|
0xfe, 0xff, 0x6f, 0xee, 0x1e, 0xf8, 0xa3, 0x46, 0x41, 0x7b, 0x51, 0xbb, 0xe9, 0xed, 0x9a, 0x45,
|
||||||
0x6f, 0xd4, 0xc9, 0xf9, 0x8d, 0x3a, 0x35, 0xa7, 0x51, 0xaf, 0x2c, 0x68, 0xd4, 0xe9, 0x59, 0x8d,
|
0x2d, 0xfb, 0xed, 0xa3, 0xb4, 0x9d, 0x12, 0x05, 0xe9, 0x2a, 0x0f, 0x80, 0xfc, 0xb1, 0x3f, 0x75,
|
||||||
0xda, 0x4f, 0xe3, 0x6a, 0x88, 0xc6, 0x7f, 0x09, 0xfe, 0xae, 0xc1, 0x3d, 0x3c, 0x3a, 0xe4, 0x69,
|
0x01, 0xd5, 0x79, 0x49, 0xff, 0x6b, 0x82, 0x7d, 0x18, 0xbc, 0xcf, 0x4f, 0x38, 0xe0, 0xbe, 0x8f,
|
||||||
0x3a, 0x8c, 0x91, 0xcc, 0xdb, 0xc7, 0x2b, 0x76, 0x92, 0xf0, 0x31, 0x49, 0xec, 0x3d, 0x5f, 0x62,
|
0x64, 0x22, 0xfe, 0x23, 0x99, 0xbc, 0xf8, 0x23, 0x99, 0xba, 0xe0, 0x23, 0xb9, 0x34, 0xe7, 0x23,
|
||||||
0x89, 0x90, 0xd1, 0x34, 0xc5, 0xbb, 0xc1, 0x14, 0x93, 0x21, 0x43, 0x7f, 0xb2, 0xf9, 0x49, 0xb2,
|
0x99, 0x9e, 0xf5, 0x91, 0xf4, 0x27, 0x71, 0x79, 0x5e, 0x12, 0x33, 0x6f, 0x97, 0xc4, 0xec, 0xa5,
|
||||||
0xa9, 0x90, 0x95, 0x97, 0x76, 0x31, 0x92, 0xf6, 0x4a, 0xc8, 0x32, 0x48, 0x80, 0x7c, 0x08, 0xd0,
|
0x92, 0xf8, 0x0f, 0xc1, 0xdf, 0xb1, 0x39, 0x5e, 0x2f, 0x19, 0xf2, 0x34, 0x98, 0x2c, 0x1f, 0x99,
|
||||||
0xe0, 0xbf, 0x44, 0x95, 0x72, 0x78, 0xd1, 0xe4, 0x4f, 0x60, 0xcb, 0xd3, 0x7a, 0x89, 0xdf, 0x81,
|
0x6f, 0x1f, 0x2d, 0xd9, 0x49, 0x92, 0x8d, 0x49, 0x58, 0xdf, 0xf1, 0x85, 0x35, 0x11, 0x12, 0x9a,
|
||||||
0x8c, 0xf7, 0xd3, 0x15, 0xae, 0xa2, 0xa7, 0xea, 0x44, 0x25, 0xf7, 0x61, 0xb3, 0x11, 0xf8, 0x85,
|
0x06, 0xf8, 0x6e, 0x30, 0xc0, 0xc9, 0x90, 0xa0, 0x3f, 0xd4, 0x85, 0x49, 0xa8, 0x53, 0x21, 0x29,
|
||||||
0x43, 0xf7, 0x60, 0xdd, 0xec, 0xeb, 0xed, 0x78, 0xe7, 0xac, 0xd9, 0xd7, 0x3d, 0x1f, 0x62, 0x3d,
|
0x2f, 0xe8, 0xa5, 0x48, 0xd0, 0x97, 0x42, 0x92, 0xc1, 0xf0, 0xcb, 0x7b, 0x00, 0x4d, 0x7e, 0x07,
|
||||||
0xc4, 0x6f, 0xa6, 0xd6, 0x89, 0x88, 0xf5, 0x10, 0xbf, 0xf1, 0xac, 0x65, 0x19, 0xd6, 0x79, 0xbb,
|
0xa9, 0x56, 0xc2, 0x25, 0x23, 0x7f, 0x0c, 0x6b, 0x1e, 0xd7, 0xdb, 0xf8, 0x1d, 0xc8, 0x78, 0x97,
|
||||||
0xe8, 0x6a, 0x75, 0xd7, 0x42, 0xc8, 0x5f, 0xac, 0xbc, 0x40, 0xcb, 0xb0, 0x5e, 0x1d, 0xf5, 0x5d,
|
0x96, 0x70, 0x0d, 0x3f, 0x51, 0x27, 0x2c, 0xb9, 0x0f, 0xf9, 0x66, 0xe0, 0x6e, 0x83, 0xee, 0xc1,
|
||||||
0xe3, 0x4c, 0xeb, 0xb8, 0xa6, 0xed, 0xa0, 0x8f, 0x21, 0x35, 0xe8, 0x6a, 0xde, 0x3d, 0x23, 0x3f,
|
0x8a, 0xd9, 0xd7, 0x3b, 0xf1, 0xca, 0x39, 0xb3, 0xaf, 0x7b, 0x3a, 0x44, 0x7a, 0x88, 0x5f, 0x4f,
|
||||||
0xb3, 0x1d, 0xfa, 0x1c, 0x54, 0x6a, 0x2d, 0xbb, 0x90, 0xf5, 0x09, 0xd1, 0x87, 0x90, 0x72, 0xc7,
|
0xa5, 0x13, 0x11, 0xe9, 0x21, 0x7e, 0xed, 0x49, 0xcb, 0x32, 0xac, 0xf0, 0x5e, 0x79, 0xaa, 0x35,
|
||||||
0x16, 0x9b, 0x68, 0xf3, 0xe4, 0x70, 0x76, 0x90, 0xae, 0xd6, 0x1a, 0x5b, 0x58, 0xa5, 0x96, 0xe8,
|
0x5c, 0x0b, 0x21, 0xff, 0x51, 0xe1, 0xc7, 0xa3, 0x02, 0x2b, 0xb5, 0x51, 0xdf, 0x35, 0x8e, 0xb5,
|
||||||
0xa3, 0xe0, 0x61, 0xfd, 0xfa, 0x6c, 0x97, 0xb3, 0x92, 0xff, 0xac, 0x2e, 0x7f, 0x23, 0xc0, 0x26,
|
0xae, 0x6b, 0xda, 0x0e, 0xfa, 0x08, 0x52, 0x83, 0x53, 0xcd, 0x7b, 0x61, 0x16, 0x66, 0x7e, 0x8a,
|
||||||
0x4b, 0x4d, 0xc5, 0x8e, 0x65, 0x0e, 0x9d, 0xc0, 0xfd, 0x42, 0x08, 0xdc, 0x2f, 0x44, 0x48, 0x8e,
|
0x7c, 0x0a, 0x2a, 0x95, 0x96, 0x5d, 0xc8, 0xf9, 0x88, 0xe8, 0x7d, 0x48, 0xb9, 0x63, 0x8b, 0x39,
|
||||||
0x6c, 0xaf, 0x29, 0x93, 0x4f, 0xb2, 0x47, 0x1c, 0xdc, 0xb1, 0xb1, 0xcb, 0xb7, 0x17, 0x1f, 0x4d,
|
0xca, 0x1f, 0xee, 0xcd, 0x36, 0x72, 0xaa, 0xb5, 0xc7, 0x16, 0x56, 0xa9, 0x24, 0xfa, 0x30, 0xf8,
|
||||||
0xa1, 0xa4, 0xde, 0x01, 0x8a, 0x0a, 0x9b, 0xf5, 0x4a, 0xf9, 0xf4, 0xb4, 0x6f, 0xe0, 0xa1, 0x5b,
|
0x4c, 0xbb, 0x3e, 0x5b, 0xe5, 0xb8, 0xec, 0x7f, 0xa5, 0xc9, 0x5f, 0x09, 0x90, 0x67, 0x5b, 0x53,
|
||||||
0x1a, 0xb9, 0x17, 0xe4, 0x74, 0xd0, 0xa1, 0xa3, 0x29, 0x96, 0x0c, 0x13, 0x54, 0x74, 0x74, 0x1b,
|
0xb1, 0x63, 0x99, 0x43, 0x27, 0xf0, 0xb2, 0x14, 0x02, 0x2f, 0x4b, 0x11, 0x92, 0x23, 0xdb, 0xfb,
|
||||||
0x36, 0xb8, 0x92, 0x43, 0x60, 0xb8, 0xd6, 0x99, 0xb0, 0x49, 0x65, 0xf2, 0xef, 0x04, 0xd8, 0xaf,
|
0x1e, 0x92, 0x9f, 0xe4, 0x84, 0x3a, 0xb8, 0x6b, 0x63, 0x97, 0x1f, 0x6e, 0xbe, 0x9a, 0x42, 0x49,
|
||||||
0x8e, 0x1b, 0xb6, 0xf9, 0x05, 0xee, 0xb8, 0x75, 0xbb, 0xd7, 0xc4, 0x9a, 0xdd, 0xb9, 0xf0, 0x2a,
|
0x5d, 0x02, 0x8a, 0x0a, 0xf9, 0x46, 0xb5, 0x72, 0x74, 0xd4, 0x37, 0xf0, 0xd0, 0x2d, 0x8f, 0xdc,
|
||||||
0x6e, 0x17, 0xd2, 0x66, 0xb7, 0xeb, 0x60, 0x97, 0x06, 0x4f, 0xa9, 0x7c, 0x44, 0x9a, 0x68, 0xdf,
|
0x33, 0x72, 0x2f, 0xec, 0xd2, 0xd5, 0x14, 0x4b, 0x86, 0x11, 0xaa, 0x3a, 0xba, 0x0d, 0xab, 0x9c,
|
||||||
0x18, 0x18, 0x2c, 0x64, 0x4a, 0x65, 0x03, 0x92, 0xbe, 0xe6, 0x74, 0x68, 0x4a, 0x19, 0x95, 0x7c,
|
0xc9, 0x21, 0x30, 0x5c, 0x2b, 0x8c, 0xd8, 0xa2, 0x34, 0xf9, 0xd7, 0x02, 0x6c, 0xd7, 0xc6, 0x4d,
|
||||||
0xa2, 0x33, 0x58, 0x7d, 0x3d, 0xc2, 0xb6, 0x81, 0xc9, 0x66, 0x20, 0x6b, 0x7d, 0x6f, 0x76, 0xa2,
|
0xdb, 0xfc, 0x1c, 0x77, 0xdd, 0x86, 0xdd, 0x6b, 0x61, 0xcd, 0xee, 0x9e, 0x79, 0x15, 0xb7, 0x09,
|
||||||
0x11, 0x00, 0x9f, 0x8d, 0xb0, 0x3d, 0x56, 0x3d, 0x67, 0xf9, 0x8f, 0x02, 0xec, 0xc5, 0x18, 0xa1,
|
0x69, 0xf3, 0xf4, 0xd4, 0xc1, 0x2e, 0x35, 0x9e, 0x52, 0xf9, 0x8a, 0x7c, 0x40, 0xfa, 0xc6, 0xc0,
|
||||||
0x33, 0x48, 0x7e, 0x89, 0xc7, 0xbc, 0x0c, 0x0a, 0x4b, 0xc6, 0x7f, 0x86, 0xc7, 0x74, 0x63, 0x7e,
|
0x60, 0x26, 0x53, 0x2a, 0x5b, 0x90, 0xed, 0x6b, 0x4e, 0x97, 0x6e, 0x29, 0xa3, 0x92, 0x9f, 0xe8,
|
||||||
0x2d, 0x24, 0xf2, 0x57, 0x54, 0x12, 0x00, 0x3d, 0x84, 0xf4, 0x00, 0xbb, 0x17, 0xa6, 0xce, 0xcb,
|
0x18, 0x96, 0x5f, 0x8d, 0xb0, 0x6d, 0x60, 0x72, 0x18, 0x48, 0xae, 0xef, 0xcd, 0xde, 0x68, 0x04,
|
||||||
0xe3, 0xd6, 0xcc, 0x50, 0xcc, 0xbd, 0x4a, 0x0d, 0x55, 0xee, 0x40, 0xe8, 0xb8, 0xd4, 0xfa, 0x23,
|
0xc0, 0xa7, 0x23, 0x6c, 0x8f, 0x55, 0x4f, 0x59, 0xfe, 0xbd, 0x00, 0x5b, 0x31, 0x42, 0xe8, 0x18,
|
||||||
0xef, 0x6c, 0xc7, 0x06, 0xf2, 0x6f, 0x05, 0xc8, 0xcd, 0xa2, 0x96, 0x57, 0xd1, 0xbb, 0x71, 0x7b,
|
0x92, 0x5f, 0xe0, 0x31, 0x2f, 0x83, 0xe2, 0x82, 0xf6, 0x9f, 0xe2, 0x31, 0x3d, 0x98, 0x6f, 0x84,
|
||||||
0x0b, 0xd6, 0x5d, 0x93, 0x74, 0x2a, 0x1b, 0x3b, 0xa3, 0x3e, 0x2b, 0xa7, 0x94, 0x9a, 0xa5, 0x32,
|
0x44, 0xe1, 0x8a, 0x4a, 0x0c, 0xa0, 0x07, 0x90, 0x1e, 0x60, 0xf7, 0xcc, 0xd4, 0x79, 0x79, 0xdc,
|
||||||
0x95, 0x8a, 0xd0, 0x87, 0xa4, 0x45, 0x51, 0x65, 0x8a, 0x72, 0x2d, 0xcd, 0x4c, 0xa0, 0x6e, 0xf7,
|
0x9a, 0x69, 0x8a, 0xa9, 0xd7, 0xa8, 0xa0, 0xca, 0x15, 0x48, 0x38, 0xce, 0xb5, 0xfe, 0xc8, 0xbb,
|
||||||
0x54, 0x6e, 0x27, 0xdf, 0x83, 0xad, 0x8a, 0x53, 0xd2, 0x07, 0xc6, 0x70, 0x82, 0x6a, 0x1f, 0x32,
|
0xd5, 0xb3, 0x85, 0xfc, 0x2b, 0x01, 0x76, 0x66, 0x85, 0x96, 0x57, 0xd1, 0xe5, 0x62, 0x7b, 0x0b,
|
||||||
0x86, 0xd3, 0xd6, 0x88, 0x8c, 0xe2, 0xca, 0xa8, 0xab, 0x06, 0x33, 0x91, 0xef, 0x42, 0xb2, 0x6e,
|
0x56, 0x5c, 0x93, 0x74, 0x2a, 0x1b, 0x3b, 0xa3, 0x3e, 0x2b, 0xa7, 0x94, 0x9a, 0xa3, 0x34, 0x95,
|
||||||
0xf7, 0x22, 0xbf, 0x2e, 0x08, 0x52, 0xbe, 0x53, 0x2f, 0xfd, 0x96, 0x1f, 0xc0, 0x46, 0x75, 0xdc,
|
0x92, 0xd0, 0xfb, 0xa4, 0x45, 0x51, 0x66, 0x8a, 0xc6, 0x5a, 0x9a, 0xb9, 0x81, 0x86, 0xdd, 0x53,
|
||||||
0xc0, 0xf6, 0xc0, 0x60, 0xef, 0x02, 0x28, 0x0f, 0x59, 0x6b, 0x3a, 0xa4, 0x1b, 0x7f, 0x4d, 0xf5,
|
0xb9, 0x9c, 0x7c, 0x0f, 0xd6, 0xaa, 0x4e, 0x59, 0x1f, 0x18, 0xc3, 0x09, 0xaa, 0x6d, 0xc8, 0x18,
|
||||||
0x8b, 0x0a, 0x76, 0xe0, 0x61, 0x82, 0x5d, 0xe4, 0xf3, 0x70, 0xf8, 0xbc, 0xa9, 0xa8, 0x4d, 0xa5,
|
0x4e, 0x47, 0x23, 0x34, 0x8a, 0x2b, 0xa3, 0x2e, 0x1b, 0x4c, 0x44, 0xbe, 0x0b, 0xc9, 0x86, 0xdd,
|
||||||
0xd9, 0xac, 0xd4, 0x6b, 0xcd, 0x56, 0xa9, 0xa5, 0xb4, 0x9f, 0xd7, 0x9a, 0x0d, 0xe5, 0xb4, 0x72,
|
0x8b, 0x7c, 0xdb, 0x10, 0xa4, 0x7c, 0xef, 0x1d, 0xfa, 0x5b, 0xfe, 0x00, 0x56, 0x6b, 0xe3, 0x26,
|
||||||
0x56, 0x51, 0xca, 0xe2, 0x15, 0x74, 0x00, 0x7b, 0x11, 0x8b, 0xd2, 0x69, 0xab, 0xf2, 0x42, 0x11,
|
0xb6, 0x07, 0x06, 0x9b, 0x08, 0xa1, 0x02, 0xe4, 0xac, 0xe9, 0x92, 0x1e, 0xfc, 0xac, 0xea, 0x27,
|
||||||
0x05, 0x74, 0x13, 0x0e, 0x22, 0xca, 0x96, 0xa2, 0x56, 0x2b, 0xb5, 0x52, 0x4b, 0x29, 0x8b, 0x89,
|
0x15, 0xed, 0xc0, 0x48, 0x8a, 0x8d, 0x70, 0x0a, 0xb0, 0xf7, 0xac, 0xa5, 0xa8, 0x2d, 0xa5, 0xd5,
|
||||||
0xc2, 0x6b, 0x10, 0xc9, 0x86, 0xf2, 0x92, 0x27, 0xad, 0x02, 0xed, 0xc3, 0x35, 0x2a, 0x53, 0x9a,
|
0xaa, 0x36, 0xea, 0xad, 0x76, 0xb9, 0xad, 0x74, 0x9e, 0xd5, 0x5b, 0x4d, 0xe5, 0xa8, 0x7a, 0x5c,
|
||||||
0x8d, 0x7a, 0xad, 0xa9, 0xb4, 0x5e, 0x36, 0x94, 0xf6, 0x69, 0xbd, 0xac, 0x88, 0x57, 0xd0, 0x75,
|
0x55, 0x2a, 0xe2, 0x15, 0xb4, 0x0b, 0x5b, 0x11, 0x89, 0xf2, 0x51, 0xbb, 0xfa, 0x5c, 0x11, 0x05,
|
||||||
0xd8, 0x8f, 0xa8, 0x2a, 0xe5, 0x76, 0xab, 0xfe, 0x4c, 0xa9, 0x89, 0x02, 0xba, 0x0d, 0x37, 0x63,
|
0x74, 0x13, 0x76, 0x23, 0xcc, 0xb6, 0xa2, 0xd6, 0xaa, 0xf5, 0x72, 0x5b, 0xa9, 0x88, 0x89, 0xe2,
|
||||||
0xd5, 0xdc, 0x28, 0x51, 0xf8, 0xbd, 0xc0, 0x0e, 0x27, 0x2c, 0xc1, 0x1c, 0xec, 0x52, 0x84, 0xfe,
|
0x2b, 0x10, 0xc9, 0x81, 0xf2, 0x36, 0x4f, 0x5a, 0x05, 0xda, 0x86, 0x6b, 0x94, 0xa6, 0xb4, 0x9a,
|
||||||
0xcc, 0x14, 0x9e, 0xda, 0x55, 0x10, 0xa7, 0xba, 0x49, 0x4e, 0xbb, 0x80, 0xa6, 0xd2, 0x4a, 0x8d,
|
0x8d, 0x7a, 0x4b, 0x69, 0xbf, 0x68, 0x2a, 0x9d, 0xa3, 0x46, 0x45, 0x11, 0xaf, 0xa0, 0xeb, 0xb0,
|
||||||
0xcb, 0x13, 0xe8, 0x1a, 0x6c, 0x4f, 0xe5, 0x65, 0xe5, 0x5c, 0x21, 0x19, 0x26, 0x83, 0x41, 0xce,
|
0x1d, 0x61, 0x55, 0x2b, 0x9d, 0x76, 0xe3, 0xa9, 0x52, 0x17, 0x05, 0x74, 0x1b, 0x6e, 0xc6, 0xb2,
|
||||||
0xeb, 0xa7, 0xcf, 0x94, 0xb2, 0x98, 0x0a, 0x1a, 0x37, 0x9f, 0x37, 0x1b, 0x4a, 0xad, 0x2c, 0xae,
|
0xb9, 0x50, 0xa2, 0xf8, 0x5b, 0x7e, 0x31, 0x63, 0x1b, 0xdc, 0x81, 0x4d, 0x8a, 0xd0, 0xbf, 0x33,
|
||||||
0x04, 0xc5, 0x95, 0x5a, 0xa5, 0x55, 0x29, 0x9d, 0x8b, 0xe9, 0xc2, 0x8f, 0x21, 0xcd, 0xce, 0xa6,
|
0x85, 0x6f, 0xed, 0x2a, 0x88, 0x53, 0xde, 0x64, 0x4f, 0x9b, 0x80, 0xa6, 0xd4, 0x6a, 0x9d, 0xd3,
|
||||||
0x64, 0xf2, 0x27, 0x4a, 0xad, 0xac, 0xa8, 0xa1, 0x55, 0xd8, 0x86, 0x0d, 0x2e, 0x3f, 0x53, 0xaa,
|
0x13, 0xe8, 0x1a, 0xac, 0x4f, 0xe9, 0x15, 0xe5, 0x44, 0x21, 0x3b, 0x4c, 0x06, 0x8d, 0x9c, 0x34,
|
||||||
0xa5, 0x73, 0x82, 0x73, 0x0b, 0xb2, 0x5c, 0x44, 0x05, 0x09, 0x84, 0x60, 0x93, 0x0b, 0xca, 0x95,
|
0x8e, 0x9e, 0x2a, 0x15, 0x31, 0x15, 0x14, 0x6e, 0x3d, 0x6b, 0x35, 0x95, 0x7a, 0x45, 0x5c, 0x0a,
|
||||||
0x17, 0x64, 0x51, 0xc4, 0x64, 0xa1, 0x0c, 0xab, 0xbc, 0x43, 0xa3, 0x3d, 0xd8, 0xa9, 0x9e, 0x95,
|
0x92, 0xab, 0xf5, 0x6a, 0xbb, 0x5a, 0x3e, 0x11, 0xd3, 0xc5, 0x1f, 0x42, 0x9a, 0xbd, 0x0b, 0x88,
|
||||||
0x28, 0x67, 0xc1, 0xd8, 0x5b, 0x90, 0xf5, 0x14, 0xcd, 0x6a, 0x93, 0x45, 0xf6, 0x04, 0xf5, 0x56,
|
0xf3, 0xc7, 0x4a, 0xbd, 0xa2, 0xa8, 0xa1, 0x2c, 0xac, 0xc3, 0x2a, 0xa7, 0x1f, 0x2b, 0xb5, 0xf2,
|
||||||
0x43, 0x4c, 0x14, 0xba, 0x90, 0xf1, 0x3a, 0x25, 0x92, 0xe0, 0x2a, 0xf9, 0x9e, 0x51, 0x29, 0xbb,
|
0x09, 0xc1, 0xb9, 0x06, 0x39, 0x4e, 0xa2, 0x84, 0x04, 0x42, 0x90, 0xe7, 0x84, 0x4a, 0xf5, 0x39,
|
||||||
0x80, 0x26, 0x9a, 0x5a, 0xbd, 0xd5, 0x56, 0x95, 0x52, 0xf9, 0xa5, 0x28, 0x10, 0x5c, 0x13, 0x39,
|
0x49, 0x8a, 0x98, 0x2c, 0x56, 0x60, 0x99, 0x77, 0x68, 0xb4, 0x05, 0x1b, 0xb5, 0xe3, 0x32, 0x8d,
|
||||||
0x93, 0x25, 0x08, 0x6b, 0x3e, 0x59, 0xb5, 0xfe, 0x82, 0x70, 0x59, 0x78, 0x05, 0xd7, 0x66, 0x36,
|
0x59, 0xd0, 0xf6, 0x1a, 0xe4, 0x3c, 0x46, 0xab, 0xd6, 0x62, 0x96, 0x3d, 0x42, 0xa3, 0xdd, 0x14,
|
||||||
0x12, 0x74, 0x07, 0x6e, 0x55, 0x5f, 0x36, 0xd4, 0xfa, 0x0f, 0x95, 0xd3, 0x56, 0x5d, 0x7d, 0xd2,
|
0x13, 0xc5, 0x53, 0xc8, 0x78, 0x9d, 0x12, 0x49, 0x70, 0x95, 0xfc, 0x9e, 0x51, 0x29, 0x9b, 0x80,
|
||||||
0x54, 0x4a, 0xea, 0xe9, 0xd3, 0x67, 0xca, 0xcb, 0x10, 0x02, 0x19, 0x6e, 0xcc, 0x36, 0xab, 0xab,
|
0x26, 0x9c, 0x7a, 0xa3, 0xdd, 0x51, 0x95, 0x72, 0xe5, 0x85, 0x28, 0x10, 0x5c, 0x13, 0x3a, 0xa3,
|
||||||
0x4f, 0xda, 0xb5, 0x52, 0x55, 0x11, 0x85, 0xc2, 0x4f, 0x61, 0xdd, 0xdf, 0x61, 0x08, 0x2d, 0xcc,
|
0x25, 0x48, 0xd4, 0x7c, 0xb4, 0x5a, 0xe3, 0x39, 0x89, 0x65, 0xf1, 0x25, 0x5c, 0x9b, 0xd9, 0x48,
|
||||||
0xae, 0xaa, 0xb4, 0x9e, 0xd6, 0xcb, 0x6d, 0xe5, 0xb3, 0xe7, 0xa5, 0xf3, 0xa6, 0x78, 0x05, 0x1d,
|
0xd0, 0x1d, 0xb8, 0x55, 0x7b, 0xd1, 0x54, 0x1b, 0xdf, 0x57, 0x8e, 0xda, 0x0d, 0xf5, 0x71, 0x4b,
|
||||||
0x82, 0x14, 0x50, 0x34, 0x5b, 0x25, 0xb5, 0xd5, 0x6c, 0xff, 0xa8, 0xd2, 0x7a, 0x2a, 0x0a, 0xa4,
|
0x29, 0xab, 0x47, 0x4f, 0x9e, 0x2a, 0x2f, 0x42, 0x08, 0x64, 0xb8, 0x31, 0x5b, 0xac, 0xa1, 0x3e,
|
||||||
0x88, 0x03, 0xda, 0xd3, 0x7a, 0xad, 0x55, 0xaa, 0xd4, 0x9a, 0x62, 0xe2, 0xe4, 0xaf, 0xfb, 0x90,
|
0xee, 0xd4, 0xcb, 0x35, 0x45, 0x14, 0x8a, 0x3f, 0x86, 0x15, 0x7f, 0x87, 0x21, 0x61, 0x61, 0x72,
|
||||||
0x25, 0xbf, 0x1d, 0x4d, 0x6c, 0x5f, 0x1a, 0x1d, 0x8c, 0x9e, 0xc1, 0xea, 0x53, 0xac, 0xf5, 0xdd,
|
0x35, 0xa5, 0xfd, 0xa4, 0x51, 0xe9, 0x28, 0x9f, 0x3e, 0x2b, 0x9f, 0xb4, 0xc4, 0x2b, 0x68, 0x0f,
|
||||||
0x8b, 0xaf, 0xd0, 0x6e, 0xe4, 0xf9, 0x45, 0x19, 0x58, 0xee, 0x38, 0x17, 0x23, 0x97, 0xc5, 0xaf,
|
0xa4, 0x00, 0xa3, 0xd5, 0x2e, 0xab, 0xed, 0x56, 0xe7, 0x07, 0xd5, 0xf6, 0x13, 0x51, 0x20, 0x45,
|
||||||
0xff, 0xf1, 0xef, 0x5f, 0x27, 0x00, 0x65, 0x8a, 0x17, 0x3c, 0xc2, 0x13, 0x58, 0x51, 0xb1, 0xa6,
|
0x1c, 0xe0, 0x1e, 0x35, 0xea, 0xed, 0x72, 0xb5, 0xde, 0x12, 0x13, 0x87, 0xbf, 0x91, 0x20, 0x47,
|
||||||
0x8f, 0xdf, 0x39, 0xd4, 0x26, 0x0d, 0x95, 0x41, 0xe9, 0xa2, 0x4d, 0xfd, 0x6b, 0x90, 0x79, 0xc1,
|
0xbe, 0x1d, 0x2d, 0x6c, 0x9f, 0x1b, 0x5d, 0x8c, 0x9e, 0xc2, 0xf2, 0x13, 0xac, 0xf5, 0xdd, 0xb3,
|
||||||
0x1f, 0x40, 0x63, 0x63, 0xed, 0x45, 0xe4, 0x4d, 0xfa, 0xd6, 0x2a, 0x6f, 0xd3, 0x60, 0x59, 0xb4,
|
0x2f, 0xd1, 0x66, 0xe4, 0x76, 0xa6, 0x0c, 0x2c, 0x77, 0xbc, 0x13, 0x43, 0x97, 0xc5, 0x37, 0x7f,
|
||||||
0x36, 0x79, 0x44, 0x45, 0x3f, 0x83, 0xed, 0x27, 0xd8, 0x65, 0x37, 0x1a, 0xef, 0xb1, 0x32, 0x36,
|
0xfb, 0xe7, 0x2f, 0x12, 0x80, 0x32, 0xa5, 0x33, 0x6e, 0xe1, 0x31, 0x2c, 0xa9, 0x58, 0xd3, 0xc7,
|
||||||
0xf0, 0x9d, 0x65, 0x1e, 0x3e, 0x1d, 0xf9, 0x83, 0xaf, 0xff, 0x24, 0x6d, 0xc1, 0x06, 0xb1, 0xc1,
|
0x97, 0x36, 0x95, 0xa7, 0xa6, 0x32, 0x28, 0x5d, 0xb2, 0xa9, 0x7e, 0x1d, 0x32, 0xcf, 0xf9, 0xe8,
|
||||||
0x43, 0xd7, 0xe8, 0x68, 0x2e, 0xd6, 0xe9, 0xcc, 0x08, 0x89, 0xc5, 0x01, 0x2e, 0x92, 0x43, 0x81,
|
0x3b, 0xd6, 0xd6, 0x56, 0x84, 0xde, 0xa2, 0x53, 0x76, 0x79, 0x9d, 0x1a, 0xcb, 0xa1, 0xec, 0x64,
|
||||||
0xf7, 0xa4, 0x8a, 0xbe, 0x02, 0x71, 0x02, 0xc0, 0x7b, 0x44, 0x88, 0x9b, 0x3f, 0x1f, 0x3b, 0x3f,
|
0x7c, 0x8e, 0x7e, 0x02, 0xeb, 0x8f, 0xb1, 0xcb, 0x9e, 0x73, 0xde, 0x98, 0x3a, 0xd6, 0xf0, 0x9d,
|
||||||
0xf7, 0x94, 0xef, 0xc5, 0x4d, 0xbd, 0x83, 0xb6, 0xd9, 0xbc, 0x04, 0x80, 0xc5, 0xe7, 0xf9, 0x8d,
|
0x45, 0x46, 0xde, 0x8e, 0xfc, 0xde, 0x9b, 0x3f, 0x4a, 0x6b, 0xb0, 0x4a, 0x64, 0xf0, 0xd0, 0x35,
|
||||||
0x00, 0x3b, 0xec, 0x34, 0x1d, 0x9c, 0xff, 0xfe, 0xec, 0x79, 0x62, 0x6e, 0xeb, 0x4b, 0xc0, 0x2a,
|
0xba, 0x9a, 0x8b, 0x75, 0xea, 0x19, 0x21, 0xb1, 0x34, 0xc0, 0x25, 0x72, 0x29, 0xf0, 0x86, 0xe9,
|
||||||
0xc6, 0xc1, 0xda, 0xcd, 0x45, 0x61, 0x3d, 0x12, 0x0a, 0xc8, 0x85, 0xcd, 0x09, 0x2b, 0xec, 0xa6,
|
0xe8, 0x4b, 0x10, 0x27, 0x00, 0xbc, 0xf1, 0x51, 0x9c, 0xff, 0x42, 0xac, 0x7f, 0xae, 0x29, 0xdf,
|
||||||
0x1b, 0xc7, 0x49, 0xfc, 0xcb, 0x2a, 0xf5, 0x93, 0x0b, 0x71, 0x53, 0x6f, 0xa3, 0xad, 0xe9, 0xd4,
|
0x8b, 0x73, 0xbd, 0x81, 0xd6, 0x99, 0x5f, 0x02, 0xc0, 0xe2, 0x7e, 0x7e, 0x29, 0xc0, 0x06, 0xbb,
|
||||||
0xec, 0x9e, 0xfc, 0xad, 0x00, 0xdb, 0xec, 0x64, 0xec, 0x9f, 0xf9, 0x83, 0x05, 0x6c, 0xf8, 0x2f,
|
0x4d, 0x07, 0xfd, 0xdf, 0x9f, 0xed, 0x27, 0x66, 0x52, 0xb2, 0x00, 0xac, 0x52, 0x1c, 0xac, 0xcd,
|
||||||
0xa1, 0x0b, 0xe1, 0xdc, 0x8f, 0x83, 0x73, 0x35, 0x17, 0x86, 0x43, 0x78, 0xf8, 0x95, 0x00, 0xdb,
|
0x9d, 0x28, 0xac, 0x87, 0x42, 0x11, 0xb9, 0x90, 0x9f, 0x44, 0x85, 0x0d, 0x19, 0xe2, 0x62, 0x12,
|
||||||
0x91, 0x4b, 0x77, 0xcc, 0xfa, 0xc4, 0x5d, 0xce, 0x63, 0xf7, 0xd6, 0xf7, 0xe2, 0xb0, 0x1c, 0xca,
|
0x3f, 0x53, 0xa7, 0x7a, 0x72, 0x31, 0xce, 0xf5, 0x3a, 0x5a, 0x9b, 0xba, 0x66, 0x23, 0x8a, 0xaf,
|
||||||
0x7b, 0x21, 0x2c, 0x45, 0x76, 0xf9, 0x1d, 0x13, 0x4c, 0xdf, 0x09, 0x70, 0x5d, 0xc5, 0x0e, 0x1e,
|
0x05, 0x58, 0x67, 0x37, 0x63, 0xbf, 0xe7, 0xf7, 0xe6, 0x44, 0xc3, 0xff, 0x02, 0x9f, 0x0b, 0xe7,
|
||||||
0xea, 0xd5, 0xb1, 0xef, 0xf5, 0xa0, 0x43, 0x1f, 0x90, 0xab, 0xf3, 0xd6, 0x2a, 0x0e, 0x48, 0x29,
|
0x7e, 0x1c, 0x9c, 0xab, 0x3b, 0x61, 0x38, 0x24, 0x0e, 0x3f, 0x17, 0x60, 0x3d, 0x32, 0x71, 0x88,
|
||||||
0x0e, 0xc8, 0x91, 0x7c, 0x3b, 0x02, 0xc4, 0xa6, 0x53, 0x5f, 0xfa, 0xe6, 0x0c, 0x17, 0x0c, 0xbb,
|
0xc9, 0x4f, 0xdc, 0x64, 0x22, 0xf6, 0x6c, 0x7d, 0x27, 0x0e, 0xcb, 0x9e, 0xbc, 0x15, 0xc2, 0x52,
|
||||||
0xdf, 0xbf, 0x7b, 0xc1, 0x50, 0xbf, 0x25, 0x0b, 0x86, 0xbd, 0x0e, 0x84, 0x0b, 0x86, 0xcd, 0xbc,
|
0x62, 0x0f, 0xff, 0x31, 0xc1, 0xf4, 0x8d, 0x00, 0xd7, 0x55, 0xec, 0xe0, 0xa1, 0x5e, 0x1b, 0xfb,
|
||||||
0xa8, 0x60, 0xfc, 0x77, 0xf8, 0x85, 0x70, 0x96, 0x2b, 0x18, 0x0a, 0x87, 0xf0, 0xf0, 0x6d, 0xa8,
|
0x06, 0x37, 0x5d, 0xfa, 0xc4, 0xab, 0x5d, 0x94, 0xab, 0x38, 0x20, 0xe5, 0x38, 0x20, 0xfb, 0xf2,
|
||||||
0x60, 0xe6, 0x21, 0x9a, 0xfd, 0xaa, 0xf0, 0x3f, 0x96, 0x0b, 0x45, 0x12, 0x57, 0x2e, 0xbe, 0xf7,
|
0xed, 0x08, 0x10, 0x9b, 0xba, 0x3e, 0xf7, 0xf9, 0x0c, 0x17, 0x0c, 0x9b, 0x6d, 0x5c, 0xbe, 0x60,
|
||||||
0x12, 0xb6, 0x74, 0xec, 0xbd, 0xe0, 0xff, 0x52, 0x2e, 0x1c, 0xc8, 0xec, 0x72, 0xf1, 0x77, 0x5d,
|
0xa8, 0xde, 0x82, 0x05, 0xc3, 0x26, 0x23, 0xe1, 0x82, 0x61, 0x9e, 0xe7, 0x15, 0x8c, 0x7f, 0x82,
|
||||||
0xef, 0xdd, 0xe1, 0xdd, 0xbb, 0xae, 0x77, 0x61, 0x5f, 0xae, 0xeb, 0xf2, 0xdb, 0x7f, 0xa4, 0xeb,
|
0x30, 0x17, 0xce, 0x62, 0x05, 0x43, 0xe1, 0x90, 0x38, 0x7c, 0x1d, 0x2a, 0x98, 0x8b, 0x10, 0xcd,
|
||||||
0x7a, 0xf3, 0x2f, 0xea, 0xba, 0xc1, 0xd7, 0x8e, 0x25, 0x60, 0x2d, 0xd7, 0x75, 0x39, 0x2c, 0xc2,
|
0x9e, 0x69, 0xbc, 0x65, 0xb9, 0x50, 0x24, 0x71, 0xe5, 0xe2, 0x9b, 0x15, 0xb1, 0xd4, 0xb1, 0x69,
|
||||||
0xca, 0x6b, 0x58, 0xa3, 0xac, 0x54, 0xbb, 0x5a, 0x3c, 0x1d, 0xb7, 0x16, 0xdd, 0xca, 0x1d, 0xf9,
|
0xc5, 0xff, 0xa4, 0x5c, 0x38, 0x90, 0xd9, 0xe5, 0xe2, 0xef, 0xba, 0xde, 0xd4, 0xe3, 0xf2, 0x5d,
|
||||||
0x6e, 0xdc, 0xc4, 0x22, 0xda, 0x9c, 0x4e, 0x4c, 0xee, 0xee, 0xe8, 0xe7, 0x02, 0x6c, 0x34, 0xc9,
|
0xd7, 0x7b, 0xb0, 0x2f, 0xd6, 0x75, 0xf9, 0xeb, 0x3f, 0xd2, 0x75, 0x3d, 0xff, 0xf3, 0xba, 0x6e,
|
||||||
0x9c, 0x93, 0x57, 0x86, 0xd9, 0x7f, 0x5d, 0x0c, 0x3d, 0x79, 0xc4, 0xd6, 0xc6, 0x83, 0xb8, 0xa9,
|
0x70, 0xda, 0xb1, 0x00, 0xac, 0xc5, 0xba, 0x2e, 0x87, 0x45, 0xa2, 0xf2, 0x0a, 0xb2, 0x34, 0x2a,
|
||||||
0xa5, 0xdc, 0x8e, 0xaf, 0x36, 0x78, 0x34, 0x9a, 0xf5, 0x2f, 0x05, 0x10, 0xbd, 0x4d, 0x3c, 0x41,
|
0xb5, 0x53, 0x2d, 0x3e, 0x1c, 0xb7, 0xe6, 0xbd, 0xca, 0x1d, 0xf9, 0x6e, 0x9c, 0x63, 0x11, 0xe5,
|
||||||
0x71, 0x7b, 0x2e, 0x0a, 0x66, 0x1e, 0x0b, 0xe2, 0x61, 0x1c, 0x88, 0x7c, 0xee, 0x60, 0x06, 0x88,
|
0xa7, 0x8e, 0xc9, 0xdb, 0x1d, 0xfd, 0x4c, 0x00, 0xd1, 0x3b, 0x41, 0x93, 0x41, 0xc3, 0xed, 0x99,
|
||||||
0x22, 0xff, 0x7b, 0x04, 0x01, 0x33, 0x86, 0xb5, 0x92, 0xae, 0x57, 0xbb, 0x5a, 0xbd, 0xd5, 0x88,
|
0x2e, 0x82, 0xb3, 0x8b, 0xd8, 0xea, 0x78, 0x10, 0xe7, 0xbc, 0xb0, 0xb3, 0xeb, 0xab, 0x0e, 0x6e,
|
||||||
0x5d, 0x82, 0xdb, 0x71, 0x6f, 0x1a, 0xbe, 0xc7, 0x88, 0x39, 0xab, 0x2f, 0x6f, 0x07, 0x16, 0xa1,
|
0xcc, 0x29, 0xf1, 0xff, 0x06, 0x22, 0xfb, 0x1f, 0x43, 0xb6, 0xac, 0xeb, 0xe4, 0x51, 0xdf, 0x6e,
|
||||||
0x68, 0xba, 0x16, 0x6f, 0x1d, 0xbe, 0x17, 0x9b, 0x56, 0x03, 0xdd, 0x9a, 0xf7, 0x33, 0x43, 0x27,
|
0xc6, 0xee, 0xff, 0x76, 0xdc, 0x40, 0xc1, 0x37, 0x09, 0xb8, 0x20, 0xf4, 0xf2, 0x7a, 0x20, 0x02,
|
||||||
0x5b, 0x0e, 0xc9, 0xf7, 0xe3, 0x90, 0xdc, 0xc8, 0xed, 0x47, 0x90, 0xf8, 0x5b, 0x47, 0x1f, 0xd6,
|
0x25, 0xd3, 0xb5, 0x88, 0xeb, 0x37, 0x82, 0x7f, 0x5c, 0xd2, 0x6e, 0xa2, 0x5b, 0x17, 0xf5, 0x78,
|
||||||
0x55, 0x3c, 0x30, 0x2f, 0xf1, 0x02, 0x3e, 0xe2, 0xd6, 0x21, 0x7e, 0x5f, 0x16, 0xa2, 0x14, 0xa0,
|
0xea, 0x2c, 0x36, 0x02, 0xdf, 0x8d, 0x73, 0x7e, 0x63, 0x67, 0x3b, 0xe2, 0xdc, 0x7f, 0x54, 0xfb,
|
||||||
0x3f, 0x08, 0xb0, 0xc3, 0xcf, 0xd8, 0xbe, 0xd3, 0xbc, 0x83, 0x8e, 0x97, 0x7c, 0x3a, 0xf0, 0x4a,
|
0xb0, 0xa2, 0xe2, 0x81, 0x79, 0x8e, 0xe7, 0x84, 0x20, 0xce, 0x71, 0xfc, 0x39, 0x28, 0x46, 0x77,
|
||||||
0xb3, 0xb8, 0xb4, 0x3d, 0xe7, 0x27, 0xbe, 0x5c, 0xe4, 0x83, 0x62, 0xaf, 0x6f, 0xbe, 0xd2, 0xfa,
|
0x8d, 0x7e, 0x27, 0xc0, 0x06, 0xbf, 0xd3, 0xfa, 0x6e, 0xcf, 0x0e, 0x3a, 0x58, 0xf0, 0xa9, 0xee,
|
||||||
0xe4, 0x6c, 0x44, 0x9c, 0x4d, 0xbb, 0xe7, 0x14, 0xdb, 0x0e, 0xf5, 0x67, 0x7d, 0x0c, 0x2a, 0x4e,
|
0x1d, 0x84, 0xd2, 0xc2, 0xf2, 0x3c, 0x39, 0xf1, 0x15, 0x22, 0xef, 0x96, 0x7a, 0x7d, 0xf3, 0xa5,
|
||||||
0x45, 0x1b, 0xd0, 0x8b, 0x78, 0x2c, 0x3f, 0xb3, 0xb7, 0x54, 0xe8, 0x86, 0x2f, 0x1f, 0xc7, 0xc1,
|
0xd6, 0x27, 0x77, 0x11, 0xa2, 0x6c, 0xda, 0x3d, 0xa7, 0xd4, 0x71, 0xa8, 0x3e, 0xeb, 0x1b, 0x50,
|
||||||
0xb8, 0x86, 0x76, 0x3c, 0x18, 0x6d, 0xc3, 0x31, 0xb4, 0x01, 0x7d, 0x05, 0x40, 0xbf, 0x10, 0x60,
|
0x75, 0xaa, 0xda, 0x80, 0x3e, 0x7c, 0x63, 0xe3, 0x33, 0xfb, 0x4f, 0x23, 0x42, 0x2f, 0x6a, 0xf9,
|
||||||
0x8f, 0xb6, 0x8b, 0xcf, 0x59, 0x70, 0xff, 0xb5, 0x3e, 0x0e, 0x89, 0x1c, 0xc7, 0xcd, 0xd4, 0x57,
|
0x20, 0x0e, 0xc6, 0x35, 0xb4, 0xe1, 0xc1, 0xe8, 0x18, 0x8e, 0xa1, 0x0d, 0xe8, 0xab, 0x1b, 0xfd,
|
||||||
0x3e, 0x89, 0xc3, 0xb1, 0x8f, 0xf6, 0x8a, 0xbe, 0x97, 0x81, 0x22, 0x0f, 0x55, 0x1c, 0xe0, 0xc7,
|
0x54, 0x80, 0x2d, 0x7a, 0x3c, 0x3f, 0x63, 0xc6, 0xfd, 0xcf, 0xe8, 0x38, 0x24, 0x72, 0x5c, 0x6c,
|
||||||
0x7f, 0x11, 0xbe, 0x2b, 0x7d, 0x23, 0xa0, 0x4f, 0x21, 0x43, 0xae, 0x30, 0xf9, 0x52, 0xa3, 0x22,
|
0xa6, 0xba, 0xf2, 0x61, 0x1c, 0x8e, 0x6d, 0xb4, 0x55, 0xf2, 0xbd, 0xc4, 0x4b, 0xdc, 0x54, 0x69,
|
||||||
0x17, 0xd0, 0xd1, 0x85, 0xeb, 0x5a, 0xce, 0xa3, 0x62, 0xb1, 0x67, 0xb8, 0x17, 0xa3, 0x57, 0xc7,
|
0x80, 0x1f, 0xfd, 0x59, 0xf8, 0xa6, 0xfc, 0x95, 0x80, 0x3e, 0x81, 0x0c, 0x79, 0x32, 0x14, 0xca,
|
||||||
0x1d, 0x73, 0x50, 0xec, 0x68, 0xe6, 0xd4, 0xd1, 0xfa, 0xb2, 0x57, 0x24, 0xd3, 0x9c, 0x24, 0x3f,
|
0xcd, 0xaa, 0x5c, 0x44, 0xfb, 0x67, 0xae, 0x6b, 0x39, 0x0f, 0x4b, 0xa5, 0x9e, 0xe1, 0x9e, 0x8d,
|
||||||
0x3c, 0x7e, 0x50, 0x10, 0x12, 0x27, 0xa2, 0x66, 0x59, 0x7d, 0xfe, 0x33, 0x51, 0xfc, 0xc2, 0x31,
|
0x5e, 0x1e, 0x74, 0xcd, 0x41, 0xa9, 0xab, 0x99, 0x53, 0x45, 0xeb, 0x8b, 0x5e, 0x89, 0xb8, 0x39,
|
||||||
0x87, 0x41, 0x49, 0xcf, 0xb6, 0x3a, 0x8f, 0x22, 0x36, 0x8f, 0x22, 0x36, 0x9f, 0xdf, 0x5d, 0x34,
|
0x4c, 0xbe, 0x7f, 0xf0, 0x41, 0x51, 0x48, 0x1c, 0x8a, 0x9a, 0x65, 0xf5, 0x79, 0x5b, 0x2e, 0x7d,
|
||||||
0x23, 0xfd, 0x1f, 0x24, 0xc4, 0xf4, 0x55, 0x9a, 0x32, 0xf4, 0xd1, 0x7f, 0x03, 0x00, 0x00, 0xff,
|
0xee, 0x98, 0xc3, 0x20, 0xa5, 0x67, 0x5b, 0xdd, 0x87, 0x11, 0x99, 0x87, 0x11, 0x99, 0xcf, 0xee,
|
||||||
0xff, 0x66, 0xd3, 0x09, 0x6b, 0x7c, 0x22, 0x00, 0x00,
|
0xce, 0xf3, 0x48, 0xff, 0x56, 0x87, 0x88, 0xbe, 0x4c, 0xd3, 0x08, 0x7d, 0xf8, 0xdf, 0x00, 0x00,
|
||||||
|
0x00, 0xff, 0xff, 0x64, 0x6c, 0xef, 0xe7, 0xe6, 0x23, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var _ context.Context
|
var _ context.Context
|
||||||
var _ grpc.ClientConn
|
var _ grpc.ClientConnInterface
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
// is compatible with the grpc package it is being compiled against.
|
// is compatible with the grpc package it is being compiled against.
|
||||||
const _ = grpc.SupportPackageIsVersion4
|
const _ = grpc.SupportPackageIsVersion6
|
||||||
|
|
||||||
// AuthServiceClient is the client API for AuthService service.
|
// AuthServiceClient is the client API for AuthService service.
|
||||||
//
|
//
|
||||||
@@ -2204,11 +2269,11 @@ type AuthServiceClient interface {
|
|||||||
GetMyUserAddress(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*UserAddress, error)
|
GetMyUserAddress(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*UserAddress, error)
|
||||||
UpdateMyUserAddress(ctx context.Context, in *UpdateUserAddressRequest, opts ...grpc.CallOption) (*UserAddress, error)
|
UpdateMyUserAddress(ctx context.Context, in *UpdateUserAddressRequest, opts ...grpc.CallOption) (*UserAddress, error)
|
||||||
GetMyMfas(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*MultiFactors, error)
|
GetMyMfas(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*MultiFactors, error)
|
||||||
SetMyPassword(ctx context.Context, in *PasswordRequest, opts ...grpc.CallOption) (*empty.Empty, error)
|
//Password
|
||||||
ChangeMyPassword(ctx context.Context, in *PasswordChange, opts ...grpc.CallOption) (*empty.Empty, error)
|
ChangeMyPassword(ctx context.Context, in *PasswordChange, opts ...grpc.CallOption) (*empty.Empty, error)
|
||||||
// MFA
|
// MFA
|
||||||
AddMfaOTP(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*MfaOtpResponse, error)
|
AddMfaOTP(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*MfaOtpResponse, error)
|
||||||
VerifyMfaOTP(ctx context.Context, in *VerifyMfaOtp, opts ...grpc.CallOption) (*MfaOtpResponse, error)
|
VerifyMfaOTP(ctx context.Context, in *VerifyMfaOtp, opts ...grpc.CallOption) (*empty.Empty, error)
|
||||||
RemoveMfaOTP(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*empty.Empty, error)
|
RemoveMfaOTP(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*empty.Empty, error)
|
||||||
SearchMyProjectOrgs(ctx context.Context, in *MyProjectOrgSearchRequest, opts ...grpc.CallOption) (*MyProjectOrgSearchResponse, error)
|
SearchMyProjectOrgs(ctx context.Context, in *MyProjectOrgSearchRequest, opts ...grpc.CallOption) (*MyProjectOrgSearchResponse, error)
|
||||||
IsIamAdmin(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*IsAdminResponse, error)
|
IsIamAdmin(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*IsAdminResponse, error)
|
||||||
@@ -2217,10 +2282,10 @@ type AuthServiceClient interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type authServiceClient struct {
|
type authServiceClient struct {
|
||||||
cc *grpc.ClientConn
|
cc grpc.ClientConnInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthServiceClient(cc *grpc.ClientConn) AuthServiceClient {
|
func NewAuthServiceClient(cc grpc.ClientConnInterface) AuthServiceClient {
|
||||||
return &authServiceClient{cc}
|
return &authServiceClient{cc}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2377,15 +2442,6 @@ func (c *authServiceClient) GetMyMfas(ctx context.Context, in *empty.Empty, opts
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *authServiceClient) SetMyPassword(ctx context.Context, in *PasswordRequest, opts ...grpc.CallOption) (*empty.Empty, error) {
|
|
||||||
out := new(empty.Empty)
|
|
||||||
err := c.cc.Invoke(ctx, "/zitadel.auth.api.v1.AuthService/SetMyPassword", in, out, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *authServiceClient) ChangeMyPassword(ctx context.Context, in *PasswordChange, opts ...grpc.CallOption) (*empty.Empty, error) {
|
func (c *authServiceClient) ChangeMyPassword(ctx context.Context, in *PasswordChange, opts ...grpc.CallOption) (*empty.Empty, error) {
|
||||||
out := new(empty.Empty)
|
out := new(empty.Empty)
|
||||||
err := c.cc.Invoke(ctx, "/zitadel.auth.api.v1.AuthService/ChangeMyPassword", in, out, opts...)
|
err := c.cc.Invoke(ctx, "/zitadel.auth.api.v1.AuthService/ChangeMyPassword", in, out, opts...)
|
||||||
@@ -2404,8 +2460,8 @@ func (c *authServiceClient) AddMfaOTP(ctx context.Context, in *empty.Empty, opts
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *authServiceClient) VerifyMfaOTP(ctx context.Context, in *VerifyMfaOtp, opts ...grpc.CallOption) (*MfaOtpResponse, error) {
|
func (c *authServiceClient) VerifyMfaOTP(ctx context.Context, in *VerifyMfaOtp, opts ...grpc.CallOption) (*empty.Empty, error) {
|
||||||
out := new(MfaOtpResponse)
|
out := new(empty.Empty)
|
||||||
err := c.cc.Invoke(ctx, "/zitadel.auth.api.v1.AuthService/VerifyMfaOTP", in, out, opts...)
|
err := c.cc.Invoke(ctx, "/zitadel.auth.api.v1.AuthService/VerifyMfaOTP", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -2471,11 +2527,11 @@ type AuthServiceServer interface {
|
|||||||
GetMyUserAddress(context.Context, *empty.Empty) (*UserAddress, error)
|
GetMyUserAddress(context.Context, *empty.Empty) (*UserAddress, error)
|
||||||
UpdateMyUserAddress(context.Context, *UpdateUserAddressRequest) (*UserAddress, error)
|
UpdateMyUserAddress(context.Context, *UpdateUserAddressRequest) (*UserAddress, error)
|
||||||
GetMyMfas(context.Context, *empty.Empty) (*MultiFactors, error)
|
GetMyMfas(context.Context, *empty.Empty) (*MultiFactors, error)
|
||||||
SetMyPassword(context.Context, *PasswordRequest) (*empty.Empty, error)
|
//Password
|
||||||
ChangeMyPassword(context.Context, *PasswordChange) (*empty.Empty, error)
|
ChangeMyPassword(context.Context, *PasswordChange) (*empty.Empty, error)
|
||||||
// MFA
|
// MFA
|
||||||
AddMfaOTP(context.Context, *empty.Empty) (*MfaOtpResponse, error)
|
AddMfaOTP(context.Context, *empty.Empty) (*MfaOtpResponse, error)
|
||||||
VerifyMfaOTP(context.Context, *VerifyMfaOtp) (*MfaOtpResponse, error)
|
VerifyMfaOTP(context.Context, *VerifyMfaOtp) (*empty.Empty, error)
|
||||||
RemoveMfaOTP(context.Context, *empty.Empty) (*empty.Empty, error)
|
RemoveMfaOTP(context.Context, *empty.Empty) (*empty.Empty, error)
|
||||||
SearchMyProjectOrgs(context.Context, *MyProjectOrgSearchRequest) (*MyProjectOrgSearchResponse, error)
|
SearchMyProjectOrgs(context.Context, *MyProjectOrgSearchRequest) (*MyProjectOrgSearchResponse, error)
|
||||||
IsIamAdmin(context.Context, *empty.Empty) (*IsAdminResponse, error)
|
IsIamAdmin(context.Context, *empty.Empty) (*IsAdminResponse, error)
|
||||||
@@ -2538,16 +2594,13 @@ func (*UnimplementedAuthServiceServer) UpdateMyUserAddress(ctx context.Context,
|
|||||||
func (*UnimplementedAuthServiceServer) GetMyMfas(ctx context.Context, req *empty.Empty) (*MultiFactors, error) {
|
func (*UnimplementedAuthServiceServer) GetMyMfas(ctx context.Context, req *empty.Empty) (*MultiFactors, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetMyMfas not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method GetMyMfas not implemented")
|
||||||
}
|
}
|
||||||
func (*UnimplementedAuthServiceServer) SetMyPassword(ctx context.Context, req *PasswordRequest) (*empty.Empty, error) {
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method SetMyPassword not implemented")
|
|
||||||
}
|
|
||||||
func (*UnimplementedAuthServiceServer) ChangeMyPassword(ctx context.Context, req *PasswordChange) (*empty.Empty, error) {
|
func (*UnimplementedAuthServiceServer) ChangeMyPassword(ctx context.Context, req *PasswordChange) (*empty.Empty, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method ChangeMyPassword not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method ChangeMyPassword not implemented")
|
||||||
}
|
}
|
||||||
func (*UnimplementedAuthServiceServer) AddMfaOTP(ctx context.Context, req *empty.Empty) (*MfaOtpResponse, error) {
|
func (*UnimplementedAuthServiceServer) AddMfaOTP(ctx context.Context, req *empty.Empty) (*MfaOtpResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method AddMfaOTP not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method AddMfaOTP not implemented")
|
||||||
}
|
}
|
||||||
func (*UnimplementedAuthServiceServer) VerifyMfaOTP(ctx context.Context, req *VerifyMfaOtp) (*MfaOtpResponse, error) {
|
func (*UnimplementedAuthServiceServer) VerifyMfaOTP(ctx context.Context, req *VerifyMfaOtp) (*empty.Empty, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method VerifyMfaOTP not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method VerifyMfaOTP not implemented")
|
||||||
}
|
}
|
||||||
func (*UnimplementedAuthServiceServer) RemoveMfaOTP(ctx context.Context, req *empty.Empty) (*empty.Empty, error) {
|
func (*UnimplementedAuthServiceServer) RemoveMfaOTP(ctx context.Context, req *empty.Empty) (*empty.Empty, error) {
|
||||||
@@ -2873,24 +2926,6 @@ func _AuthService_GetMyMfas_Handler(srv interface{}, ctx context.Context, dec fu
|
|||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func _AuthService_SetMyPassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(PasswordRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(AuthServiceServer).SetMyPassword(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: "/zitadel.auth.api.v1.AuthService/SetMyPassword",
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(AuthServiceServer).SetMyPassword(ctx, req.(*PasswordRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _AuthService_ChangeMyPassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
func _AuthService_ChangeMyPassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
in := new(PasswordChange)
|
in := new(PasswordChange)
|
||||||
if err := dec(in); err != nil {
|
if err := dec(in); err != nil {
|
||||||
@@ -3089,10 +3124,6 @@ var _AuthService_serviceDesc = grpc.ServiceDesc{
|
|||||||
MethodName: "GetMyMfas",
|
MethodName: "GetMyMfas",
|
||||||
Handler: _AuthService_GetMyMfas_Handler,
|
Handler: _AuthService_GetMyMfas_Handler,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
MethodName: "SetMyPassword",
|
|
||||||
Handler: _AuthService_SetMyPassword_Handler,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
MethodName: "ChangeMyPassword",
|
MethodName: "ChangeMyPassword",
|
||||||
Handler: _AuthService_ChangeMyPassword_Handler,
|
Handler: _AuthService_ChangeMyPassword_Handler,
|
||||||
|
@@ -13,7 +13,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/golang/protobuf/descriptor"
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
"github.com/golang/protobuf/ptypes/empty"
|
"github.com/golang/protobuf/ptypes/empty"
|
||||||
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||||
@@ -24,13 +23,11 @@ import (
|
|||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Suppress "imported and not used" errors
|
|
||||||
var _ codes.Code
|
var _ codes.Code
|
||||||
var _ io.Reader
|
var _ io.Reader
|
||||||
var _ status.Status
|
var _ status.Status
|
||||||
var _ = runtime.String
|
var _ = runtime.String
|
||||||
var _ = utilities.NewDoubleArray
|
var _ = utilities.NewDoubleArray
|
||||||
var _ = descriptor.ForMessage
|
|
||||||
|
|
||||||
func request_AuthService_Healthz_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
func request_AuthService_Healthz_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
var protoReq empty.Empty
|
var protoReq empty.Empty
|
||||||
@@ -466,40 +463,6 @@ func local_request_AuthService_GetMyMfas_0(ctx context.Context, marshaler runtim
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func request_AuthService_SetMyPassword_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq PasswordRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
|
||||||
if berr != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
|
||||||
}
|
|
||||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := client.SetMyPassword(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func local_request_AuthService_SetMyPassword_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
|
||||||
var protoReq PasswordRequest
|
|
||||||
var metadata runtime.ServerMetadata
|
|
||||||
|
|
||||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
|
||||||
if berr != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
|
||||||
}
|
|
||||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := server.SetMyPassword(ctx, &protoReq)
|
|
||||||
return msg, metadata, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func request_AuthService_ChangeMyPassword_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
func request_AuthService_ChangeMyPassword_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
var protoReq PasswordChange
|
var protoReq PasswordChange
|
||||||
var metadata runtime.ServerMetadata
|
var metadata runtime.ServerMetadata
|
||||||
@@ -693,13 +656,13 @@ func local_request_AuthService_GetMyZitadelPermissions_0(ctx context.Context, ma
|
|||||||
// RegisterAuthServiceHandlerServer registers the http handlers for service AuthService to "mux".
|
// RegisterAuthServiceHandlerServer registers the http handlers for service AuthService to "mux".
|
||||||
// UnaryRPC :call AuthServiceServer directly.
|
// UnaryRPC :call AuthServiceServer directly.
|
||||||
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
|
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
|
||||||
func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server AuthServiceServer) error {
|
func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server AuthServiceServer, opts []grpc.DialOption) error {
|
||||||
|
|
||||||
mux.Handle("GET", pattern_AuthService_Healthz_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
mux.Handle("GET", pattern_AuthService_Healthz_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -719,7 +682,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -739,7 +702,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -759,7 +722,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -779,7 +742,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -799,7 +762,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -819,7 +782,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -839,7 +802,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -859,7 +822,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -879,7 +842,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -899,7 +862,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -919,7 +882,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -939,7 +902,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -959,7 +922,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -979,7 +942,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -999,7 +962,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -1019,7 +982,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -1035,31 +998,11 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
mux.Handle("PUT", pattern_AuthService_SetMyPassword_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := local_request_AuthService_SetMyPassword_0(rctx, inboundMarshaler, server, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_AuthService_SetMyPassword_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
mux.Handle("PUT", pattern_AuthService_ChangeMyPassword_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
mux.Handle("PUT", pattern_AuthService_ChangeMyPassword_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -1079,7 +1022,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -1099,7 +1042,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -1119,7 +1062,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -1139,7 +1082,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -1159,7 +1102,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -1179,7 +1122,7 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
@@ -1576,26 +1519,6 @@ func RegisterAuthServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
mux.Handle("PUT", pattern_AuthService_SetMyPassword_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
|
||||||
defer cancel()
|
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
|
||||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, md, err := request_AuthService_SetMyPassword_0(rctx, inboundMarshaler, client, req, pathParams)
|
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
|
||||||
if err != nil {
|
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_AuthService_SetMyPassword_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
mux.Handle("PUT", pattern_AuthService_ChangeMyPassword_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
mux.Handle("PUT", pattern_AuthService_ChangeMyPassword_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -1774,8 +1697,6 @@ var (
|
|||||||
|
|
||||||
pattern_AuthService_GetMyMfas_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "mfas"}, "", runtime.AssumeColonVerbOpt(true)))
|
pattern_AuthService_GetMyMfas_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "mfas"}, "", runtime.AssumeColonVerbOpt(true)))
|
||||||
|
|
||||||
pattern_AuthService_SetMyPassword_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "passwords"}, "", runtime.AssumeColonVerbOpt(true)))
|
|
||||||
|
|
||||||
pattern_AuthService_ChangeMyPassword_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "passwords", "_change"}, "", runtime.AssumeColonVerbOpt(true)))
|
pattern_AuthService_ChangeMyPassword_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "passwords", "_change"}, "", runtime.AssumeColonVerbOpt(true)))
|
||||||
|
|
||||||
pattern_AuthService_AddMfaOTP_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "mfa", "otp"}, "", runtime.AssumeColonVerbOpt(true)))
|
pattern_AuthService_AddMfaOTP_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "mfa", "otp"}, "", runtime.AssumeColonVerbOpt(true)))
|
||||||
@@ -1826,8 +1747,6 @@ var (
|
|||||||
|
|
||||||
forward_AuthService_GetMyMfas_0 = runtime.ForwardResponseMessage
|
forward_AuthService_GetMyMfas_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
forward_AuthService_SetMyPassword_0 = runtime.ForwardResponseMessage
|
|
||||||
|
|
||||||
forward_AuthService_ChangeMyPassword_0 = runtime.ForwardResponseMessage
|
forward_AuthService_ChangeMyPassword_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
forward_AuthService_AddMfaOTP_0 = runtime.ForwardResponseMessage
|
forward_AuthService_AddMfaOTP_0 = runtime.ForwardResponseMessage
|
||||||
|
@@ -308,7 +308,7 @@
|
|||||||
"200": {
|
"200": {
|
||||||
"description": "A successful response.",
|
"description": "A successful response.",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/v1MfaOtpResponse"
|
"properties": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -343,34 +343,9 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/users/me/passwords": {
|
|
||||||
"put": {
|
|
||||||
"operationId": "SetMyPassword",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "A successful response.",
|
|
||||||
"schema": {
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"in": "body",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/v1PasswordRequest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"AuthService"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/users/me/passwords/_change": {
|
"/users/me/passwords/_change": {
|
||||||
"put": {
|
"put": {
|
||||||
|
"summary": "Password",
|
||||||
"operationId": "ChangeMyPassword",
|
"operationId": "ChangeMyPassword",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
@@ -732,14 +707,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"v1PasswordRequest": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"password": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"v1SearchMethod": {
|
"v1SearchMethod": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -832,6 +799,14 @@
|
|||||||
"sequence": {
|
"sequence": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "uint64"
|
"format": "uint64"
|
||||||
|
},
|
||||||
|
"creation_date": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
},
|
||||||
|
"change_date": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -851,6 +826,14 @@
|
|||||||
"sequence": {
|
"sequence": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "uint64"
|
"format": "uint64"
|
||||||
|
},
|
||||||
|
"creation_date": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
},
|
||||||
|
"change_date": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -870,6 +853,14 @@
|
|||||||
"sequence": {
|
"sequence": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "uint64"
|
"format": "uint64"
|
||||||
|
},
|
||||||
|
"creation_date": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
},
|
||||||
|
"change_date": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -903,6 +894,14 @@
|
|||||||
"sequence": {
|
"sequence": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "uint64"
|
"format": "uint64"
|
||||||
|
},
|
||||||
|
"creation_date": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
},
|
||||||
|
"change_date": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -4,20 +4,28 @@ import (
|
|||||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
|
auth_util "github.com/caos/zitadel/internal/api/auth"
|
||||||
grpc_util "github.com/caos/zitadel/internal/api/grpc"
|
grpc_util "github.com/caos/zitadel/internal/api/grpc"
|
||||||
"github.com/caos/zitadel/internal/api/grpc/server/middleware"
|
"github.com/caos/zitadel/internal/api/grpc/server/middleware"
|
||||||
|
"github.com/caos/zitadel/internal/auth/auth"
|
||||||
|
"github.com/caos/zitadel/internal/auth/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ AuthServiceServer = (*Server)(nil)
|
var _ AuthServiceServer = (*Server)(nil)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
port string
|
port string
|
||||||
searchLimit int
|
repo repository.Repository
|
||||||
|
verifier *auth.TokenVerifier
|
||||||
|
authZ auth_util.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartServer(conf grpc_util.ServerConfig) *Server {
|
func StartServer(conf grpc_util.ServerConfig, authZ auth_util.Config, repo repository.Repository) *Server {
|
||||||
return &Server{
|
return &Server{
|
||||||
port: conf.Port,
|
port: conf.Port,
|
||||||
|
repo: repo,
|
||||||
|
authZ: authZ,
|
||||||
|
verifier: auth.Start(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,6 +39,7 @@ func (s *Server) GRPCServer() (*grpc.Server, error) {
|
|||||||
grpc.UnaryInterceptor(
|
grpc.UnaryInterceptor(
|
||||||
grpc_middleware.ChainUnaryServer(
|
grpc_middleware.ChainUnaryServer(
|
||||||
middleware.ErrorHandler(),
|
middleware.ErrorHandler(),
|
||||||
|
AuthService_Authorization_Interceptor(s.verifier, &s.authZ),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@@ -2,24 +2,42 @@ package grpc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/caos/zitadel/internal/errors"
|
|
||||||
"github.com/golang/protobuf/ptypes/empty"
|
"github.com/golang/protobuf/ptypes/empty"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) GetMyUserProfile(ctx context.Context, _ *empty.Empty) (*UserProfile, error) {
|
func (s *Server) GetMyUserProfile(ctx context.Context, _ *empty.Empty) (*UserProfile, error) {
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-fis93", "Not implemented")
|
profile, err := s.repo.MyProfile(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return profileFromModel(profile), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) GetMyUserEmail(ctx context.Context, _ *empty.Empty) (*UserEmail, error) {
|
func (s *Server) GetMyUserEmail(ctx context.Context, _ *empty.Empty) (*UserEmail, error) {
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-93j5d", "Not implemented")
|
email, err := s.repo.MyEmail(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return emailFromModel(email), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) GetMyUserPhone(ctx context.Context, _ *empty.Empty) (*UserPhone, error) {
|
func (s *Server) GetMyUserPhone(ctx context.Context, _ *empty.Empty) (*UserPhone, error) {
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-Hj75G", "Not implemented")
|
phone, err := s.repo.MyPhone(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return phoneFromModel(phone), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) GetMyUserAddress(ctx context.Context, _ *empty.Empty) (*UserAddress, error) {
|
func (s *Server) GetMyUserAddress(ctx context.Context, _ *empty.Empty) (*UserAddress, error) {
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-21jd4", "Not implemented")
|
address, err := s.repo.MyAddress(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return addressFromModel(address), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) GetMyMfas(ctx context.Context, _ *empty.Empty) (*MultiFactors, error) {
|
func (s *Server) GetMyMfas(ctx context.Context, _ *empty.Empty) (*MultiFactors, error) {
|
||||||
@@ -27,57 +45,76 @@ func (s *Server) GetMyMfas(ctx context.Context, _ *empty.Empty) (*MultiFactors,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) UpdateMyUserProfile(ctx context.Context, request *UpdateUserProfileRequest) (*UserProfile, error) {
|
func (s *Server) UpdateMyUserProfile(ctx context.Context, request *UpdateUserProfileRequest) (*UserProfile, error) {
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-dlep3", "Not implemented")
|
profile, err := s.repo.ChangeMyProfile(ctx, updateProfileToModel(ctx, request))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return profileFromModel(profile), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ChangeMyUserEmail(ctx context.Context, request *UpdateUserEmailRequest) (*UserEmail, error) {
|
func (s *Server) ChangeMyUserEmail(ctx context.Context, request *UpdateUserEmailRequest) (*UserEmail, error) {
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-lme45", "Not implemented")
|
email, err := s.repo.ChangeMyEmail(ctx, updateEmailToModel(ctx, request))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return emailFromModel(email), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) VerifyMyUserEmail(ctx context.Context, request *VerifyMyUserEmailRequest) (*empty.Empty, error) {
|
func (s *Server) VerifyMyUserEmail(ctx context.Context, request *VerifyMyUserEmailRequest) (*empty.Empty, error) {
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-poru7", "Not implemented")
|
err := s.repo.VerifyMyEmail(ctx, request.Code)
|
||||||
}
|
return &empty.Empty{}, err
|
||||||
|
|
||||||
func (s *Server) VerifyUserEmail(ctx context.Context, request *VerifyUserEmailRequest) (*empty.Empty, error) {
|
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-dlep3", "Not implemented")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ResendMyEmailVerificationMail(ctx context.Context, _ *empty.Empty) (*empty.Empty, error) {
|
func (s *Server) ResendMyEmailVerificationMail(ctx context.Context, _ *empty.Empty) (*empty.Empty, error) {
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-dh69i", "Not implemented")
|
err := s.repo.ResendMyEmailVerificationMail(ctx)
|
||||||
|
return &empty.Empty{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ChangeMyUserPhone(ctx context.Context, request *UpdateUserPhoneRequest) (*UserPhone, error) {
|
func (s *Server) ChangeMyUserPhone(ctx context.Context, request *UpdateUserPhoneRequest) (*UserPhone, error) {
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-dk45g", "Not implemented")
|
phone, err := s.repo.ChangeMyPhone(ctx, updatePhoneToModel(ctx, request))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return phoneFromModel(phone), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) VerifyMyUserPhone(ctx context.Context, request *VerifyUserPhoneRequest) (*empty.Empty, error) {
|
func (s *Server) VerifyMyUserPhone(ctx context.Context, request *VerifyUserPhoneRequest) (*empty.Empty, error) {
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-ol6gE", "Not implemented")
|
err := s.repo.VerifyMyPhone(ctx, request.Code)
|
||||||
|
return &empty.Empty{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ResendMyPhoneVerificationCode(ctx context.Context, _ *empty.Empty) (*empty.Empty, error) {
|
func (s *Server) ResendMyPhoneVerificationCode(ctx context.Context, _ *empty.Empty) (*empty.Empty, error) {
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-Wk8rf", "Not implemented")
|
err := s.repo.ResendMyPhoneVerificationCode(ctx)
|
||||||
|
return &empty.Empty{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) UpdateMyUserAddress(ctx context.Context, request *UpdateUserAddressRequest) (*UserAddress, error) {
|
func (s *Server) UpdateMyUserAddress(ctx context.Context, request *UpdateUserAddressRequest) (*UserAddress, error) {
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-cmt7F", "Not implemented")
|
address, err := s.repo.ChangeMyAddress(ctx, updateAddressToModel(ctx, request))
|
||||||
}
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
func (s *Server) SetMyPassword(ctx context.Context, request *PasswordRequest) (*empty.Empty, error) {
|
}
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-pl9c2", "Not implemented")
|
return addressFromModel(address), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ChangeMyPassword(ctx context.Context, request *PasswordChange) (*empty.Empty, error) {
|
func (s *Server) ChangeMyPassword(ctx context.Context, request *PasswordChange) (*empty.Empty, error) {
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-dlo6G", "Not implemented")
|
err := s.repo.ChangeMyPassword(ctx, request.OldPassword, request.NewPassword)
|
||||||
|
return &empty.Empty{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) AddMfaOTP(ctx context.Context, _ *empty.Empty) (_ *MfaOtpResponse, err error) {
|
func (s *Server) AddMfaOTP(ctx context.Context, _ *empty.Empty) (_ *MfaOtpResponse, err error) {
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-al35G", "Not implemented")
|
otp, err := s.repo.AddMyMfaOTP(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return otpFromModel(otp), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) VerifyMfaOTP(ctx context.Context, request *VerifyMfaOtp) (_ *MfaOtpResponse, err error) {
|
func (s *Server) VerifyMfaOTP(ctx context.Context, request *VerifyMfaOtp) (*empty.Empty, error) {
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-kgjZ7", "Not implemented")
|
err := s.repo.VerifyMyMfaOTP(ctx, request.Code)
|
||||||
|
return &empty.Empty{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) RemoveMfaOTP(ctx context.Context, _ *empty.Empty) (_ *empty.Empty, err error) {
|
func (s *Server) RemoveMfaOTP(ctx context.Context, _ *empty.Empty) (_ *empty.Empty, err error) {
|
||||||
return nil, errors.ThrowUnimplemented(nil, "GRPC-9k46d", "Not implemented")
|
s.repo.RemoveMyMfaOTP(ctx)
|
||||||
|
return &empty.Empty{}, err
|
||||||
}
|
}
|
||||||
|
174
pkg/auth/api/grpc/user_converter.go
Normal file
174
pkg/auth/api/grpc/user_converter.go
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/caos/logging"
|
||||||
|
"github.com/caos/zitadel/internal/api/auth"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/models"
|
||||||
|
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||||
|
"github.com/golang/protobuf/ptypes"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
func profileFromModel(profile *usr_model.Profile) *UserProfile {
|
||||||
|
creationDate, err := ptypes.TimestampProto(profile.CreationDate)
|
||||||
|
logging.Log("GRPC-56t5s").OnError(err).Debug("unable to parse timestamp")
|
||||||
|
|
||||||
|
changeDate, err := ptypes.TimestampProto(profile.ChangeDate)
|
||||||
|
logging.Log("GRPC-K58ds").OnError(err).Debug("unable to parse timestamp")
|
||||||
|
|
||||||
|
return &UserProfile{
|
||||||
|
Id: profile.AggregateID,
|
||||||
|
CreationDate: creationDate,
|
||||||
|
ChangeDate: changeDate,
|
||||||
|
Sequence: profile.Sequence,
|
||||||
|
UserName: profile.UserName,
|
||||||
|
FirstName: profile.FirstName,
|
||||||
|
LastName: profile.LastName,
|
||||||
|
DisplayName: profile.DisplayName,
|
||||||
|
NickName: profile.NickName,
|
||||||
|
PreferredLanguage: profile.PreferredLanguage.String(),
|
||||||
|
Gender: genderFromModel(profile.Gender),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateProfileToModel(ctx context.Context, u *UpdateUserProfileRequest) *usr_model.Profile {
|
||||||
|
preferredLanguage, err := language.Parse(u.PreferredLanguage)
|
||||||
|
logging.Log("GRPC-lk73L").OnError(err).Debug("language malformed")
|
||||||
|
|
||||||
|
return &usr_model.Profile{
|
||||||
|
ObjectRoot: models.ObjectRoot{AggregateID: auth.GetCtxData(ctx).UserID},
|
||||||
|
FirstName: u.FirstName,
|
||||||
|
LastName: u.LastName,
|
||||||
|
NickName: u.NickName,
|
||||||
|
DisplayName: u.DisplayName,
|
||||||
|
PreferredLanguage: preferredLanguage,
|
||||||
|
Gender: genderToModel(u.Gender),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func emailFromModel(email *usr_model.Email) *UserEmail {
|
||||||
|
creationDate, err := ptypes.TimestampProto(email.CreationDate)
|
||||||
|
logging.Log("GRPC-sdoi3").OnError(err).Debug("unable to parse timestamp")
|
||||||
|
|
||||||
|
changeDate, err := ptypes.TimestampProto(email.ChangeDate)
|
||||||
|
logging.Log("GRPC-klJK3").OnError(err).Debug("unable to parse timestamp")
|
||||||
|
|
||||||
|
return &UserEmail{
|
||||||
|
Id: email.AggregateID,
|
||||||
|
CreationDate: creationDate,
|
||||||
|
ChangeDate: changeDate,
|
||||||
|
Sequence: email.Sequence,
|
||||||
|
Email: email.EmailAddress,
|
||||||
|
IsEmailVerified: email.IsEmailVerified,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateEmailToModel(ctx context.Context, e *UpdateUserEmailRequest) *usr_model.Email {
|
||||||
|
return &usr_model.Email{
|
||||||
|
ObjectRoot: models.ObjectRoot{AggregateID: auth.GetCtxData(ctx).UserID},
|
||||||
|
EmailAddress: e.Email,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func phoneFromModel(phone *usr_model.Phone) *UserPhone {
|
||||||
|
creationDate, err := ptypes.TimestampProto(phone.CreationDate)
|
||||||
|
logging.Log("GRPC-kjn5J").OnError(err).Debug("unable to parse timestamp")
|
||||||
|
|
||||||
|
changeDate, err := ptypes.TimestampProto(phone.ChangeDate)
|
||||||
|
logging.Log("GRPC-LKA9S").OnError(err).Debug("unable to parse timestamp")
|
||||||
|
|
||||||
|
return &UserPhone{
|
||||||
|
Id: phone.AggregateID,
|
||||||
|
CreationDate: creationDate,
|
||||||
|
ChangeDate: changeDate,
|
||||||
|
Sequence: phone.Sequence,
|
||||||
|
Phone: phone.PhoneNumber,
|
||||||
|
IsPhoneVerified: phone.IsPhoneVerified,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updatePhoneToModel(ctx context.Context, e *UpdateUserPhoneRequest) *usr_model.Phone {
|
||||||
|
return &usr_model.Phone{
|
||||||
|
ObjectRoot: models.ObjectRoot{AggregateID: auth.GetCtxData(ctx).UserID},
|
||||||
|
PhoneNumber: e.Phone,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addressFromModel(address *usr_model.Address) *UserAddress {
|
||||||
|
creationDate, err := ptypes.TimestampProto(address.CreationDate)
|
||||||
|
logging.Log("GRPC-65FRs").OnError(err).Debug("unable to parse timestamp")
|
||||||
|
|
||||||
|
changeDate, err := ptypes.TimestampProto(address.ChangeDate)
|
||||||
|
logging.Log("GRPC-aslk4").OnError(err).Debug("unable to parse timestamp")
|
||||||
|
|
||||||
|
return &UserAddress{
|
||||||
|
Id: address.AggregateID,
|
||||||
|
CreationDate: creationDate,
|
||||||
|
ChangeDate: changeDate,
|
||||||
|
Sequence: address.Sequence,
|
||||||
|
Country: address.Country,
|
||||||
|
StreetAddress: address.StreetAddress,
|
||||||
|
Region: address.Region,
|
||||||
|
PostalCode: address.PostalCode,
|
||||||
|
Locality: address.Locality,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateAddressToModel(ctx context.Context, address *UpdateUserAddressRequest) *usr_model.Address {
|
||||||
|
return &usr_model.Address{
|
||||||
|
ObjectRoot: models.ObjectRoot{AggregateID: auth.GetCtxData(ctx).UserID},
|
||||||
|
Country: address.Country,
|
||||||
|
StreetAddress: address.StreetAddress,
|
||||||
|
Region: address.Region,
|
||||||
|
PostalCode: address.PostalCode,
|
||||||
|
Locality: address.Locality,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func otpFromModel(otp *usr_model.OTP) *MfaOtpResponse {
|
||||||
|
return &MfaOtpResponse{
|
||||||
|
UserId: otp.AggregateID,
|
||||||
|
Url: otp.Url,
|
||||||
|
Secret: otp.SecretString,
|
||||||
|
State: mfaStateFromModel(otp.State),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func genderFromModel(gender usr_model.Gender) Gender {
|
||||||
|
switch gender {
|
||||||
|
case usr_model.GENDER_FEMALE:
|
||||||
|
return Gender_GENDER_FEMALE
|
||||||
|
case usr_model.GENDER_MALE:
|
||||||
|
return Gender_GENDER_MALE
|
||||||
|
case usr_model.GENDER_DIVERSE:
|
||||||
|
return Gender_GENDER_DIVERSE
|
||||||
|
default:
|
||||||
|
return Gender_GENDER_UNSPECIFIED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func genderToModel(gender Gender) usr_model.Gender {
|
||||||
|
switch gender {
|
||||||
|
case Gender_GENDER_FEMALE:
|
||||||
|
return usr_model.GENDER_FEMALE
|
||||||
|
case Gender_GENDER_MALE:
|
||||||
|
return usr_model.GENDER_MALE
|
||||||
|
case Gender_GENDER_DIVERSE:
|
||||||
|
return usr_model.GENDER_DIVERSE
|
||||||
|
default:
|
||||||
|
return usr_model.GENDER_UNDEFINED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mfaStateFromModel(state usr_model.MfaState) MFAState {
|
||||||
|
switch state {
|
||||||
|
case usr_model.MFASTATE_READY:
|
||||||
|
return MFAState_MFASTATE_NOT_READY
|
||||||
|
case usr_model.MFASTATE_NOTREADY:
|
||||||
|
return MFAState_MFASTATE_NOT_READY
|
||||||
|
default:
|
||||||
|
return MFAState_MFASTATE_UNSPECIFIED
|
||||||
|
}
|
||||||
|
}
|
@@ -13,515 +13,511 @@ package zitadel.auth.api.v1;
|
|||||||
option go_package = "github.com/caos/zitadel/pkg/auth/api/grpc";
|
option go_package = "github.com/caos/zitadel/pkg/auth/api/grpc";
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
|
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
|
||||||
info: {
|
info: {
|
||||||
title: "Auth API";
|
title: "Auth API";
|
||||||
version: "0.1";
|
version: "0.1";
|
||||||
contact:{
|
contact:{
|
||||||
url: "https://github.com/caos/zitadel/pkg/auth"
|
url: "https://github.com/caos/zitadel/pkg/auth"
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
schemes: HTTPS;
|
schemes: HTTPS;
|
||||||
|
|
||||||
consumes: "application/json";
|
consumes: "application/json";
|
||||||
consumes: "application/grpc";
|
consumes: "application/grpc";
|
||||||
|
|
||||||
produces: "application/json";
|
produces: "application/json";
|
||||||
produces: "application/grpc";
|
produces: "application/grpc";
|
||||||
};
|
};
|
||||||
|
|
||||||
service AuthService {
|
service AuthService {
|
||||||
// Readiness
|
// Readiness
|
||||||
rpc Healthz(google.protobuf.Empty) returns (google.protobuf.Empty) {
|
rpc Healthz(google.protobuf.Empty) returns (google.protobuf.Empty) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
get: "/healthz"
|
get: "/healthz"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc Ready(google.protobuf.Empty) returns (google.protobuf.Empty) {
|
rpc Ready(google.protobuf.Empty) returns (google.protobuf.Empty) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
get: "/ready"
|
get: "/ready"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc Validate(google.protobuf.Empty) returns (google.protobuf.Struct) {
|
rpc Validate(google.protobuf.Empty) returns (google.protobuf.Struct) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
get: "/validate"
|
get: "/validate"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authorization
|
// Authorization
|
||||||
rpc GetMyUserSessions(google.protobuf.Empty) returns (UserSessionViews) {
|
rpc GetMyUserSessions(google.protobuf.Empty) returns (UserSessionViews) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
get: "/me/usersessions"
|
get: "/me/usersessions"
|
||||||
};
|
};
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
permission: "authenticated"
|
permission: "authenticated"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
//User
|
//User
|
||||||
rpc GetMyUserProfile(google.protobuf.Empty) returns (UserProfile) {
|
rpc GetMyUserProfile(google.protobuf.Empty) returns (UserProfile) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
get: "/users/me/profile"
|
get: "/users/me/profile"
|
||||||
};
|
};
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
permission: "authenticated"
|
permission: "authenticated"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc UpdateMyUserProfile(UpdateUserProfileRequest) returns (UserProfile) {
|
rpc UpdateMyUserProfile(UpdateUserProfileRequest) returns (UserProfile) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
put: "/users/me/profile"
|
put: "/users/me/profile"
|
||||||
body: "*"
|
body: "*"
|
||||||
};
|
};
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
permission: "authenticated"
|
permission: "authenticated"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc GetMyUserEmail(google.protobuf.Empty) returns (UserEmail) {
|
rpc GetMyUserEmail(google.protobuf.Empty) returns (UserEmail) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
get: "/users/me/email"
|
get: "/users/me/email"
|
||||||
};
|
};
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
permission: "authenticated"
|
permission: "authenticated"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc ChangeMyUserEmail(UpdateUserEmailRequest) returns (UserEmail) {
|
rpc ChangeMyUserEmail(UpdateUserEmailRequest) returns (UserEmail) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
put: "/users/me/email"
|
put: "/users/me/email"
|
||||||
body: "*"
|
body: "*"
|
||||||
};
|
};
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
permission: "authenticated"
|
permission: "authenticated"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc VerifyMyUserEmail(VerifyMyUserEmailRequest) returns (google.protobuf.Empty) {
|
rpc VerifyMyUserEmail(VerifyMyUserEmailRequest) returns (google.protobuf.Empty) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
post: "/users/me/email/_verify"
|
post: "/users/me/email/_verify"
|
||||||
body: "*"
|
body: "*"
|
||||||
};
|
};
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
permission: "authenticated"
|
permission: "authenticated"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc ResendMyEmailVerificationMail(google.protobuf.Empty) returns (google.protobuf.Empty) {
|
rpc ResendMyEmailVerificationMail(google.protobuf.Empty) returns (google.protobuf.Empty) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
post: "/users/me/email/_resendverification"
|
post: "/users/me/email/_resendverification"
|
||||||
body: "*"
|
body: "*"
|
||||||
};
|
};
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
permission: "authenticated"
|
permission: "authenticated"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc GetMyUserPhone(google.protobuf.Empty) returns (UserPhone) {
|
rpc GetMyUserPhone(google.protobuf.Empty) returns (UserPhone) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
get: "/users/me/phone"
|
get: "/users/me/phone"
|
||||||
};
|
};
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
permission: "authenticated"
|
permission: "authenticated"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc ChangeMyUserPhone(UpdateUserPhoneRequest) returns (UserPhone) {
|
rpc ChangeMyUserPhone(UpdateUserPhoneRequest) returns (UserPhone) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
put: "/users/me/phone"
|
put: "/users/me/phone"
|
||||||
body: "*"
|
body: "*"
|
||||||
};
|
};
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
permission: "authenticated"
|
permission: "authenticated"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc VerifyMyUserPhone(VerifyUserPhoneRequest) returns (google.protobuf.Empty) {
|
rpc VerifyMyUserPhone(VerifyUserPhoneRequest) returns (google.protobuf.Empty) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
post: "/users/me/phone/_verify"
|
post: "/users/me/phone/_verify"
|
||||||
body: "*"
|
body: "*"
|
||||||
};
|
};
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
permission: "authenticated"
|
permission: "authenticated"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc ResendMyPhoneVerificationCode(google.protobuf.Empty) returns (google.protobuf.Empty) {
|
rpc ResendMyPhoneVerificationCode(google.protobuf.Empty) returns (google.protobuf.Empty) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
post: "/users/me/phone/_resendverification"
|
post: "/users/me/phone/_resendverification"
|
||||||
body: "*"
|
body: "*"
|
||||||
};
|
};
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
permission: "authenticated"
|
permission: "authenticated"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc GetMyUserAddress(google.protobuf.Empty) returns (UserAddress) {
|
rpc GetMyUserAddress(google.protobuf.Empty) returns (UserAddress) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
get: "/users/me/address"
|
get: "/users/me/address"
|
||||||
};
|
};
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
permission: "authenticated"
|
permission: "authenticated"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc UpdateMyUserAddress(UpdateUserAddressRequest) returns (UserAddress) {
|
rpc UpdateMyUserAddress(UpdateUserAddressRequest) returns (UserAddress) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
put: "/users/me/address"
|
put: "/users/me/address"
|
||||||
body: "*"
|
body: "*"
|
||||||
};
|
};
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
permission: "authenticated"
|
permission: "authenticated"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc GetMyMfas(google.protobuf.Empty) returns (MultiFactors) {
|
rpc GetMyMfas(google.protobuf.Empty) returns (MultiFactors) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
get: "/users/me/mfas"
|
get: "/users/me/mfas"
|
||||||
};
|
};
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
permission: "authenticated"
|
permission: "authenticated"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
//Password
|
//Password
|
||||||
|
rpc ChangeMyPassword(PasswordChange) returns (google.protobuf.Empty) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
put: "/users/me/passwords/_change"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
|
||||||
rpc SetMyPassword(PasswordRequest) returns (google.protobuf.Empty) {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
option (google.api.http) = {
|
permission: "authenticated"
|
||||||
put: "/users/me/passwords"
|
};
|
||||||
body: "*"
|
}
|
||||||
};
|
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
// MFA
|
||||||
permission: "authenticated"
|
rpc AddMfaOTP(google.protobuf.Empty) returns (MfaOtpResponse) {
|
||||||
};
|
option (google.api.http) = {
|
||||||
}
|
post: "/users/me/mfa/otp"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
|
permission: "authenticated"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
rpc ChangeMyPassword(PasswordChange) returns (google.protobuf.Empty) {
|
rpc VerifyMfaOTP(VerifyMfaOtp) returns (google.protobuf.Empty) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
put: "/users/me/passwords/_change"
|
put: "/users/me/mfa/otp/_verify"
|
||||||
body: "*"
|
body: "*"
|
||||||
};
|
};
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
permission: "authenticated"
|
permission: "authenticated"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// MFA
|
rpc RemoveMfaOTP(google.protobuf.Empty) returns (google.protobuf.Empty) {
|
||||||
rpc AddMfaOTP(google.protobuf.Empty) returns (MfaOtpResponse) {
|
option (google.api.http) = {
|
||||||
option (google.api.http) = {
|
delete: "/users/me/mfa/otp"
|
||||||
post: "/users/me/mfa/otp"
|
};
|
||||||
body: "*"
|
|
||||||
};
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
|
||||||
permission: "authenticated"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
rpc VerifyMfaOTP(VerifyMfaOtp) returns (MfaOtpResponse) {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
option (google.api.http) = {
|
permission: "authenticated"
|
||||||
put: "/users/me/mfa/otp/_verify"
|
};
|
||||||
body: "*"
|
}
|
||||||
};
|
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
rpc SearchMyProjectOrgs(MyProjectOrgSearchRequest) returns (MyProjectOrgSearchResponse) {
|
||||||
permission: "authenticated"
|
option (google.api.http) = {
|
||||||
};
|
post: "/global/projectorgs/_search"
|
||||||
}
|
body: "*"
|
||||||
|
};
|
||||||
|
|
||||||
rpc RemoveMfaOTP(google.protobuf.Empty) returns (google.protobuf.Empty) {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
option (google.api.http) = {
|
permission: "authenticated"
|
||||||
delete: "/users/me/mfa/otp"
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
rpc IsIamAdmin(google.protobuf.Empty) returns (IsAdminResponse) {
|
||||||
permission: "authenticated"
|
option (google.api.http) = {
|
||||||
};
|
get: "/global/_isiamadmin"
|
||||||
}
|
};
|
||||||
|
|
||||||
rpc SearchMyProjectOrgs(MyProjectOrgSearchRequest) returns (MyProjectOrgSearchResponse) {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
option (google.api.http) = {
|
permission: "authenticated"
|
||||||
post: "/global/projectorgs/_search"
|
};
|
||||||
body: "*"
|
}
|
||||||
};
|
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
//Permission
|
||||||
permission: "authenticated"
|
rpc GetMyZitadelPermissions(google.protobuf.Empty) returns (MyPermissions) {
|
||||||
};
|
option (google.api.http) = {
|
||||||
}
|
get: "/permissions/zitadel/me"
|
||||||
|
};
|
||||||
|
|
||||||
rpc IsIamAdmin(google.protobuf.Empty) returns (IsAdminResponse) {
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
option (google.api.http) = {
|
permission: "authenticated"
|
||||||
get: "/global/_isiamadmin"
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
|
||||||
permission: "authenticated"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//Permission
|
|
||||||
rpc GetMyZitadelPermissions(google.protobuf.Empty) returns (MyPermissions) {
|
|
||||||
option (google.api.http) = {
|
|
||||||
get: "/permissions/zitadel/me"
|
|
||||||
};
|
|
||||||
|
|
||||||
option (caos.zitadel.utils.v1.auth_option) = {
|
|
||||||
permission: "authenticated"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message UserSessionViews {
|
message UserSessionViews {
|
||||||
repeated UserSessionView user_sessions = 1;
|
repeated UserSessionView user_sessions = 1;
|
||||||
}
|
}
|
||||||
message UserSessionView {
|
message UserSessionView {
|
||||||
string id = 1;
|
string id = 1;
|
||||||
string agent_id = 2;
|
string agent_id = 2;
|
||||||
UserSessionState auth_state = 3;
|
UserSessionState auth_state = 3;
|
||||||
string user_id = 4;
|
string user_id = 4;
|
||||||
string user_name = 5;
|
string user_name = 5;
|
||||||
uint64 sequence = 6;
|
uint64 sequence = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum UserSessionState {
|
enum UserSessionState {
|
||||||
USERSESSIONSTATE_UNSPECIFIED = 0;
|
USERSESSIONSTATE_UNSPECIFIED = 0;
|
||||||
USERSESSIONSTATE_ACTIVE = 1;
|
USERSESSIONSTATE_ACTIVE = 1;
|
||||||
USERSESSIONSTATE_TERMINATED = 2;
|
USERSESSIONSTATE_TERMINATED = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum OIDCResponseType {
|
enum OIDCResponseType {
|
||||||
OIDCRESPONSETYPE_CODE = 0;
|
OIDCRESPONSETYPE_CODE = 0;
|
||||||
OIDCRESPONSETYPE_ID_TOKEN = 1;
|
OIDCRESPONSETYPE_ID_TOKEN = 1;
|
||||||
OIDCRESPONSETYPE_ID_TOKEN_TOKEN = 2;
|
OIDCRESPONSETYPE_ID_TOKEN_TOKEN = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message User {
|
message User {
|
||||||
string id = 1;
|
string id = 1;
|
||||||
UserState state = 2;
|
UserState state = 2;
|
||||||
google.protobuf.Timestamp creation_date = 3;
|
google.protobuf.Timestamp creation_date = 3;
|
||||||
google.protobuf.Timestamp activation_date = 4;
|
google.protobuf.Timestamp activation_date = 4;
|
||||||
google.protobuf.Timestamp change_date = 5;
|
google.protobuf.Timestamp change_date = 5;
|
||||||
google.protobuf.Timestamp last_login = 6;
|
google.protobuf.Timestamp last_login = 6;
|
||||||
google.protobuf.Timestamp password_changed = 7;
|
google.protobuf.Timestamp password_changed = 7;
|
||||||
string user_name = 8;
|
string user_name = 8;
|
||||||
string first_name = 9;
|
string first_name = 9;
|
||||||
string last_name = 10;
|
string last_name = 10;
|
||||||
string nick_name = 11;
|
string nick_name = 11;
|
||||||
string display_name = 12;
|
string display_name = 12;
|
||||||
string preferred_language = 13;
|
string preferred_language = 13;
|
||||||
Gender gender = 14;
|
Gender gender = 14;
|
||||||
string email = 15;
|
string email = 15;
|
||||||
bool is_email_verified = 16;
|
bool is_email_verified = 16;
|
||||||
string phone = 17;
|
string phone = 17;
|
||||||
bool is_phone_verified = 18;
|
bool is_phone_verified = 18;
|
||||||
string country = 19;
|
string country = 19;
|
||||||
string locality = 20;
|
string locality = 20;
|
||||||
string postal_code = 21;
|
string postal_code = 21;
|
||||||
string region = 22;
|
string region = 22;
|
||||||
string street_address = 23;
|
string street_address = 23;
|
||||||
bool password_change_required = 24;
|
bool password_change_required = 24;
|
||||||
uint64 sequence = 25;
|
uint64 sequence = 25;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum UserState {
|
enum UserState {
|
||||||
USERSTATE_UNSPECIEFIED = 0;
|
USERSTATE_UNSPECIEFIED = 0;
|
||||||
USERSTATE_ACTIVE = 1;
|
USERSTATE_ACTIVE = 1;
|
||||||
USERSTATE_INACTIVE = 2;
|
USERSTATE_INACTIVE = 2;
|
||||||
USERSTATE_DELETED = 3;
|
USERSTATE_DELETED = 3;
|
||||||
USERSTATE_LOCKED = 4;
|
USERSTATE_LOCKED = 4;
|
||||||
USERSTATE_SUSPEND = 5;
|
USERSTATE_SUSPEND = 5;
|
||||||
USERSTATE_INITIAL= 6;
|
USERSTATE_INITIAL = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Gender {
|
enum Gender {
|
||||||
GENDER_UNSPECIFIED = 0;
|
GENDER_UNSPECIFIED = 0;
|
||||||
GENDER_FEMALE = 1;
|
GENDER_FEMALE = 1;
|
||||||
GENDER_MALE = 2;
|
GENDER_MALE = 2;
|
||||||
GENDER_DIVERSE = 3;
|
GENDER_DIVERSE = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UserProfile {
|
message UserProfile {
|
||||||
string id = 1;
|
string id = 1;
|
||||||
string user_name = 2;
|
string user_name = 2;
|
||||||
string first_name = 3;
|
string first_name = 3;
|
||||||
string last_name = 4;
|
string last_name = 4;
|
||||||
string nick_name = 5;
|
string nick_name = 5;
|
||||||
string display_name = 6;
|
string display_name = 6;
|
||||||
string preferred_language = 7;
|
string preferred_language = 7;
|
||||||
Gender gender = 8;
|
Gender gender = 8;
|
||||||
uint64 sequence = 26;
|
uint64 sequence = 9;
|
||||||
|
google.protobuf.Timestamp creation_date = 10;
|
||||||
|
google.protobuf.Timestamp change_date = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UpdateUserProfileRequest {
|
message UpdateUserProfileRequest {
|
||||||
string first_name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
string first_name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||||
string last_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
string last_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||||
string nick_name = 3 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
string nick_name = 3 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||||
string display_name = 4 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
string display_name = 4 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||||
string preferred_language = 5 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
string preferred_language = 5 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||||
Gender gender = 6;
|
Gender gender = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UserEmail {
|
message UserEmail {
|
||||||
string id = 1;
|
string id = 1;
|
||||||
string email = 2;
|
string email = 2;
|
||||||
bool isEmailVerified = 3;
|
bool isEmailVerified = 3;
|
||||||
uint64 sequence = 4;
|
uint64 sequence = 4;
|
||||||
|
google.protobuf.Timestamp creation_date = 5;
|
||||||
|
google.protobuf.Timestamp change_date = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message VerifyMyUserEmailRequest {
|
message VerifyMyUserEmailRequest {
|
||||||
string code = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
string code = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||||
}
|
}
|
||||||
|
|
||||||
message VerifyUserEmailRequest {
|
message VerifyUserEmailRequest {
|
||||||
string id = 1;
|
string id = 1;
|
||||||
string code = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
string code = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||||
}
|
}
|
||||||
|
|
||||||
message UpdateUserEmailRequest {
|
message UpdateUserEmailRequest {
|
||||||
string email = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
string email = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||||
}
|
}
|
||||||
|
|
||||||
message UserPhone {
|
message UserPhone {
|
||||||
string id = 1;
|
string id = 1;
|
||||||
string phone = 2;
|
string phone = 2;
|
||||||
bool is_phone_verified = 3;
|
bool is_phone_verified = 3;
|
||||||
uint64 sequence = 4;
|
uint64 sequence = 4;
|
||||||
|
google.protobuf.Timestamp creation_date = 5;
|
||||||
|
google.protobuf.Timestamp change_date = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UpdateUserPhoneRequest {
|
message UpdateUserPhoneRequest {
|
||||||
string phone = 1 [(validate.rules).string = {min_len: 1, max_len: 20}];
|
string phone = 1 [(validate.rules).string = {min_len: 1, max_len: 20}];
|
||||||
}
|
}
|
||||||
|
|
||||||
message VerifyUserPhoneRequest {
|
message VerifyUserPhoneRequest {
|
||||||
string code = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
string code = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||||
}
|
}
|
||||||
|
|
||||||
message UserAddress {
|
message UserAddress {
|
||||||
string id = 1;
|
string id = 1;
|
||||||
string country = 2;
|
string country = 2;
|
||||||
string locality = 3;
|
string locality = 3;
|
||||||
string postal_code = 4;
|
string postal_code = 4;
|
||||||
string region = 5;
|
string region = 5;
|
||||||
string street_address = 6;
|
string street_address = 6;
|
||||||
uint64 sequence = 7;
|
uint64 sequence = 7;
|
||||||
|
google.protobuf.Timestamp creation_date = 8;
|
||||||
|
google.protobuf.Timestamp change_date = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UpdateUserAddressRequest {
|
message UpdateUserAddressRequest {
|
||||||
string country = 1 [(validate.rules).string = {max_len: 200}];
|
string country = 1 [(validate.rules).string = {max_len: 200}];
|
||||||
string locality = 2 [(validate.rules).string = {max_len: 200}];
|
string locality = 2 [(validate.rules).string = {max_len: 200}];
|
||||||
string postal_code = 3 [(validate.rules).string = {max_len: 200}];
|
string postal_code = 3 [(validate.rules).string = {max_len: 200}];
|
||||||
string region = 4 [(validate.rules).string = {max_len: 200}];
|
string region = 4 [(validate.rules).string = {max_len: 200}];
|
||||||
string street_address = 5 [(validate.rules).string = {max_len: 200}];
|
string street_address = 5 [(validate.rules).string = {max_len: 200}];
|
||||||
}
|
}
|
||||||
|
|
||||||
message PasswordID {
|
message PasswordID {
|
||||||
string id = 1;
|
string id = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PasswordRequest {
|
message PasswordRequest {
|
||||||
string password = 1 [(validate.rules).string = {min_len: 1, max_len: 72}];
|
string password = 1 [(validate.rules).string = {min_len: 1, max_len: 72}];
|
||||||
}
|
}
|
||||||
|
|
||||||
message PasswordChange {
|
message PasswordChange {
|
||||||
string old_password = 1 [(validate.rules).string = {min_len: 1, max_len: 72}];
|
string old_password = 1 [(validate.rules).string = {min_len: 1, max_len: 72}];
|
||||||
string new_password = 2 [(validate.rules).string = {min_len: 1, max_len: 72}];
|
string new_password = 2 [(validate.rules).string = {min_len: 1, max_len: 72}];
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MfaType {
|
enum MfaType {
|
||||||
MFATYPE_UNSPECIFIED = 0;
|
MFATYPE_UNSPECIFIED = 0;
|
||||||
MFATYPE_SMS = 1;
|
MFATYPE_SMS = 1;
|
||||||
MFATYPE_OTP = 2;
|
MFATYPE_OTP = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message VerifyMfaOtp {
|
message VerifyMfaOtp {
|
||||||
string code = 1;
|
string code = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message MultiFactors {
|
message MultiFactors {
|
||||||
repeated MultiFactor mfas = 1;
|
repeated MultiFactor mfas = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message MultiFactor {
|
message MultiFactor {
|
||||||
MfaType type = 1;
|
MfaType type = 1;
|
||||||
MFAState state = 2;
|
MFAState state = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message MfaOtpResponse {
|
message MfaOtpResponse {
|
||||||
string user_id = 1;
|
string user_id = 1;
|
||||||
string url = 2;
|
string url = 2;
|
||||||
string secret = 3;
|
string secret = 3;
|
||||||
MFAState state = 4;
|
MFAState state = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MFAState {
|
enum MFAState {
|
||||||
MFASTATE_UNSPECIFIED = 0;
|
MFASTATE_UNSPECIFIED = 0;
|
||||||
MFASTATE_NOT_READY = 1;
|
MFASTATE_NOT_READY = 1;
|
||||||
MFASTATE_READY = 2;
|
MFASTATE_READY = 2;
|
||||||
MFASTATE_REMOVED = 3;
|
MFASTATE_REMOVED = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message OIDCClientAuth {
|
message OIDCClientAuth {
|
||||||
string client_id = 1;
|
string client_id = 1;
|
||||||
string client_secret = 2;
|
string client_secret = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message MyProjectOrgSearchRequest {
|
message MyProjectOrgSearchRequest {
|
||||||
uint64 offset = 1;
|
uint64 offset = 1;
|
||||||
uint64 limit = 2;
|
uint64 limit = 2;
|
||||||
bool asc = 4;
|
bool asc = 4;
|
||||||
repeated MyProjectOrgSearchQuery queries = 5;
|
repeated MyProjectOrgSearchQuery queries = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message MyProjectOrgSearchQuery {
|
message MyProjectOrgSearchQuery {
|
||||||
MyProjectOrgSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}];;
|
MyProjectOrgSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}];;
|
||||||
SearchMethod method = 2;
|
SearchMethod method = 2;
|
||||||
string value = 3;
|
string value = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MyProjectOrgSearchKey {
|
enum MyProjectOrgSearchKey {
|
||||||
MYPROJECTORGSEARCHKEY_UNSPECIFIED = 0;
|
MYPROJECTORGSEARCHKEY_UNSPECIFIED = 0;
|
||||||
MYPROJECTORGSEARCHKEY_ORG_NAME = 1;
|
MYPROJECTORGSEARCHKEY_ORG_NAME = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message MyProjectOrgSearchResponse {
|
message MyProjectOrgSearchResponse {
|
||||||
uint64 offset = 1;
|
uint64 offset = 1;
|
||||||
uint64 limit = 2;
|
uint64 limit = 2;
|
||||||
uint64 total_result = 3;
|
uint64 total_result = 3;
|
||||||
repeated Org result = 4;
|
repeated Org result = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message IsAdminResponse {
|
message IsAdminResponse {
|
||||||
bool is_admin = 1;
|
bool is_admin = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Org {
|
message Org {
|
||||||
string id = 1;
|
string id = 1;
|
||||||
string name = 2;
|
string name = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message MyPermissions {
|
message MyPermissions {
|
||||||
repeated string permissions = 1;
|
repeated string permissions = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SearchMethod {
|
enum SearchMethod {
|
||||||
SEARCHMETHOD_EQUALS = 0;
|
SEARCHMETHOD_EQUALS = 0;
|
||||||
SEARCHMETHOD_STARTS_WITH = 1;
|
SEARCHMETHOD_STARTS_WITH = 1;
|
||||||
SEARCHMETHOD_CONTAINS = 2;
|
SEARCHMETHOD_CONTAINS = 2;
|
||||||
}
|
}
|
@@ -2,17 +2,23 @@ package auth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/caos/logging"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/api/auth"
|
"github.com/caos/zitadel/internal/api/auth"
|
||||||
app "github.com/caos/zitadel/internal/auth"
|
"github.com/caos/zitadel/internal/auth/repository/eventsourcing"
|
||||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||||
"github.com/caos/zitadel/pkg/auth/api"
|
"github.com/caos/zitadel/pkg/auth/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
App app.Config
|
API api.Config
|
||||||
API api.Config
|
Repository eventsourcing.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start(ctx context.Context, config Config, authZ auth.Config, systemDefaults sd.SystemDefaults) {
|
func Start(ctx context.Context, config Config, authZ auth.Config, systemDefaults sd.SystemDefaults) {
|
||||||
api.Start(ctx, config.API)
|
repo, err := eventsourcing.Start(config.Repository, systemDefaults)
|
||||||
|
logging.Log("MAIN-9uBxp").OnError(err).Panic("unable to start app")
|
||||||
|
|
||||||
|
api.Start(ctx, config.API, authZ, repo)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user