feat: User login commands (#1228)

* feat: change login to command side

* feat: change login to command side

* fix: fix push on user

* feat: user command side

* feat: sign out

* feat: command side login

* feat: command side login

* feat: fix register user

* feat: fix register user

* feat: fix web auth n events

* feat: add machine keys

* feat: send codes

* feat: move authrequest to domain

* feat: move authrequest to domain

* feat: webauthn working

* feat: external users

* feat: external users login

* feat: notify users

* fix: tests

* feat: cascade remove user grants on project remove

* fix: webauthn

* fix: pr requests

* fix: register human with member

* fix: fix bugs

* fix: fix bugs
This commit is contained in:
Fabi 2021-02-08 11:30:30 +01:00 committed by GitHub
parent c65331df1a
commit 320679467b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
123 changed files with 2949 additions and 1212 deletions

View File

@ -115,7 +115,7 @@ func startZitadel(configPaths []string) {
logging.Log("MAIN-s9KOw").OnError(err).Fatal("error starting authz repo")
var authRepo *auth_es.EsRepository
if *authEnabled || *oidcEnabled || *loginEnabled {
authRepo, err = auth_es.Start(conf.Auth, conf.InternalAuthZ, conf.SystemDefaults, authZRepo)
authRepo, err = auth_es.Start(conf.Auth, conf.InternalAuthZ, conf.SystemDefaults, command, authZRepo)
logging.Log("MAIN-9oRw6").OnError(err).Fatal("error starting auth repo")
}
@ -123,7 +123,7 @@ func startZitadel(configPaths []string) {
startUI(ctx, conf, authRepo, command, query)
if *notificationEnabled {
notification.Start(ctx, conf.Notification, conf.SystemDefaults)
notification.Start(ctx, conf.Notification, conf.SystemDefaults, command)
}
<-ctx.Done()
@ -166,7 +166,7 @@ func startAPI(ctx context.Context, conf *Config, authZRepo *authz_repo.EsReposit
apis.RegisterServer(ctx, auth.CreateServer(command, query, authRepo))
}
if *oidcEnabled {
op := oidc.NewProvider(ctx, conf.API.OIDC, authRepo, *localDevMode)
op := oidc.NewProvider(ctx, conf.API.OIDC, command, query, authRepo, *localDevMode)
apis.RegisterHandler("/oauth/v2", op.HttpHandler())
}
apis.Start(ctx)

View File

@ -1,70 +0,0 @@
package eventstore
import (
"context"
admin_view "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view"
"github.com/caos/zitadel/internal/config/systemdefaults"
caos_errs "github.com/caos/zitadel/internal/errors"
iam_view "github.com/caos/zitadel/internal/iam/repository/view/model"
"github.com/caos/zitadel/internal/api/authz"
org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing"
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
OrgEvents *org_event.OrgEventstore
View *admin_view.View
SystemDefaults systemdefaults.SystemDefaults
}
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) {
pwPolicy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if caos_errs.IsNotFound(err) {
pwPolicy, err = repo.View.PasswordComplexityPolicyByAggregateID(repo.SystemDefaults.IamID)
}
if err != nil {
return nil, err
}
pwPolicyView := iam_view.PasswordComplexityViewToModel(pwPolicy)
orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if err != nil && caos_errs.IsNotFound(err) {
orgPolicy, err = repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID)
if err != nil {
return nil, err
}
}
orgPolicyView := iam_view.OrgIAMViewToModel(orgPolicy)
return repo.UserEvents.CreateUser(ctx, user, pwPolicyView, orgPolicyView)
}
func (repo *UserRepo) RegisterUser(ctx context.Context, user *usr_model.User, resourceOwner string) (*usr_model.User, error) {
policyResourceOwner := authz.GetCtxData(ctx).OrgID
if resourceOwner != "" {
policyResourceOwner = resourceOwner
}
pwPolicy, err := repo.View.PasswordComplexityPolicyByAggregateID(policyResourceOwner)
if caos_errs.IsNotFound(err) {
pwPolicy, err = repo.View.PasswordComplexityPolicyByAggregateID(repo.SystemDefaults.IamID)
}
if err != nil {
return nil, err
}
pwPolicyView := iam_view.PasswordComplexityViewToModel(pwPolicy)
orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(policyResourceOwner)
if caos_errs.IsNotFound(err) {
orgPolicy, err = repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID)
}
if err != nil {
return nil, err
}
orgPolicyView := iam_view.OrgIAMViewToModel(orgPolicy)
return repo.UserEvents.RegisterUser(ctx, user, pwPolicyView, orgPolicyView, resourceOwner)
}

View File

@ -28,7 +28,6 @@ type EsRepository struct {
eventstore.OrgRepo
eventstore.IAMRepository
eventstore.AdministratorRepo
eventstore.UserRepo
}
func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, roles []string) (*EsRepository, error) {
@ -86,12 +85,6 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, r
AdministratorRepo: eventstore.AdministratorRepo{
View: view,
},
UserRepo: eventstore.UserRepo{
UserEvents: user,
OrgEvents: org,
View: view,
SystemDefaults: systemDefaults,
},
}, nil
}

View File

@ -12,7 +12,7 @@ import (
func loginPolicyToDomain(policy *admin.DefaultLoginPolicyRequest) *domain.LoginPolicy {
return &domain.LoginPolicy{
AllowUsernamePassword: policy.AllowUsernamePassword,
AllowExternalIdp: policy.AllowExternalIdp,
AllowExternalIDP: policy.AllowExternalIdp,
AllowRegister: policy.AllowRegister,
ForceMFA: policy.ForceMfa,
PasswordlessType: passwordlessTypeToDomain(policy.PasswordlessType),
@ -22,7 +22,7 @@ func loginPolicyToDomain(policy *admin.DefaultLoginPolicyRequest) *domain.LoginP
func loginPolicyFromDomain(policy *domain.LoginPolicy) *admin.DefaultLoginPolicy {
return &admin.DefaultLoginPolicy{
AllowUsernamePassword: policy.AllowUsernamePassword,
AllowExternalIdp: policy.AllowExternalIdp,
AllowExternalIdp: policy.AllowExternalIDP,
AllowRegister: policy.AllowRegister,
ForceMfa: policy.ForceMFA,
PasswordlessType: passwordlessTypeFromDomain(policy.PasswordlessType),

View File

@ -162,19 +162,19 @@ func (s *Server) AddMfaOTP(ctx context.Context, _ *empty.Empty) (_ *auth.MfaOtpR
func (s *Server) VerifyMfaOTP(ctx context.Context, request *auth.VerifyMfaOtp) (*empty.Empty, error) {
ctxData := authz.GetCtxData(ctx)
err := s.command.CheckMFAOTPSetup(ctx, ctxData.UserID, request.Code, "", ctxData.ResourceOwner)
err := s.command.HumanCheckMFAOTPSetup(ctx, ctxData.UserID, request.Code, "", ctxData.ResourceOwner)
return &empty.Empty{}, err
}
func (s *Server) RemoveMfaOTP(ctx context.Context, _ *empty.Empty) (_ *empty.Empty, err error) {
ctxData := authz.GetCtxData(ctx)
err = s.command.RemoveHumanOTP(ctx, ctxData.UserID, ctxData.OrgID)
err = s.command.HumanRemoveOTP(ctx, ctxData.UserID, ctxData.OrgID)
return &empty.Empty{}, err
}
func (s *Server) AddMyMfaU2F(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNResponse, err error) {
ctxData := authz.GetCtxData(ctx)
u2f, err := s.command.AddHumanU2F(ctx, ctxData.UserID, ctxData.ResourceOwner, false)
u2f, err := s.command.HumanAddU2FSetup(ctx, ctxData.UserID, ctxData.ResourceOwner, false)
if err != nil {
return nil, err
}
@ -183,13 +183,13 @@ func (s *Server) AddMyMfaU2F(ctx context.Context, _ *empty.Empty) (_ *auth.WebAu
func (s *Server) VerifyMyMfaU2F(ctx context.Context, request *auth.VerifyWebAuthN) (*empty.Empty, error) {
ctxData := authz.GetCtxData(ctx)
err := s.command.VerifyHumanU2F(ctx, ctxData.UserID, ctxData.OrgID, request.TokenName, "", request.PublicKeyCredential)
err := s.command.HumanVerifyU2FSetup(ctx, ctxData.UserID, ctxData.OrgID, request.TokenName, "", request.PublicKeyCredential)
return &empty.Empty{}, err
}
func (s *Server) RemoveMyMfaU2F(ctx context.Context, id *auth.WebAuthNTokenID) (*empty.Empty, error) {
ctxData := authz.GetCtxData(ctx)
err := s.command.RemoveHumanU2F(ctx, ctxData.UserID, id.Id, ctxData.OrgID)
err := s.command.HumanRemoveU2F(ctx, ctxData.UserID, id.Id, ctxData.OrgID)
return &empty.Empty{}, err
}
@ -203,7 +203,7 @@ func (s *Server) GetMyPasswordless(ctx context.Context, _ *empty.Empty) (_ *auth
func (s *Server) AddMyPasswordless(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNResponse, err error) {
ctxData := authz.GetCtxData(ctx)
u2f, err := s.command.AddHumanPasswordless(ctx, ctxData.UserID, ctxData.ResourceOwner, false)
u2f, err := s.command.HumanAddPasswordlessSetup(ctx, ctxData.UserID, ctxData.ResourceOwner, false)
if err != nil {
return nil, err
}
@ -212,13 +212,13 @@ func (s *Server) AddMyPasswordless(ctx context.Context, _ *empty.Empty) (_ *auth
func (s *Server) VerifyMyPasswordless(ctx context.Context, request *auth.VerifyWebAuthN) (*empty.Empty, error) {
ctxData := authz.GetCtxData(ctx)
err := s.command.VerifyHumanPasswordless(ctx, ctxData.UserID, ctxData.OrgID, request.TokenName, "", request.PublicKeyCredential)
err := s.command.HumanHumanPasswordlessSetup(ctx, ctxData.UserID, ctxData.OrgID, request.TokenName, "", request.PublicKeyCredential)
return &empty.Empty{}, err
}
func (s *Server) RemoveMyPasswordless(ctx context.Context, id *auth.WebAuthNTokenID) (*empty.Empty, error) {
ctxData := authz.GetCtxData(ctx)
err := s.command.RemoveHumanPasswordless(ctx, ctxData.UserID, id.Id, ctxData.ResourceOwner)
err := s.command.HumanRemovePasswordless(ctx, ctxData.UserID, id.Id, ctxData.ResourceOwner)
return &empty.Empty{}, err
}

View File

@ -20,7 +20,7 @@ func loginPolicyRequestToDomain(ctx context.Context, policy *management.LoginPol
AggregateID: authz.GetCtxData(ctx).OrgID,
},
AllowUsernamePassword: policy.AllowUsernamePassword,
AllowExternalIdp: policy.AllowExternalIdp,
AllowExternalIDP: policy.AllowExternalIdp,
AllowRegister: policy.AllowRegister,
ForceMFA: policy.ForceMfa,
PasswordlessType: passwordlessTypeToDomain(policy.PasswordlessType),
@ -30,7 +30,7 @@ func loginPolicyRequestToDomain(ctx context.Context, policy *management.LoginPol
func loginPolicyFromDomain(policy *domain.LoginPolicy) *management.LoginPolicy {
return &management.LoginPolicy{
AllowUsernamePassword: policy.AllowUsernamePassword,
AllowExternalIdp: policy.AllowExternalIdp,
AllowExternalIdp: policy.AllowExternalIDP,
AllowRegister: policy.AllowRegister,
ChangeDate: timestamppb.New(policy.ChangeDate),
ForceMfa: policy.ForceMFA,

View File

@ -2,7 +2,6 @@ package management
import (
"context"
"github.com/golang/protobuf/ptypes/empty"
"github.com/caos/zitadel/internal/api/authz"
@ -36,7 +35,11 @@ func (s *Server) ReactivateProject(ctx context.Context, in *management.ProjectID
}
func (s *Server) RemoveProject(ctx context.Context, in *management.ProjectID) (*empty.Empty, error) {
err := s.command.RemoveProject(ctx, in.Id, authz.GetCtxData(ctx).OrgID)
grants, err := s.usergrant.UserGrantsByProjectID(ctx, in.Id)
if err != nil {
return &empty.Empty{}, err
}
err = s.command.RemoveProject(ctx, in.Id, authz.GetCtxData(ctx).OrgID, userGrantsToIDs(grants)...)
return &empty.Empty{}, err
}

View File

@ -217,11 +217,11 @@ func (s *Server) GetUserMfas(ctx context.Context, userID *management.UserID) (*m
}
func (s *Server) RemoveMfaOTP(ctx context.Context, userID *management.UserID) (*empty.Empty, error) {
return &empty.Empty{}, s.command.RemoveHumanOTP(ctx, userID.Id, authz.GetCtxData(ctx).OrgID)
return &empty.Empty{}, s.command.HumanRemoveOTP(ctx, userID.Id, authz.GetCtxData(ctx).OrgID)
}
func (s *Server) RemoveMfaU2F(ctx context.Context, webAuthNTokenID *management.WebAuthNTokenID) (*empty.Empty, error) {
return &empty.Empty{}, s.command.RemoveHumanU2F(ctx, webAuthNTokenID.UserId, webAuthNTokenID.Id, authz.GetCtxData(ctx).OrgID)
return &empty.Empty{}, s.command.HumanRemoveU2F(ctx, webAuthNTokenID.UserId, webAuthNTokenID.Id, authz.GetCtxData(ctx).OrgID)
}
func (s *Server) GetPasswordless(ctx context.Context, userID *management.UserID) (_ *management.WebAuthNTokens, err error) {
@ -233,7 +233,7 @@ func (s *Server) GetPasswordless(ctx context.Context, userID *management.UserID)
}
func (s *Server) RemovePasswordless(ctx context.Context, id *management.WebAuthNTokenID) (*empty.Empty, error) {
return &empty.Empty{}, s.command.RemoveHumanPasswordless(ctx, id.UserId, id.Id, authz.GetCtxData(ctx).OrgID)
return &empty.Empty{}, s.command.HumanRemovePasswordless(ctx, id.UserId, id.Id, authz.GetCtxData(ctx).OrgID)
}
func (s *Server) SearchUserMemberships(ctx context.Context, in *management.UserMembershipSearchRequest) (*management.UserMembershipSearchResponse, error) {

View File

@ -177,3 +177,11 @@ func usergrantStateFromDomain(state domain.UserGrantState) management.UserGrantS
return management.UserGrantState_USERGRANTSTATE_UNSPECIFIED
}
}
func userGrantsToIDs(userGrants []*grant_model.UserGrantView) []string {
converted := make([]string, len(userGrants))
for i, grant := range userGrants {
converted[i] = grant.ID
}
return converted
}

View File

@ -2,21 +2,22 @@ package management
import (
"context"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/pkg/grpc/management"
"github.com/golang/protobuf/ptypes/empty"
)
func (s *Server) AddMachineKey(ctx context.Context, req *management.AddMachineKeyRequest) (*management.AddMachineKeyResponse, error) {
key, err := s.user.AddMachineKey(ctx, addMachineKeyToModel(req))
key, err := s.command.AddUserMachineKey(ctx, addMachineKeyToDomain(req), authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return addMachineKeyFromModel(key), nil
return addMachineKeyFromDomain(key), nil
}
func (s *Server) DeleteMachineKey(ctx context.Context, req *management.MachineKeyIDRequest) (*empty.Empty, error) {
err := s.user.RemoveMachineKey(ctx, req.UserId, req.KeyId)
err := s.command.RemoveUserMachineKey(ctx, req.UserId, req.KeyId, authz.GetCtxData(ctx).OrgID)
return &empty.Empty{}, err
}

View File

@ -2,6 +2,7 @@ package management
import (
"encoding/json"
"google.golang.org/protobuf/types/known/timestamppb"
"time"
"github.com/caos/zitadel/internal/api/authz"
@ -75,7 +76,7 @@ func machineKeyViewFromModel(key *usr_model.MachineKeyView) *management.MachineK
}
}
func addMachineKeyToModel(key *management.AddMachineKeyRequest) *usr_model.MachineKey {
func addMachineKeyToDomain(key *management.AddMachineKeyRequest) *domain.MachineKey {
expirationDate := time.Time{}
if key.ExpirationDate != nil {
var err error
@ -83,20 +84,14 @@ func addMachineKeyToModel(key *management.AddMachineKeyRequest) *usr_model.Machi
logging.Log("MANAG-iNshR").OnError(err).Debug("unable to parse expiration date")
}
return &usr_model.MachineKey{
return &domain.MachineKey{
ExpirationDate: expirationDate,
Type: machineKeyTypeToModel(key.Type),
Type: machineKeyTypeToDomain(key.Type),
ObjectRoot: models.ObjectRoot{AggregateID: key.UserId},
}
}
func addMachineKeyFromModel(key *usr_model.MachineKey) *management.AddMachineKeyResponse {
creationDate, err := ptypes.TimestampProto(key.CreationDate)
logging.Log("MANAG-dlb8m").OnError(err).Debug("unable to parse cretaion date")
expirationDate, err := ptypes.TimestampProto(key.ExpirationDate)
logging.Log("MANAG-dlb8m").OnError(err).Debug("unable to parse cretaion date")
func addMachineKeyFromDomain(key *domain.MachineKey) *management.AddMachineKeyResponse {
detail, err := json.Marshal(struct {
Type string `json:"type"`
KeyID string `json:"keyId"`
@ -112,20 +107,29 @@ func addMachineKeyFromModel(key *usr_model.MachineKey) *management.AddMachineKey
return &management.AddMachineKeyResponse{
Id: key.KeyID,
CreationDate: creationDate,
ExpirationDate: expirationDate,
CreationDate: timestamppb.New(key.CreationDate),
ExpirationDate: timestamppb.New(key.ExpirationDate),
Sequence: key.Sequence,
KeyDetails: detail,
Type: machineKeyTypeFromModel(key.Type),
Type: machineKeyTypeFromDomain(key.Type),
}
}
func machineKeyTypeToModel(typ management.MachineKeyType) usr_model.MachineKeyType {
func machineKeyTypeToDomain(typ management.MachineKeyType) domain.MachineKeyType {
switch typ {
case management.MachineKeyType_MACHINEKEY_JSON:
return usr_model.MachineKeyTypeJSON
return domain.MachineKeyTypeJSON
default:
return usr_model.MachineKeyTypeNONE
return domain.MachineKeyTypeNONE
}
}
func machineKeyTypeFromDomain(typ domain.MachineKeyType) management.MachineKeyType {
switch typ {
case domain.MachineKeyTypeJSON:
return management.MachineKeyType_MACHINEKEY_JSON
default:
return management.MachineKeyType_MACHINEKEY_UNSPECIFIED
}
}

View File

@ -89,7 +89,7 @@ func (o *OPStorage) CreateToken(ctx context.Context, req op.TokenRequest) (_ str
userAgentID = authReq.AgentID
applicationID = authReq.ApplicationID
}
resp, err := o.repo.CreateToken(ctx, userAgentID, applicationID, req.GetSubject(), req.GetAudience(), req.GetScopes(), o.defaultAccessTokenLifetime) //PLANNED: lifetime from client
resp, err := o.command.CreateUserToken(ctx, authReq.UserOrgID, userAgentID, applicationID, req.GetSubject(), req.GetAudience(), req.GetScopes(), o.defaultAccessTokenLifetime) //PLANNED: lifetime from client
if err != nil {
return "", time.Time{}, err
}
@ -113,7 +113,11 @@ func (o *OPStorage) TerminateSession(ctx context.Context, userID, clientID strin
if !ok {
return errors.ThrowPreconditionFailed(nil, "OIDC-fso7F", "no user agent id")
}
return o.repo.SignOut(ctx, userAgentID)
userIDs, err := o.repo.UserSessionUserIDsByAgentID(ctx, userAgentID)
if err != nil {
return err
}
return o.command.HumansSignOut(ctx, userAgentID, userIDs)
}
func (o *OPStorage) GetSigningKey(ctx context.Context, keyCh chan<- jose.SigningKey, errCh chan<- error, timer <-chan time.Time) {

View File

@ -2,6 +2,7 @@ package oidc
import (
"context"
"github.com/caos/zitadel/internal/v2/domain"
"net"
"time"
@ -10,7 +11,6 @@ import (
"golang.org/x/text/language"
http_utils "github.com/caos/zitadel/internal/api/http"
"github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/errors"
)
@ -22,7 +22,7 @@ const (
)
type AuthRequest struct {
*model.AuthRequest
*domain.AuthRequest
}
func (a *AuthRequest) GetID() string {
@ -92,26 +92,26 @@ func (a *AuthRequest) GetSubject() string {
func (a *AuthRequest) Done() bool {
for _, step := range a.PossibleSteps {
if step.Type() == model.NextStepRedirectToCallback {
if step.Type() == domain.NextStepRedirectToCallback {
return true
}
}
return false
}
func (a *AuthRequest) oidc() *model.AuthRequestOIDC {
return a.Request.(*model.AuthRequestOIDC)
func (a *AuthRequest) oidc() *domain.AuthRequestOIDC {
return a.Request.(*domain.AuthRequestOIDC)
}
func AuthRequestFromBusiness(authReq *model.AuthRequest) (_ op.AuthRequest, err error) {
if _, ok := authReq.Request.(*model.AuthRequestOIDC); !ok {
func AuthRequestFromBusiness(authReq *domain.AuthRequest) (_ op.AuthRequest, err error) {
if _, ok := authReq.Request.(*domain.AuthRequestOIDC); !ok {
return nil, errors.ThrowInvalidArgument(nil, "OIDC-Haz7A", "auth request is not of type oidc")
}
return &AuthRequest{authReq}, nil
}
func CreateAuthRequestToBusiness(ctx context.Context, authReq *oidc.AuthRequest, userAgentID, userID string) *model.AuthRequest {
return &model.AuthRequest{
func CreateAuthRequestToBusiness(ctx context.Context, authReq *oidc.AuthRequest, userAgentID, userID string) *domain.AuthRequest {
return &domain.AuthRequest{
AgentID: userAgentID,
BrowserInfo: ParseBrowserInfoFromContext(ctx),
ApplicationID: authReq.ClientID,
@ -123,7 +123,7 @@ func CreateAuthRequestToBusiness(ctx context.Context, authReq *oidc.AuthRequest,
LoginHint: authReq.LoginHint,
MaxAuthAge: authReq.MaxAge,
UserID: userID,
Request: &model.AuthRequestOIDC{
Request: &domain.AuthRequestOIDC{
Scopes: authReq.Scopes,
ResponseType: ResponseTypeToBusiness(authReq.ResponseType),
Nonce: authReq.Nonce,
@ -132,10 +132,10 @@ func CreateAuthRequestToBusiness(ctx context.Context, authReq *oidc.AuthRequest,
}
}
func ParseBrowserInfoFromContext(ctx context.Context) *model.BrowserInfo {
func ParseBrowserInfoFromContext(ctx context.Context) *domain.BrowserInfo {
userAgent, acceptLang := HttpHeadersFromContext(ctx)
ip := IpFromContext(ctx)
return &model.BrowserInfo{RemoteIP: ip, UserAgent: userAgent, AcceptLanguage: acceptLang}
return &domain.BrowserInfo{RemoteIP: ip, UserAgent: userAgent, AcceptLanguage: acceptLang}
}
func HttpHeadersFromContext(ctx context.Context) (userAgent, acceptLang string) {
@ -160,22 +160,22 @@ func IpFromContext(ctx context.Context) net.IP {
return net.ParseIP(ipString)
}
func PromptToBusiness(prompt oidc.Prompt) model.Prompt {
func PromptToBusiness(prompt oidc.Prompt) domain.Prompt {
switch prompt {
case oidc.PromptNone:
return model.PromptNone
return domain.PromptNone
case oidc.PromptLogin:
return model.PromptLogin
return domain.PromptLogin
case oidc.PromptConsent:
return model.PromptConsent
return domain.PromptConsent
case oidc.PromptSelectAccount:
return model.PromptSelectAccount
return domain.PromptSelectAccount
default:
return model.PromptUnspecified
return domain.PromptUnspecified
}
}
func ACRValuesToBusiness(values []string) []model.LevelOfAssurance {
func ACRValuesToBusiness(values []string) []domain.LevelOfAssurance {
return nil
}
@ -190,52 +190,52 @@ func UILocalesToBusiness(tags []language.Tag) []string {
return locales
}
func ResponseTypeToBusiness(responseType oidc.ResponseType) model.OIDCResponseType {
func ResponseTypeToBusiness(responseType oidc.ResponseType) domain.OIDCResponseType {
switch responseType {
case oidc.ResponseTypeCode:
return model.OIDCResponseTypeCode
return domain.OIDCResponseTypeCode
case oidc.ResponseTypeIDTokenOnly:
return model.OIDCResponseTypeIdToken
return domain.OIDCResponseTypeIDToken
case oidc.ResponseTypeIDToken:
return model.OIDCResponseTypeIdTokenToken
return domain.OIDCResponseTypeIDTokenToken
default:
return model.OIDCResponseTypeCode
return domain.OIDCResponseTypeCode
}
}
func ResponseTypeToOIDC(responseType model.OIDCResponseType) oidc.ResponseType {
func ResponseTypeToOIDC(responseType domain.OIDCResponseType) oidc.ResponseType {
switch responseType {
case model.OIDCResponseTypeCode:
case domain.OIDCResponseTypeCode:
return oidc.ResponseTypeCode
case model.OIDCResponseTypeIdTokenToken:
case domain.OIDCResponseTypeIDTokenToken:
return oidc.ResponseTypeIDToken
case model.OIDCResponseTypeIdToken:
case domain.OIDCResponseTypeIDToken:
return oidc.ResponseTypeIDTokenOnly
default:
return oidc.ResponseTypeCode
}
}
func CodeChallengeToBusiness(challenge string, method oidc.CodeChallengeMethod) *model.OIDCCodeChallenge {
func CodeChallengeToBusiness(challenge string, method oidc.CodeChallengeMethod) *domain.OIDCCodeChallenge {
if challenge == "" {
return nil
}
challengeMethod := model.CodeChallengeMethodPlain
challengeMethod := domain.CodeChallengeMethodPlain
if method == oidc.CodeChallengeMethodS256 {
challengeMethod = model.CodeChallengeMethodS256
challengeMethod = domain.CodeChallengeMethodS256
}
return &model.OIDCCodeChallenge{
return &domain.OIDCCodeChallenge{
Challenge: challenge,
Method: challengeMethod,
}
}
func CodeChallengeToOIDC(challenge *model.OIDCCodeChallenge) *oidc.CodeChallenge {
func CodeChallengeToOIDC(challenge *domain.OIDCCodeChallenge) *oidc.CodeChallenge {
if challenge == nil {
return nil
}
challengeMethod := oidc.CodeChallengeMethodPlain
if challenge.Method == model.CodeChallengeMethodS256 {
if challenge.Method == domain.CodeChallengeMethodS256 {
challengeMethod = oidc.CodeChallengeMethodS256
}
return &oidc.CodeChallenge{
@ -244,12 +244,12 @@ func CodeChallengeToOIDC(challenge *model.OIDCCodeChallenge) *oidc.CodeChallenge
}
}
func AMRFromMFAType(mfaType model.MFAType) string {
func AMRFromMFAType(mfaType domain.MFAType) string {
switch mfaType {
case model.MFATypeOTP:
case domain.MFATypeOTP:
return amrOTP
case model.MFATypeU2F,
model.MFATypeU2FUserVerification:
case domain.MFATypeU2F,
domain.MFATypeU2FUserVerification:
return amrUserPresence
default:
return ""

View File

@ -3,6 +3,8 @@ package oidc
import (
"context"
"github.com/caos/zitadel/internal/telemetry/metrics"
"github.com/caos/zitadel/internal/v2/command"
"github.com/caos/zitadel/internal/v2/query"
"time"
"github.com/caos/logging"
@ -46,13 +48,15 @@ type Endpoint struct {
type OPStorage struct {
repo repository.Repository
command *command.CommandSide
query *query.QuerySide
defaultLoginURL string
defaultAccessTokenLifetime time.Duration
defaultIdTokenLifetime time.Duration
signingKeyAlgorithm string
}
func NewProvider(ctx context.Context, config OPHandlerConfig, repo repository.Repository, localDevMode bool) op.OpenIDProvider {
func NewProvider(ctx context.Context, config OPHandlerConfig, command *command.CommandSide, query *query.QuerySide, repo repository.Repository, localDevMode bool) op.OpenIDProvider {
cookieHandler, err := middleware.NewUserAgentHandler(config.UserAgentCookieConfig, id.SonyFlakeGenerator, localDevMode)
logging.Log("OIDC-sd4fd").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Panic("cannot user agent handler")
config.OPConfig.CodeMethodS256 = true
@ -60,7 +64,7 @@ func NewProvider(ctx context.Context, config OPHandlerConfig, repo repository.Re
provider, err := op.NewOpenIDProvider(
ctx,
config.OPConfig,
newStorage(config.StorageConfig, repo),
newStorage(config.StorageConfig, command, query, repo),
op.WithHttpInterceptors(
middleware.MetricsHandler(metricTypes),
middleware.TelemetryHandler(),
@ -79,9 +83,11 @@ func NewProvider(ctx context.Context, config OPHandlerConfig, repo repository.Re
return provider
}
func newStorage(config StorageConfig, repo repository.Repository) *OPStorage {
func newStorage(config StorageConfig, command *command.CommandSide, query *query.QuerySide, repo repository.Repository) *OPStorage {
return &OPStorage{
repo: repo,
command: command,
query: query,
defaultLoginURL: config.DefaultLoginURL,
signingKeyAlgorithm: config.SigningKeyAlgorithm,
defaultAccessTokenLifetime: config.DefaultAccessTokenLifetime.Duration,

View File

@ -2,32 +2,30 @@ package repository
import (
"context"
"github.com/caos/zitadel/internal/auth_request/model"
org_model "github.com/caos/zitadel/internal/org/model"
user_model "github.com/caos/zitadel/internal/user/model"
"github.com/caos/zitadel/internal/v2/domain"
)
type AuthRequestRepository interface {
CreateAuthRequest(ctx context.Context, request *model.AuthRequest) (*model.AuthRequest, error)
AuthRequestByID(ctx context.Context, id, userAgentID string) (*model.AuthRequest, error)
AuthRequestByIDCheckLoggedIn(ctx context.Context, id, userAgentID string) (*model.AuthRequest, error)
AuthRequestByCode(ctx context.Context, code string) (*model.AuthRequest, error)
CreateAuthRequest(ctx context.Context, request *domain.AuthRequest) (*domain.AuthRequest, error)
AuthRequestByID(ctx context.Context, id, userAgentID string) (*domain.AuthRequest, error)
AuthRequestByIDCheckLoggedIn(ctx context.Context, id, userAgentID string) (*domain.AuthRequest, error)
AuthRequestByCode(ctx context.Context, code string) (*domain.AuthRequest, error)
SaveAuthCode(ctx context.Context, id, code, userAgentID string) error
DeleteAuthRequest(ctx context.Context, id string) error
CheckLoginName(ctx context.Context, id, loginName, userAgentID string) error
CheckExternalUserLogin(ctx context.Context, authReqID, userAgentID string, user *model.ExternalUser, info *model.BrowserInfo) error
CheckExternalUserLogin(ctx context.Context, authReqID, userAgentID string, user *domain.ExternalUser, info *domain.BrowserInfo) error
SelectUser(ctx context.Context, id, userID, userAgentID string) error
SelectExternalIDP(ctx context.Context, authReqID, idpConfigID, userAgentID string) error
VerifyPassword(ctx context.Context, id, userID, password, userAgentID string, info *model.BrowserInfo) error
VerifyPassword(ctx context.Context, id, userID, resourceOwner, password, userAgentID string, info *domain.BrowserInfo) error
VerifyMFAOTP(ctx context.Context, agentID, authRequestID, code, userAgentID string, info *model.BrowserInfo) error
BeginMFAU2FLogin(ctx context.Context, userID, authRequestID, userAgentID string) (*user_model.WebAuthNLogin, error)
VerifyMFAU2F(ctx context.Context, userID, authRequestID, userAgentID string, credentialData []byte, info *model.BrowserInfo) error
BeginPasswordlessLogin(ctx context.Context, userID, authRequestID, userAgentID string) (*user_model.WebAuthNLogin, error)
VerifyPasswordless(ctx context.Context, userID, authRequestID, userAgentID string, credentialData []byte, info *model.BrowserInfo) error
VerifyMFAOTP(ctx context.Context, authRequestID, userID, resourceOwner, code, userAgentID string, info *domain.BrowserInfo) error
BeginMFAU2FLogin(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string) (*domain.WebAuthNLogin, error)
VerifyMFAU2F(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string, credentialData []byte, info *domain.BrowserInfo) error
BeginPasswordlessLogin(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string) (*domain.WebAuthNLogin, error)
VerifyPasswordless(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string, credentialData []byte, info *domain.BrowserInfo) error
LinkExternalUsers(ctx context.Context, authReqID, userAgentID string, info *model.BrowserInfo) error
AutoRegisterExternalUser(ctx context.Context, user *user_model.User, externalIDP *user_model.ExternalIDP, member *org_model.OrgMember, authReqID, userAgentID, resourceOwner string, info *model.BrowserInfo) error
LinkExternalUsers(ctx context.Context, authReqID, userAgentID string, info *domain.BrowserInfo) error
AutoRegisterExternalUser(ctx context.Context, user *domain.Human, externalIDP *domain.ExternalIDP, orgMemberRoles []string, authReqID, userAgentID, resourceOwner string, info *domain.BrowserInfo) error
ResetLinkingUsers(ctx context.Context, authReqID, userAgentID string) error
}

View File

@ -2,6 +2,8 @@ package eventstore
import (
"context"
"github.com/caos/zitadel/internal/v2/command"
"github.com/caos/zitadel/internal/v2/domain"
"time"
"github.com/caos/logging"
@ -9,10 +11,10 @@ import (
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
"github.com/caos/zitadel/internal/auth_request/model"
auth_req_model "github.com/caos/zitadel/internal/auth_request/model"
cache "github.com/caos/zitadel/internal/auth_request/repository"
"github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/eventstore/sdk"
iam_model "github.com/caos/zitadel/internal/iam/model"
iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model"
iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model"
@ -30,6 +32,7 @@ import (
)
type AuthRequestRepo struct {
Command *command.CommandSide
UserEvents *user_event.UserEventstore
OrgEvents *org_event.OrgEventstore
AuthRequests cache.AuthRequestCache
@ -37,6 +40,7 @@ type AuthRequestRepo struct {
UserSessionViewProvider userSessionViewProvider
UserViewProvider userViewProvider
UserCommandProvider userCommandProvider
UserEventProvider userEventProvider
OrgViewProvider orgViewProvider
LoginPolicyViewProvider loginPolicyViewProvider
@ -72,7 +76,10 @@ type idpProviderViewProvider interface {
type userEventProvider interface {
UserEventsByID(ctx context.Context, id string, sequence uint64) ([]*es_models.Event, error)
BulkAddExternalIDPs(ctx context.Context, userID string, externalIDPs []*user_model.ExternalIDP) error
}
type userCommandProvider interface {
BulkAddedHumanExternalIDP(ctx context.Context, userID, resourceOwner string, externalIDPs []*domain.ExternalIDP) error
}
type orgViewProvider interface {
@ -92,7 +99,7 @@ func (repo *AuthRequestRepo) Health(ctx context.Context) error {
return repo.AuthRequests.Health(ctx)
}
func (repo *AuthRequestRepo) CreateAuthRequest(ctx context.Context, request *model.AuthRequest) (_ *model.AuthRequest, err error) {
func (repo *AuthRequestRepo) CreateAuthRequest(ctx context.Context, request *domain.AuthRequest) (_ *domain.AuthRequest, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
reqID, err := repo.IdGenerator.Next()
@ -124,13 +131,13 @@ func (repo *AuthRequestRepo) CreateAuthRequest(ctx context.Context, request *mod
return request, nil
}
func (repo *AuthRequestRepo) AuthRequestByID(ctx context.Context, id, userAgentID string) (_ *model.AuthRequest, err error) {
func (repo *AuthRequestRepo) AuthRequestByID(ctx context.Context, id, userAgentID string) (_ *domain.AuthRequest, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
return repo.getAuthRequestNextSteps(ctx, id, userAgentID, false)
}
func (repo *AuthRequestRepo) AuthRequestByIDCheckLoggedIn(ctx context.Context, id, userAgentID string) (_ *model.AuthRequest, err error) {
func (repo *AuthRequestRepo) AuthRequestByIDCheckLoggedIn(ctx context.Context, id, userAgentID string) (_ *domain.AuthRequest, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
return repo.getAuthRequestNextSteps(ctx, id, userAgentID, true)
@ -147,7 +154,7 @@ func (repo *AuthRequestRepo) SaveAuthCode(ctx context.Context, id, code, userAge
return repo.AuthRequests.UpdateAuthRequest(ctx, request)
}
func (repo *AuthRequestRepo) AuthRequestByCode(ctx context.Context, code string) (_ *model.AuthRequest, err error) {
func (repo *AuthRequestRepo) AuthRequestByCode(ctx context.Context, code string) (_ *domain.AuthRequest, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
request, err := repo.AuthRequests.GetAuthRequestByCode(ctx, code)
@ -200,7 +207,7 @@ func (repo *AuthRequestRepo) SelectExternalIDP(ctx context.Context, authReqID, i
return repo.AuthRequests.UpdateAuthRequest(ctx, request)
}
func (repo *AuthRequestRepo) CheckExternalUserLogin(ctx context.Context, authReqID, userAgentID string, externalUser *model.ExternalUser, info *model.BrowserInfo) (err error) {
func (repo *AuthRequestRepo) CheckExternalUserLogin(ctx context.Context, authReqID, userAgentID string, externalUser *domain.ExternalUser, info *domain.BrowserInfo) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
request, err := repo.getAuthRequest(ctx, authReqID, userAgentID)
@ -218,14 +225,14 @@ func (repo *AuthRequestRepo) CheckExternalUserLogin(ctx context.Context, authReq
return err
}
err = repo.UserEvents.ExternalLoginChecked(ctx, request.UserID, request.WithCurrentInfo(info))
err = repo.Command.HumanExternalLoginChecked(ctx, request.UserOrgID, request.UserID, request.WithCurrentInfo(info))
if err != nil {
return err
}
return repo.AuthRequests.UpdateAuthRequest(ctx, request)
}
func (repo *AuthRequestRepo) setLinkingUser(ctx context.Context, request *model.AuthRequest, externalUser *model.ExternalUser) error {
func (repo *AuthRequestRepo) setLinkingUser(ctx context.Context, request *domain.AuthRequest, externalUser *domain.ExternalUser) error {
request.LinkingUsers = append(request.LinkingUsers, externalUser)
return repo.AuthRequests.UpdateAuthRequest(ctx, request)
}
@ -248,27 +255,27 @@ func (repo *AuthRequestRepo) SelectUser(ctx context.Context, id, userID, userAge
return repo.AuthRequests.UpdateAuthRequest(ctx, request)
}
func (repo *AuthRequestRepo) VerifyPassword(ctx context.Context, id, userID, password, userAgentID string, info *model.BrowserInfo) (err error) {
func (repo *AuthRequestRepo) VerifyPassword(ctx context.Context, id, userID, resourceOwner, password, userAgentID string, info *domain.BrowserInfo) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
request, err := repo.getAuthRequestEnsureUser(ctx, id, userAgentID, userID)
if err != nil {
return err
}
return repo.UserEvents.CheckPassword(ctx, userID, password, request.WithCurrentInfo(info))
return repo.Command.HumanCheckPassword(ctx, resourceOwner, userID, password, request.WithCurrentInfo(info))
}
func (repo *AuthRequestRepo) VerifyMFAOTP(ctx context.Context, authRequestID, userID, code, userAgentID string, info *model.BrowserInfo) (err error) {
func (repo *AuthRequestRepo) VerifyMFAOTP(ctx context.Context, authRequestID, userID, resourceOwner, code, userAgentID string, info *domain.BrowserInfo) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
request, err := repo.getAuthRequestEnsureUser(ctx, authRequestID, userAgentID, userID)
if err != nil {
return err
}
return repo.UserEvents.CheckMFAOTP(ctx, userID, code, request.WithCurrentInfo(info))
return repo.Command.HumanCheckMFAOTP(ctx, userID, code, resourceOwner, request.WithCurrentInfo(info))
}
func (repo *AuthRequestRepo) BeginMFAU2FLogin(ctx context.Context, userID, authRequestID, userAgentID string) (login *user_model.WebAuthNLogin, err error) {
func (repo *AuthRequestRepo) BeginMFAU2FLogin(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string) (login *domain.WebAuthNLogin, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
@ -276,51 +283,51 @@ func (repo *AuthRequestRepo) BeginMFAU2FLogin(ctx context.Context, userID, authR
if err != nil {
return nil, err
}
return repo.UserEvents.BeginU2FLogin(ctx, userID, request, true)
return repo.Command.HumanBeginU2FLogin(ctx, userID, resourceOwner, request, true)
}
func (repo *AuthRequestRepo) VerifyMFAU2F(ctx context.Context, userID, authRequestID, userAgentID string, credentialData []byte, info *model.BrowserInfo) (err error) {
func (repo *AuthRequestRepo) VerifyMFAU2F(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string, credentialData []byte, info *domain.BrowserInfo) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
request, err := repo.getAuthRequestEnsureUser(ctx, authRequestID, userAgentID, userID)
if err != nil {
return err
}
return repo.UserEvents.VerifyMFAU2F(ctx, userID, credentialData, request, true)
return repo.Command.HumanFinishU2FLogin(ctx, userID, resourceOwner, credentialData, request, true)
}
func (repo *AuthRequestRepo) BeginPasswordlessLogin(ctx context.Context, userID, authRequestID, userAgentID string) (login *user_model.WebAuthNLogin, err error) {
func (repo *AuthRequestRepo) BeginPasswordlessLogin(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string) (login *domain.WebAuthNLogin, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
request, err := repo.getAuthRequestEnsureUser(ctx, authRequestID, userAgentID, userID)
if err != nil {
return nil, err
}
return repo.UserEvents.BeginPasswordlessLogin(ctx, userID, request, true)
return repo.Command.HumanBeginPasswordlessLogin(ctx, userID, resourceOwner, request, true)
}
func (repo *AuthRequestRepo) VerifyPasswordless(ctx context.Context, userID, authRequestID, userAgentID string, credentialData []byte, info *model.BrowserInfo) (err error) {
func (repo *AuthRequestRepo) VerifyPasswordless(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string, credentialData []byte, info *domain.BrowserInfo) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
request, err := repo.getAuthRequestEnsureUser(ctx, authRequestID, userAgentID, userID)
if err != nil {
return err
}
return repo.UserEvents.VerifyPasswordless(ctx, userID, credentialData, request, true)
return repo.Command.HumanFinishPasswordlessLogin(ctx, userID, resourceOwner, credentialData, request, true)
}
func (repo *AuthRequestRepo) LinkExternalUsers(ctx context.Context, authReqID, userAgentID string, info *model.BrowserInfo) (err error) {
func (repo *AuthRequestRepo) LinkExternalUsers(ctx context.Context, authReqID, userAgentID string, info *domain.BrowserInfo) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
request, err := repo.getAuthRequest(ctx, authReqID, userAgentID)
if err != nil {
return err
}
err = linkExternalIDPs(ctx, repo.UserEventProvider, request)
err = linkExternalIDPs(ctx, repo.UserCommandProvider, request)
if err != nil {
return err
}
err = repo.UserEvents.ExternalLoginChecked(ctx, request.UserID, request.WithCurrentInfo(info))
err = repo.Command.HumanExternalLoginChecked(ctx, request.UserOrgID, request.UserID, request.WithCurrentInfo(info))
if err != nil {
return err
}
@ -338,62 +345,29 @@ func (repo *AuthRequestRepo) ResetLinkingUsers(ctx context.Context, authReqID, u
return repo.AuthRequests.UpdateAuthRequest(ctx, request)
}
func (repo *AuthRequestRepo) AutoRegisterExternalUser(ctx context.Context, registerUser *user_model.User, externalIDP *user_model.ExternalIDP, orgMember *org_model.OrgMember, authReqID, userAgentID, resourceOwner string, info *model.BrowserInfo) (err error) {
func (repo *AuthRequestRepo) AutoRegisterExternalUser(ctx context.Context, registerUser *domain.Human, externalIDP *domain.ExternalIDP, orgMemberRoles []string, authReqID, userAgentID, resourceOwner string, info *domain.BrowserInfo) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
request, err := repo.getAuthRequest(ctx, authReqID, userAgentID)
if err != nil {
return err
}
policyResourceOwner := authz.GetCtxData(ctx).OrgID
if resourceOwner != "" {
policyResourceOwner = resourceOwner
}
pwPolicy, err := repo.View.PasswordComplexityPolicyByAggregateID(policyResourceOwner)
if errors.IsNotFound(err) {
pwPolicy, err = repo.View.PasswordComplexityPolicyByAggregateID(repo.IAMID)
}
human, err := repo.Command.RegisterHuman(ctx, resourceOwner, registerUser, externalIDP, orgMemberRoles)
if err != nil {
return err
}
pwPolicyView := iam_es_model.PasswordComplexityViewToModel(pwPolicy)
orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(policyResourceOwner)
if errors.IsNotFound(err) {
orgPolicy, err = repo.View.OrgIAMPolicyByAggregateID(repo.IAMID)
}
if err != nil {
return err
}
orgPolicyView := iam_es_model.OrgIAMViewToModel(orgPolicy)
user, aggregates, err := repo.UserEvents.PrepareRegisterUser(ctx, registerUser, externalIDP, pwPolicyView, orgPolicyView, resourceOwner)
if err != nil {
return err
}
if orgMember != nil {
orgMember.UserID = user.AggregateID
_, memberAggregate, err := repo.OrgEvents.PrepareAddOrgMember(ctx, orgMember, policyResourceOwner)
if err != nil {
return err
}
aggregates = append(aggregates, memberAggregate)
}
err = sdk.PushAggregates(ctx, repo.UserEvents.PushAggregates, user.AppendEvents, aggregates...)
if err != nil {
return err
}
request.UserID = user.AggregateID
request.UserOrgID = user.ResourceOwner
request.UserID = human.AggregateID
request.UserOrgID = human.ResourceOwner
request.SelectedIDPConfigID = externalIDP.IDPConfigID
request.LinkingUsers = nil
err = repo.UserEvents.ExternalLoginChecked(ctx, request.UserID, request.WithCurrentInfo(info))
err = repo.Command.HumanExternalLoginChecked(ctx, request.UserOrgID, request.UserID, request.WithCurrentInfo(info))
if err != nil {
return err
}
return repo.AuthRequests.UpdateAuthRequest(ctx, request)
}
func (repo *AuthRequestRepo) getAuthRequestNextSteps(ctx context.Context, id, userAgentID string, checkLoggedIn bool) (*model.AuthRequest, error) {
func (repo *AuthRequestRepo) getAuthRequestNextSteps(ctx context.Context, id, userAgentID string, checkLoggedIn bool) (*domain.AuthRequest, error) {
request, err := repo.getAuthRequest(ctx, id, userAgentID)
if err != nil {
return nil, err
@ -406,7 +380,7 @@ func (repo *AuthRequestRepo) getAuthRequestNextSteps(ctx context.Context, id, us
return request, nil
}
func (repo *AuthRequestRepo) getAuthRequestEnsureUser(ctx context.Context, authRequestID, userAgentID, userID string) (*model.AuthRequest, error) {
func (repo *AuthRequestRepo) getAuthRequestEnsureUser(ctx context.Context, authRequestID, userAgentID, userID string) (*domain.AuthRequest, error) {
request, err := repo.getAuthRequest(ctx, authRequestID, userAgentID)
if err != nil {
return nil, err
@ -417,7 +391,7 @@ func (repo *AuthRequestRepo) getAuthRequestEnsureUser(ctx context.Context, authR
return request, nil
}
func (repo *AuthRequestRepo) getAuthRequest(ctx context.Context, id, userAgentID string) (*model.AuthRequest, error) {
func (repo *AuthRequestRepo) getAuthRequest(ctx context.Context, id, userAgentID string) (*domain.AuthRequest, error) {
request, err := repo.AuthRequests.GetAuthRequestByID(ctx, id)
if err != nil {
return nil, err
@ -432,22 +406,24 @@ func (repo *AuthRequestRepo) getAuthRequest(ctx context.Context, id, userAgentID
return request, nil
}
func (repo *AuthRequestRepo) getLoginPolicyAndIDPProviders(ctx context.Context, orgID string) (*iam_model.LoginPolicyView, []*iam_model.IDPProviderView, error) {
func (repo *AuthRequestRepo) getLoginPolicyAndIDPProviders(ctx context.Context, orgID string) (*domain.LoginPolicy, []*domain.IDPProvider, error) {
policy, err := repo.getLoginPolicy(ctx, orgID)
if err != nil {
return nil, nil, err
}
if !policy.AllowExternalIDP {
return policy, nil, nil
return policy.ToLoginPolicyDomain(), nil, nil
}
idpProviders, err := getLoginPolicyIDPProviders(repo.IDPProviderViewProvider, repo.IAMID, orgID, policy.Default)
if err != nil {
return nil, nil, err
}
return policy, idpProviders, nil
providers := iam_model.IdpProviderViewsToDomain(idpProviders)
return policy.ToLoginPolicyDomain(), providers, nil
}
func (repo *AuthRequestRepo) fillLoginPolicy(ctx context.Context, request *model.AuthRequest) error {
func (repo *AuthRequestRepo) fillLoginPolicy(ctx context.Context, request *domain.AuthRequest) error {
orgID := request.RequestedOrgID
if orgID == "" {
orgID = request.UserOrgID
@ -467,7 +443,7 @@ func (repo *AuthRequestRepo) fillLoginPolicy(ctx context.Context, request *model
return nil
}
func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *model.AuthRequest, loginName string) (err error) {
func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *domain.AuthRequest, loginName string) (err error) {
user := new(user_view_model.UserView)
if request.RequestedOrgID != "" {
user, err = repo.View.UserByLoginNameAndResourceOwner(loginName, request.RequestedOrgID)
@ -488,7 +464,7 @@ func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *model.
return nil
}
func (repo AuthRequestRepo) checkLoginPolicyWithResourceOwner(ctx context.Context, request *model.AuthRequest, user *user_view_model.UserView) error {
func (repo AuthRequestRepo) checkLoginPolicyWithResourceOwner(ctx context.Context, request *domain.AuthRequest, user *user_view_model.UserView) error {
loginPolicy, idpProviders, err := repo.getLoginPolicyAndIDPProviders(ctx, user.ResourceOwner)
if err != nil {
return err
@ -507,7 +483,7 @@ func (repo AuthRequestRepo) checkLoginPolicyWithResourceOwner(ctx context.Contex
return nil
}
func (repo *AuthRequestRepo) checkSelectedExternalIDP(request *model.AuthRequest, idpConfigID string) error {
func (repo *AuthRequestRepo) checkSelectedExternalIDP(request *domain.AuthRequest, idpConfigID string) error {
for _, externalIDP := range request.AllowedExternalIDPs {
if externalIDP.IDPConfigID == idpConfigID {
request.SelectedIDPConfigID = idpConfigID
@ -517,7 +493,7 @@ func (repo *AuthRequestRepo) checkSelectedExternalIDP(request *model.AuthRequest
return errors.ThrowNotFound(nil, "LOGIN-Nsm8r", "Errors.User.ExternalIDP.NotAllowed")
}
func (repo *AuthRequestRepo) checkExternalUserLogin(request *model.AuthRequest, idpConfigID, externalUserID string) (err error) {
func (repo *AuthRequestRepo) checkExternalUserLogin(request *domain.AuthRequest, idpConfigID, externalUserID string) (err error) {
externalIDP := new(user_view_model.ExternalIDPView)
if request.RequestedOrgID != "" {
externalIDP, err = repo.View.ExternalIDPByExternalUserIDAndIDPConfigIDAndResourceOwner(externalUserID, idpConfigID, request.RequestedOrgID)
@ -531,27 +507,27 @@ func (repo *AuthRequestRepo) checkExternalUserLogin(request *model.AuthRequest,
return nil
}
func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *model.AuthRequest, checkLoggedIn bool) ([]model.NextStep, error) {
func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.AuthRequest, checkLoggedIn bool) ([]domain.NextStep, error) {
if request == nil {
return nil, errors.ThrowInvalidArgument(nil, "EVENT-ds27a", "Errors.Internal")
}
steps := make([]model.NextStep, 0)
if !checkLoggedIn && request.Prompt == model.PromptNone {
return append(steps, &model.RedirectToCallbackStep{}), nil
steps := make([]domain.NextStep, 0)
if !checkLoggedIn && request.Prompt == domain.PromptNone {
return append(steps, &domain.RedirectToCallbackStep{}), nil
}
if request.UserID == "" {
if request.LinkingUsers != nil && len(request.LinkingUsers) > 0 {
steps = append(steps, new(model.ExternalNotFoundOptionStep))
steps = append(steps, new(domain.ExternalNotFoundOptionStep))
return steps, nil
}
steps = append(steps, new(model.LoginStep))
if request.Prompt == model.PromptSelectAccount || request.Prompt == model.PromptUnspecified {
steps = append(steps, new(domain.LoginStep))
if request.Prompt == domain.PromptSelectAccount || request.Prompt == domain.PromptUnspecified {
users, err := repo.usersForUserSelection(request)
if err != nil {
return nil, err
}
if len(users) > 0 || request.Prompt == model.PromptSelectAccount {
steps = append(steps, &model.SelectUserStep{Users: users})
if len(users) > 0 || request.Prompt == domain.PromptSelectAccount {
steps = append(steps, &domain.SelectUserStep{Users: users})
}
}
return steps, nil
@ -572,7 +548,7 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *model.AuthR
if selectedIDPConfigID == "" {
selectedIDPConfigID = userSession.SelectedIDPConfigID
}
return append(steps, &model.ExternalLoginStep{SelectedIDPConfigID: selectedIDPConfigID}), nil
return append(steps, &domain.ExternalLoginStep{SelectedIDPConfigID: selectedIDPConfigID}), nil
}
if isInternalLogin || (!isInternalLogin && len(request.LinkingUsers) > 0) {
step := repo.firstFactorChecked(request, user, userSession)
@ -590,13 +566,13 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *model.AuthR
}
if user.PasswordChangeRequired {
steps = append(steps, &model.ChangePasswordStep{})
steps = append(steps, &domain.ChangePasswordStep{})
}
if !user.IsEmailVerified {
steps = append(steps, &model.VerifyEMailStep{})
steps = append(steps, &domain.VerifyEMailStep{})
}
if user.UsernameChangeRequired {
steps = append(steps, &model.ChangeUsernameStep{})
steps = append(steps, &domain.ChangeUsernameStep{})
}
if user.PasswordChangeRequired || !user.IsEmailVerified || user.UsernameChangeRequired {
@ -604,7 +580,7 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *model.AuthR
}
if request.LinkingUsers != nil && len(request.LinkingUsers) != 0 {
return append(steps, &model.LinkUsersStep{}), nil
return append(steps, &domain.LinkUsersStep{}), nil
}
//PLANNED: consent step
@ -614,46 +590,46 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *model.AuthR
return nil, err
}
if missing {
return append(steps, &model.GrantRequiredStep{}), nil
return append(steps, &domain.GrantRequiredStep{}), nil
}
return append(steps, &model.RedirectToCallbackStep{}), nil
return append(steps, &domain.RedirectToCallbackStep{}), nil
}
func (repo *AuthRequestRepo) usersForUserSelection(request *model.AuthRequest) ([]model.UserSelection, error) {
func (repo *AuthRequestRepo) usersForUserSelection(request *domain.AuthRequest) ([]domain.UserSelection, error) {
userSessions, err := userSessionsByUserAgentID(repo.UserSessionViewProvider, request.AgentID)
if err != nil {
return nil, err
}
users := make([]model.UserSelection, len(userSessions))
users := make([]domain.UserSelection, len(userSessions))
for i, session := range userSessions {
users[i] = model.UserSelection{
users[i] = domain.UserSelection{
UserID: session.UserID,
DisplayName: session.DisplayName,
LoginName: session.LoginName,
UserSessionState: session.State,
UserSessionState: auth_req_model.UserSessionStateToDomain(session.State),
SelectionPossible: request.RequestedOrgID == "" || request.RequestedOrgID == session.ResourceOwner,
}
}
return users, nil
}
func (repo *AuthRequestRepo) firstFactorChecked(request *model.AuthRequest, user *user_model.UserView, userSession *user_model.UserSessionView) model.NextStep {
func (repo *AuthRequestRepo) firstFactorChecked(request *domain.AuthRequest, user *user_model.UserView, userSession *user_model.UserSessionView) domain.NextStep {
if user.InitRequired {
return &model.InitUserStep{PasswordSet: user.PasswordSet}
return &domain.InitUserStep{PasswordSet: user.PasswordSet}
}
var step model.NextStep
if request.LoginPolicy.PasswordlessType != iam_model.PasswordlessTypeNotAllowed && user.IsPasswordlessReady() {
var step domain.NextStep
if request.LoginPolicy.PasswordlessType != domain.PasswordlessTypeNotAllowed && user.IsPasswordlessReady() {
if checkVerificationTime(userSession.PasswordlessVerification, repo.MultiFactorCheckLifeTime) {
request.AuthTime = userSession.PasswordlessVerification
return nil
}
step = &model.PasswordlessStep{}
step = &domain.PasswordlessStep{}
}
if !user.PasswordSet {
return &model.InitPasswordStep{}
return &domain.InitPasswordStep{}
}
if checkVerificationTime(userSession.PasswordVerification, repo.PasswordCheckLifeTime) {
@ -664,13 +640,13 @@ func (repo *AuthRequestRepo) firstFactorChecked(request *model.AuthRequest, user
if step != nil {
return step
}
return &model.PasswordStep{}
return &domain.PasswordStep{}
}
func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView, request *model.AuthRequest, user *user_model.UserView) (model.NextStep, bool, error) {
func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView, request *domain.AuthRequest, user *user_model.UserView) (domain.NextStep, bool, error) {
mfaLevel := request.MFALevel()
allowedProviders, required := user.MFATypesAllowed(mfaLevel, request.LoginPolicy)
promptRequired := (user.MFAMaxSetUp < mfaLevel) || (len(allowedProviders) == 0 && required)
promptRequired := (auth_req_model.MFALevelToDomain(user.MFAMaxSetUp) < mfaLevel) || (len(allowedProviders) == 0 && required)
if promptRequired || !repo.mfaSkippedOrSetUp(user) {
types := user.MFATypesSetupPossible(mfaLevel, request.LoginPolicy)
if promptRequired && len(types) == 0 {
@ -679,7 +655,7 @@ func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView,
if len(types) == 0 {
return nil, true, nil
}
return &model.MFAPromptStep{
return &domain.MFAPromptStep{
Required: promptRequired,
MFAProviders: types,
}, false, nil
@ -687,26 +663,26 @@ func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView,
switch mfaLevel {
default:
fallthrough
case model.MFALevelNotSetUp:
case domain.MFALevelNotSetUp:
if len(allowedProviders) == 0 {
return nil, true, nil
}
fallthrough
case model.MFALevelSecondFactor:
case domain.MFALevelSecondFactor:
if checkVerificationTime(userSession.SecondFactorVerification, repo.SecondFactorCheckLifeTime) {
request.MFAsVerified = append(request.MFAsVerified, userSession.SecondFactorVerificationType)
request.MFAsVerified = append(request.MFAsVerified, auth_req_model.MFATypeToDomain(userSession.SecondFactorVerificationType))
request.AuthTime = userSession.SecondFactorVerification
return nil, true, nil
}
fallthrough
case model.MFALevelMultiFactor:
case domain.MFALevelMultiFactor:
if checkVerificationTime(userSession.MultiFactorVerification, repo.MultiFactorCheckLifeTime) {
request.MFAsVerified = append(request.MFAsVerified, userSession.MultiFactorVerificationType)
request.MFAsVerified = append(request.MFAsVerified, auth_req_model.MFATypeToDomain(userSession.MultiFactorVerificationType))
request.AuthTime = userSession.MultiFactorVerification
return nil, true, nil
}
}
return &model.MFAVerificationStep{
return &domain.MFAVerificationStep{
MFAProviders: allowedProviders,
}, false, nil
}
@ -733,7 +709,7 @@ func (repo *AuthRequestRepo) getLoginPolicy(ctx context.Context, orgID string) (
return iam_es_model.LoginPolicyViewToModel(policy), err
}
func setOrgID(orgViewProvider orgViewProvider, request *model.AuthRequest) error {
func setOrgID(orgViewProvider orgViewProvider, request *domain.AuthRequest) error {
primaryDomain := request.GetScopeOrgPrimaryDomain()
if primaryDomain == "" {
return nil
@ -881,14 +857,14 @@ func userByID(ctx context.Context, viewProvider userViewProvider, eventProvider
return user_view_model.UserToModel(&userCopy), nil
}
func linkExternalIDPs(ctx context.Context, userEventProvider userEventProvider, request *model.AuthRequest) error {
externalIDPs := make([]*user_model.ExternalIDP, len(request.LinkingUsers))
func linkExternalIDPs(ctx context.Context, userCommandProvider userCommandProvider, request *domain.AuthRequest) error {
externalIDPs := make([]*domain.ExternalIDP, len(request.LinkingUsers))
for i, linkingUser := range request.LinkingUsers {
externalIDP := &user_model.ExternalIDP{
ObjectRoot: es_models.ObjectRoot{AggregateID: request.UserID},
IDPConfigID: linkingUser.IDPConfigID,
UserID: linkingUser.ExternalUserID,
DisplayName: linkingUser.DisplayName,
externalIDP := &domain.ExternalIDP{
ObjectRoot: es_models.ObjectRoot{AggregateID: request.UserID},
IDPConfigID: linkingUser.IDPConfigID,
ExternalUserID: linkingUser.ExternalUserID,
DisplayName: linkingUser.DisplayName,
}
externalIDPs[i] = externalIDP
}
@ -896,10 +872,10 @@ func linkExternalIDPs(ctx context.Context, userEventProvider userEventProvider,
UserID: "LOGIN",
OrgID: request.UserOrgID,
}
return userEventProvider.BulkAddExternalIDPs(authz.SetCtxData(ctx, data), request.UserID, externalIDPs)
return userCommandProvider.BulkAddedHumanExternalIDP(authz.SetCtxData(ctx, data), request.UserID, request.UserOrgID, externalIDPs)
}
func linkingIDPConfigExistingInAllowedIDPs(linkingUsers []*model.ExternalUser, idpProviders []*iam_model.IDPProviderView) bool {
func linkingIDPConfigExistingInAllowedIDPs(linkingUsers []*domain.ExternalUser, idpProviders []*domain.IDPProvider) bool {
for _, linkingUser := range linkingUsers {
exists := false
for _, idp := range idpProviders {
@ -914,10 +890,10 @@ func linkingIDPConfigExistingInAllowedIDPs(linkingUsers []*model.ExternalUser, i
}
return true
}
func userGrantRequired(ctx context.Context, request *model.AuthRequest, user *user_model.UserView, userGrantProvider userGrantProvider) (_ bool, err error) {
func userGrantRequired(ctx context.Context, request *domain.AuthRequest, user *user_model.UserView, userGrantProvider userGrantProvider) (_ bool, err error) {
var app *project_view_model.ApplicationView
switch request.Request.Type() {
case model.AuthRequestTypeOIDC:
case domain.AuthRequestTypeOIDC:
app, err = userGrantProvider.ApplicationByClientID(ctx, request.ApplicationID)
if err != nil {
return false, err

View File

@ -2,12 +2,8 @@ package eventstore
import (
"context"
"strings"
"github.com/caos/logging"
auth_req_model "github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/telemetry/tracing"
usr_model "github.com/caos/zitadel/internal/user/model"
user_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
@ -22,34 +18,6 @@ type TokenRepo struct {
View *view.View
}
func (repo *TokenRepo) CreateToken(ctx context.Context, agentID, clientID, userID string, audience, scopes []string, lifetime time.Duration) (*usr_model.Token, error) {
preferredLanguage := ""
user, _ := repo.View.UserByID(userID)
if user != nil {
preferredLanguage = user.PreferredLanguage
}
for _, scope := range scopes {
if strings.HasPrefix(scope, auth_req_model.ProjectIDScope) && strings.HasSuffix(scope, auth_req_model.AudSuffix) {
audience = append(audience, strings.TrimSuffix(strings.TrimPrefix(scope, auth_req_model.ProjectIDScope), auth_req_model.AudSuffix))
}
}
now := time.Now().UTC()
token := &usr_model.Token{
ObjectRoot: models.ObjectRoot{
AggregateID: userID,
},
UserAgentID: agentID,
ApplicationID: clientID,
Audience: audience,
Scopes: scopes,
Expiration: now.Add(lifetime),
PreferredLanguage: preferredLanguage,
}
return repo.UserEvents.TokenAdded(ctx, token)
}
func (repo *TokenRepo) IsTokenValid(ctx context.Context, userID, tokenID string) (bool, error) {
token, err := repo.TokenByID(ctx, userID, tokenID)
if err == nil {

View File

@ -36,14 +36,6 @@ func (repo *UserRepo) Health(ctx context.Context) error {
return repo.UserEvents.Health(ctx)
}
func (repo *UserRepo) Register(ctx context.Context, user *model.User, orgMember *org_model.OrgMember, resourceOwner string) (*model.User, error) {
return repo.registerUser(ctx, user, nil, orgMember, resourceOwner)
}
func (repo *UserRepo) RegisterExternalUser(ctx context.Context, user *model.User, externalIDP *model.ExternalIDP, orgMember *org_model.OrgMember, resourceOwner string) (*model.User, error) {
return repo.registerUser(ctx, user, externalIDP, orgMember, resourceOwner)
}
func (repo *UserRepo) registerUser(ctx context.Context, registerUser *model.User, externalIDP *model.ExternalIDP, orgMember *org_model.OrgMember, resourceOwner string) (*model.User, error) {
policyResourceOwner := authz.GetCtxData(ctx).OrgID
if resourceOwner != "" {
@ -133,18 +125,6 @@ func (repo *UserRepo) MyEmail(ctx context.Context) (*model.Email, error) {
return user.GetEmail()
}
func (repo *UserRepo) VerifyEmail(ctx context.Context, userID, code string) error {
return repo.UserEvents.VerifyEmail(ctx, userID, code)
}
func (repo *UserRepo) VerifyMyEmail(ctx context.Context, code string) error {
return repo.UserEvents.VerifyEmail(ctx, authz.GetCtxData(ctx).UserID, code)
}
func (repo *UserRepo) ResendEmailVerificationMail(ctx context.Context, userID string) error {
return repo.UserEvents.CreateEmailVerificationCode(ctx, userID)
}
func (repo *UserRepo) MyPhone(ctx context.Context) (*model.Phone, error) {
user, err := repo.UserByID(ctx, authz.GetCtxData(ctx).UserID)
if err != nil {
@ -156,21 +136,6 @@ func (repo *UserRepo) MyPhone(ctx context.Context) (*model.Phone, error) {
return user.GetPhone()
}
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) RemoveMyPhone(ctx context.Context) error {
return repo.UserEvents.RemovePhone(ctx, authz.GetCtxData(ctx).UserID)
}
func (repo *UserRepo) VerifyMyPhone(ctx context.Context, code string) error {
return repo.UserEvents.VerifyPhone(ctx, authz.GetCtxData(ctx).UserID, code)
}
func (repo *UserRepo) MyAddress(ctx context.Context) (*model.Address, error) {
user, err := repo.UserByID(ctx, authz.GetCtxData(ctx).UserID)
if err != nil {
@ -182,34 +147,6 @@ func (repo *UserRepo) MyAddress(ctx context.Context) (*model.Address, error) {
return user.GetAddress()
}
func (repo *UserRepo) ChangeMyPassword(ctx context.Context, old, new string) error {
policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if errors.IsNotFound(err) {
policy, err = repo.View.PasswordComplexityPolicyByAggregateID(repo.SystemDefaults.IamID)
}
if err != nil {
return err
}
pwPolicyView := iam_es_model.PasswordComplexityViewToModel(policy)
_, err = repo.UserEvents.ChangePassword(ctx, pwPolicyView, authz.GetCtxData(ctx).UserID, old, new, "")
return err
}
func (repo *UserRepo) ChangePassword(ctx context.Context, userID, old, new, userAgentID string) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if errors.IsNotFound(err) {
policy, err = repo.View.PasswordComplexityPolicyByAggregateID(repo.SystemDefaults.IamID)
}
if err != nil {
return err
}
pwPolicyView := iam_es_model.PasswordComplexityViewToModel(policy)
_, err = repo.UserEvents.ChangePassword(ctx, pwPolicyView, userID, old, new, userAgentID)
return err
}
func (repo *UserRepo) MyUserMFAs(ctx context.Context) ([]*model.MultiFactor, error) {
user, err := repo.UserByID(ctx, authz.GetCtxData(ctx).UserID)
if err != nil {
@ -225,142 +162,24 @@ func (repo *UserRepo) MyUserMFAs(ctx context.Context) ([]*model.MultiFactor, err
return mfas, nil
}
func (repo *UserRepo) AddMFAOTP(ctx context.Context, userID string) (*model.OTP, error) {
accountName := ""
user, err := repo.UserByID(ctx, userID)
if err != nil {
logging.Log("EVENT-Fk93s").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname")
} else {
accountName = user.PreferredLoginName
}
return repo.UserEvents.AddOTP(ctx, userID, accountName)
}
func (repo *UserRepo) VerifyMFAOTPSetup(ctx context.Context, userID, code, userAgentID string) error {
return repo.UserEvents.CheckMFAOTPSetup(ctx, userID, code, userAgentID)
}
func (repo *UserRepo) RemoveMyMFAOTP(ctx context.Context) error {
return repo.UserEvents.RemoveOTP(ctx, authz.GetCtxData(ctx).UserID)
}
func (repo *UserRepo) AddMFAU2F(ctx context.Context, userID string) (*model.WebAuthNToken, error) {
accountName := ""
user, err := repo.UserByID(ctx, userID)
if err != nil {
logging.Log("EVENT-DAqe1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname")
} else {
accountName = user.PreferredLoginName
}
return repo.UserEvents.AddU2F(ctx, userID, accountName, true)
}
func (repo *UserRepo) AddMyMFAU2F(ctx context.Context) (*model.WebAuthNToken, error) {
userID := authz.GetCtxData(ctx).UserID
accountName := ""
user, err := repo.UserByID(ctx, userID)
if err != nil {
logging.Log("EVENT-Ghwl1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname")
} else {
accountName = user.PreferredLoginName
}
return repo.UserEvents.AddU2F(ctx, userID, accountName, false)
}
func (repo *UserRepo) VerifyMFAU2FSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error {
return repo.UserEvents.VerifyU2FSetup(ctx, userID, tokenName, userAgentID, credentialData)
}
func (repo *UserRepo) GetPasswordless(ctx context.Context, userID string) ([]*model.WebAuthNToken, error) {
return repo.UserEvents.GetPasswordless(ctx, userID)
}
func (repo *UserRepo) AddPasswordless(ctx context.Context, userID string) (*model.WebAuthNToken, error) {
accountName := ""
user, err := repo.UserByID(ctx, userID)
if err != nil {
logging.Log("EVENT-Vj2k1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname")
} else {
accountName = user.PreferredLoginName
}
return repo.UserEvents.AddPasswordless(ctx, userID, accountName, true)
}
func (repo *UserRepo) GetMyPasswordless(ctx context.Context) ([]*model.WebAuthNToken, error) {
return repo.UserEvents.GetPasswordless(ctx, authz.GetCtxData(ctx).UserID)
}
func (repo *UserRepo) VerifyPasswordlessSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error {
return repo.UserEvents.VerifyPasswordlessSetup(ctx, userID, tokenName, userAgentID, credentialData)
}
func (repo *UserRepo) RemoveMyPasswordless(ctx context.Context, webAuthNTokenID string) error {
return repo.UserEvents.RemovePasswordlessToken(ctx, authz.GetCtxData(ctx).UserID, webAuthNTokenID)
}
func (repo *UserRepo) ChangeMyUsername(ctx context.Context, username string) error {
ctxData := authz.GetCtxData(ctx)
orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(ctxData.OrgID)
if errors.IsNotFound(err) {
orgPolicy, err = repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID)
}
if err != nil {
return err
}
orgPolicyView := iam_es_model.OrgIAMViewToModel(orgPolicy)
return repo.UserEvents.ChangeUsername(ctx, ctxData.UserID, username, orgPolicyView)
}
func (repo *UserRepo) ResendInitVerificationMail(ctx context.Context, userID string) error {
_, err := repo.UserEvents.CreateInitializeUserCodeByID(ctx, userID)
return err
}
func (repo *UserRepo) VerifyInitCode(ctx context.Context, userID, code, password string) error {
policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if errors.IsNotFound(err) {
policy, err = repo.View.PasswordComplexityPolicyByAggregateID(repo.SystemDefaults.IamID)
}
if err != nil {
return err
}
pwPolicyView := iam_es_model.PasswordComplexityViewToModel(policy)
return repo.UserEvents.VerifyInitCode(ctx, pwPolicyView, userID, code, password)
}
func (repo *UserRepo) SkipMFAInit(ctx context.Context, userID string) error {
return repo.UserEvents.SkipMFAInit(ctx, userID)
}
func (repo *UserRepo) RequestPasswordReset(ctx context.Context, loginname string) error {
user, err := repo.View.UserByLoginName(loginname)
if err != nil {
return err
}
return repo.UserEvents.RequestSetPassword(ctx, user.ID, model.NotificationTypeEmail)
}
func (repo *UserRepo) SetPassword(ctx context.Context, userID, code, password, userAgentID string) error {
policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if errors.IsNotFound(err) {
policy, err = repo.View.PasswordComplexityPolicyByAggregateID(repo.SystemDefaults.IamID)
}
if err != nil {
return err
}
pwPolicyView := iam_es_model.PasswordComplexityViewToModel(policy)
return repo.UserEvents.SetPassword(ctx, pwPolicyView, userID, code, password, userAgentID)
}
func (repo *UserRepo) SignOut(ctx context.Context, agentID string) error {
func (repo *UserRepo) UserSessionUserIDsByAgentID(ctx context.Context, agentID string) ([]string, error) {
userSessions, err := repo.View.UserSessionsByAgentID(agentID)
if err != nil {
return err
return nil, err
}
userIDs := make([]string, len(userSessions))
for i, session := range userSessions {
userIDs[i] = session.UserID
}
return repo.UserEvents.SignOut(ctx, agentID, userIDs)
return userIDs, nil
}
func (repo *UserRepo) UserByID(ctx context.Context, id string) (*model.UserView, error) {
@ -385,6 +204,27 @@ func (repo *UserRepo) UserByID(ctx context.Context, id string) (*model.UserView,
return usr_view_model.UserToModel(&userCopy), nil
}
func (repo *UserRepo) UserByLoginName(ctx context.Context, loginname string) (*model.UserView, error) {
user, err := repo.View.UserByLoginName(loginname)
if err != nil {
return nil, err
}
events, err := repo.UserEvents.UserEventsByID(ctx, user.ID, user.Sequence)
if err != nil {
logging.Log("EVENT-PSoc3").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error retrieving new events")
return usr_view_model.UserToModel(user), nil
}
userCopy := *user
for _, event := range events {
if err := userCopy.AppendEvent(event); err != nil {
return usr_view_model.UserToModel(user), nil
}
}
if userCopy.State == int32(model.UserStateDeleted) {
return nil, errors.ThrowNotFound(nil, "EVENT-vZ8us", "Errors.User.NotFound")
}
return usr_view_model.UserToModel(&userCopy), nil
}
func (repo *UserRepo) MyUserChanges(ctx context.Context, lastSequence uint64, limit uint64, sortAscending bool) (*model.UserChanges, error) {
changes, err := repo.UserEvents.UserChanges(ctx, authz.GetCtxData(ctx).UserID, lastSequence, limit, sortAscending)
if err != nil {
@ -405,19 +245,6 @@ func (repo *UserRepo) MyUserChanges(ctx context.Context, lastSequence uint64, li
return changes, nil
}
func (repo *UserRepo) ChangeUsername(ctx context.Context, userID, username string) error {
policyResourceOwner := authz.GetCtxData(ctx).OrgID
orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(policyResourceOwner)
if errors.IsNotFound(err) {
orgPolicy, err = repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID)
}
if err != nil {
return err
}
orgPolicyView := iam_es_model.OrgIAMViewToModel(orgPolicy)
return repo.UserEvents.ChangeUsername(ctx, userID, username, orgPolicyView)
}
func checkIDs(ctx context.Context, obj es_models.ObjectRoot) error {
if obj.AggregateID != authz.GetCtxData(ctx).UserID {
return errors.ThrowPermissionDenied(nil, "EVENT-kFi9w", "object does not belong to user")

View File

@ -20,6 +20,7 @@ import (
es_org "github.com/caos/zitadel/internal/org/repository/eventsourcing"
es_proj "github.com/caos/zitadel/internal/project/repository/eventsourcing"
es_user "github.com/caos/zitadel/internal/user/repository/eventsourcing"
"github.com/caos/zitadel/internal/v2/command"
"github.com/caos/zitadel/internal/v2/query"
)
@ -46,7 +47,7 @@ type EsRepository struct {
eventstore.IAMRepository
}
func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, authZRepo *authz_repo.EsRepository) (*EsRepository, error) {
func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, command *command.CommandSide, authZRepo *authz_repo.EsRepository) (*EsRepository, error) {
es, err := es_int.Start(conf.Eventstore)
if err != nil {
return nil, err
@ -131,12 +132,14 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, au
SystemDefaults: systemDefaults,
},
eventstore.AuthRequestRepo{
Command: command,
UserEvents: user,
OrgEvents: org,
AuthRequests: authReq,
View: view,
UserSessionViewProvider: view,
UserViewProvider: view,
UserCommandProvider: command,
UserEventProvider: user,
OrgViewProvider: view,
IDPProviderViewProvider: view,

View File

@ -3,11 +3,9 @@ package repository
import (
"context"
usr_model "github.com/caos/zitadel/internal/user/model"
"time"
)
type TokenRepository interface {
CreateToken(ctx context.Context, agentID, clientID, userID string, audience, scopes []string, lifetime time.Duration) (*usr_model.Token, error)
IsTokenValid(ctx context.Context, userID, tokenID string) (bool, error)
TokenByID(ctx context.Context, userID, tokenID string) (*usr_model.TokenView, error)
}

View File

@ -3,43 +3,18 @@ package repository
import (
"context"
org_model "github.com/caos/zitadel/internal/org/model"
"github.com/caos/zitadel/internal/user/model"
)
type UserRepository interface {
Register(ctx context.Context, user *model.User, member *org_model.OrgMember, resourceOwner string) (*model.User, error)
RegisterExternalUser(ctx context.Context, user *model.User, externalIDP *model.ExternalIDP, member *org_model.OrgMember, 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, userAgentID string) error
ChangePassword(ctx context.Context, userID, old, new, userAgentID string) error
VerifyEmail(ctx context.Context, userID, code string) error
ResendEmailVerificationMail(ctx context.Context, userID string) error
VerifyInitCode(ctx context.Context, userID, code, password string) error
ResendInitVerificationMail(ctx context.Context, userID string) error
AddMFAOTP(ctx context.Context, userID string) (*model.OTP, error)
VerifyMFAOTPSetup(ctx context.Context, userID, code, userAgentID string) error
AddMFAU2F(ctx context.Context, id string) (*model.WebAuthNToken, error)
VerifyMFAU2FSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error
GetPasswordless(ctx context.Context, id string) ([]*model.WebAuthNToken, error)
AddPasswordless(ctx context.Context, id string) (*model.WebAuthNToken, error)
VerifyPasswordlessSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error
ChangeUsername(ctx context.Context, userID, username string) error
SignOut(ctx context.Context, agentID string) error
UserSessionUserIDsByAgentID(ctx context.Context, agentID string) ([]string, error)
UserByID(ctx context.Context, userID string) (*model.UserView, error)
UserByLoginName(ctx context.Context, loginName string) (*model.UserView, error)
MachineKeyByID(ctx context.Context, keyID string) (*model.MachineKeyView, error)
}
@ -59,8 +34,6 @@ type myUserRepo interface {
MyUserMFAs(ctx context.Context) ([]*model.MultiFactor, error)
AddMyMFAU2F(ctx context.Context) (*model.WebAuthNToken, error)
GetMyPasswordless(ctx context.Context) ([]*model.WebAuthNToken, error)
MyUserChanges(ctx context.Context, lastSequence uint64, limit uint64, sortAscending bool) (*model.UserChanges, error)

View File

@ -1,5 +1,9 @@
package model
import (
"github.com/caos/zitadel/internal/v2/domain"
)
type NextStep interface {
Type() NextStepType
}
@ -164,3 +168,45 @@ const (
MFALevelMultiFactor
MFALevelMultiFactorCertified
)
func MFATypeToDomain(mfaType MFAType) domain.MFAType {
switch mfaType {
case MFATypeOTP:
return domain.MFATypeOTP
case MFATypeU2F:
return domain.MFATypeU2F
case MFATypeU2FUserVerification:
return domain.MFATypeU2FUserVerification
default:
return domain.MFATypeOTP
}
}
func MFALevelToDomain(mfaLevel MFALevel) domain.MFALevel {
switch mfaLevel {
case MFALevelNotSetUp:
return domain.MFALevelNotSetUp
case MFALevelSecondFactor:
return domain.MFALevelSecondFactor
case MFALevelMultiFactor:
return domain.MFALevelMultiFactor
case MFALevelMultiFactorCertified:
return domain.MFALevelMultiFactorCertified
default:
return domain.MFALevelNotSetUp
}
}
func UserSessionStateToDomain(state UserSessionState) domain.UserSessionState {
switch state {
case UserSessionStateActive:
return domain.UserSessionStateActive
case UserSessionStateTerminated:
return domain.UserSessionStateTerminated
default:
return domain.UserSessionStateActive
}
}

View File

@ -6,8 +6,8 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/config/types"
caos_errs "github.com/caos/zitadel/internal/errors"
)
@ -34,19 +34,19 @@ func (c *AuthRequestCache) Health(ctx context.Context) error {
return c.client.PingContext(ctx)
}
func (c *AuthRequestCache) GetAuthRequestByID(_ context.Context, id string) (*model.AuthRequest, error) {
func (c *AuthRequestCache) GetAuthRequestByID(_ context.Context, id string) (*domain.AuthRequest, error) {
return c.getAuthRequest("id", id)
}
func (c *AuthRequestCache) GetAuthRequestByCode(_ context.Context, code string) (*model.AuthRequest, error) {
func (c *AuthRequestCache) GetAuthRequestByCode(_ context.Context, code string) (*domain.AuthRequest, error) {
return c.getAuthRequest("code", code)
}
func (c *AuthRequestCache) SaveAuthRequest(_ context.Context, request *model.AuthRequest) error {
func (c *AuthRequestCache) SaveAuthRequest(_ context.Context, request *domain.AuthRequest) error {
return c.saveAuthRequest(request, "INSERT INTO auth.auth_requests (id, request, request_type) VALUES($1, $2, $3)", request.Request.Type())
}
func (c *AuthRequestCache) UpdateAuthRequest(_ context.Context, request *model.AuthRequest) error {
func (c *AuthRequestCache) UpdateAuthRequest(_ context.Context, request *domain.AuthRequest) error {
return c.saveAuthRequest(request, "UPDATE auth.auth_requests SET request = $2, code = $3 WHERE id = $1", request.Code)
}
@ -58,9 +58,9 @@ func (c *AuthRequestCache) DeleteAuthRequest(_ context.Context, id string) error
return nil
}
func (c *AuthRequestCache) getAuthRequest(key, value string) (*model.AuthRequest, error) {
func (c *AuthRequestCache) getAuthRequest(key, value string) (*domain.AuthRequest, error) {
var b []byte
var requestType model.AuthRequestType
var requestType domain.AuthRequestType
query := fmt.Sprintf("SELECT request, request_type FROM auth.auth_requests WHERE %s = $1", key)
err := c.client.QueryRow(query, value).Scan(&b, &requestType)
if err != nil {
@ -69,7 +69,7 @@ func (c *AuthRequestCache) getAuthRequest(key, value string) (*model.AuthRequest
}
return nil, caos_errs.ThrowInternal(err, "CACHE-as3kj", "Errors.Internal")
}
request, err := model.NewAuthRequestFromType(requestType)
request, err := domain.NewAuthRequestFromType(requestType)
if err == nil {
err = json.Unmarshal(b, request)
}
@ -79,7 +79,7 @@ func (c *AuthRequestCache) getAuthRequest(key, value string) (*model.AuthRequest
return request, nil
}
func (c *AuthRequestCache) saveAuthRequest(request *model.AuthRequest, query string, param interface{}) error {
func (c *AuthRequestCache) saveAuthRequest(request *domain.AuthRequest, query string, param interface{}) error {
b, err := json.Marshal(request)
if err != nil {
return caos_errs.ThrowInternal(err, "CACHE-os0GH", "Errors.Internal")

View File

@ -107,7 +107,7 @@ func (mr *MockAuthRequestCacheMockRecorder) SaveAuthRequest(arg0, arg1 interface
}
// UpdateAuthRequest mocks base method
func (m *MockAuthRequestCache) UpdateAuthRequest(arg0 context.Context, arg1 *model.AuthRequest) error {
func (m *MockAuthRequestCache) UpdateAuthRequest(arg0 context.Context, arg1 *domain.AuthRequest) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateAuthRequest", arg0, arg1)
ret0, _ := ret[0].(error)

View File

@ -2,16 +2,15 @@ package repository
import (
"context"
"github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/v2/domain"
)
type AuthRequestCache interface {
Health(ctx context.Context) error
GetAuthRequestByID(ctx context.Context, id string) (*model.AuthRequest, error)
GetAuthRequestByCode(ctx context.Context, code string) (*model.AuthRequest, error)
SaveAuthRequest(ctx context.Context, request *model.AuthRequest) error
UpdateAuthRequest(ctx context.Context, request *model.AuthRequest) error
GetAuthRequestByID(ctx context.Context, id string) (*domain.AuthRequest, error)
GetAuthRequestByCode(ctx context.Context, code string) (*domain.AuthRequest, error)
SaveAuthRequest(ctx context.Context, request *domain.AuthRequest) error
UpdateAuthRequest(ctx context.Context, request *domain.AuthRequest) error
DeleteAuthRequest(ctx context.Context, id string) error
}

View File

@ -63,7 +63,7 @@ func (e *testEvent) Data() interface{} {
return e.data()
}
func (e *testEvent) UniqueConstraint() []EventUniqueConstraint {
func (e *testEvent) UniqueConstraint() []*EventUniqueConstraint {
return nil
}

View File

@ -188,7 +188,7 @@ func (db *CRDB) handleUniqueConstraints(ctx context.Context, tx *sql.Tx, uniqueC
logging.LogWithFields("SQL-M0vsf",
"unique_type", uniqueConstraint.UniqueType,
"unique_field", uniqueConstraint.UniqueField).WithError(err).Info("delete unique constraint failed")
return caos_errs.ThrowInternal(err, "SQL-2M9fs", "unable to remove unique constraint ")
return caos_errs.ThrowInternal(err, "SQL-6n88i", "unable to remove unique constraint ")
}
}
}

View File

@ -139,14 +139,22 @@ func (factory *SearchQueryBuilder) eventTypeFilter() *repository.Filter {
if len(factory.eventTypes) == 1 {
return repository.NewFilter(repository.FieldEventType, factory.eventTypes[0], repository.OperationEquals)
}
return repository.NewFilter(repository.FieldEventType, factory.eventTypes, repository.OperationIn)
eventTypes := make([]repository.EventType, len(factory.eventTypes))
for i, eventType := range factory.eventTypes {
eventTypes[i] = repository.EventType(eventType)
}
return repository.NewFilter(repository.FieldEventType, eventTypes, repository.OperationIn)
}
func (factory *SearchQueryBuilder) aggregateTypeFilter() *repository.Filter {
if len(factory.aggregateTypes) == 1 {
return repository.NewFilter(repository.FieldAggregateType, factory.aggregateTypes[0], repository.OperationEquals)
}
return repository.NewFilter(repository.FieldAggregateType, factory.aggregateTypes, repository.OperationIn)
aggregateTypes := make([]repository.AggregateType, len(factory.aggregateTypes))
for i, aggregateType := range factory.aggregateTypes {
aggregateTypes[i] = repository.AggregateType(aggregateType)
}
return repository.NewFilter(repository.FieldAggregateType, aggregateTypes, repository.OperationIn)
}
func (factory *SearchQueryBuilder) eventSequenceFilter() *repository.Filter {

View File

@ -2,6 +2,7 @@ package model
import (
"github.com/caos/zitadel/internal/model"
"github.com/caos/zitadel/internal/v2/domain"
"time"
)
@ -60,3 +61,63 @@ func (r *IDPProviderSearchRequest) EnsureLimit(limit uint64) {
func (r *IDPProviderSearchRequest) AppendAggregateIDQuery(aggregateID string) {
r.Queries = append(r.Queries, &IDPProviderSearchQuery{Key: IDPProviderSearchKeyAggregateID, Method: model.SearchMethodEquals, Value: aggregateID})
}
func IdpProviderViewsToDomain(idpProviders []*IDPProviderView) []*domain.IDPProvider {
providers := make([]*domain.IDPProvider, len(idpProviders))
for i, provider := range idpProviders {
p := &domain.IDPProvider{
IDPConfigID: provider.IDPConfigID,
Type: idpProviderTypeToDomain(provider.IDPProviderType),
Name: provider.Name,
IDPConfigType: idpConfigTypeToDomain(provider.IDPConfigType),
StylingType: idpStylingTypeToDomain(provider.StylingType),
IDPState: idpStateToDomain(provider.IDPState),
}
providers[i] = p
}
return providers
}
func idpProviderTypeToDomain(idpType IDPProviderType) domain.IdentityProviderType {
switch idpType {
case IDPProviderTypeSystem:
return domain.IdentityProviderTypeSystem
case IDPProviderTypeOrg:
return domain.IdentityProviderTypeOrg
default:
return domain.IdentityProviderTypeSystem
}
}
func idpConfigTypeToDomain(idpType IdpConfigType) domain.IDPConfigType {
switch idpType {
case IDPConfigTypeOIDC:
return domain.IDPConfigTypeOIDC
case IDPConfigTypeSAML:
return domain.IDPConfigTypeSAML
default:
return domain.IDPConfigTypeOIDC
}
}
func idpStylingTypeToDomain(stylingType IDPStylingType) domain.IDPConfigStylingType {
switch stylingType {
case IDPStylingTypeGoogle:
return domain.IDPConfigStylingTypeGoogle
default:
return domain.IDPConfigStylingTypeUnspecified
}
}
func idpStateToDomain(state IDPConfigState) domain.IDPConfigState {
switch state {
case IDPConfigStateActive:
return domain.IDPConfigStateActive
case IDPConfigStateInactive:
return domain.IDPConfigStateInactive
case IDPConfigStateRemoved:
return domain.IDPConfigStateRemoved
default:
return domain.IDPConfigStateActive
}
}

View File

@ -1,7 +1,9 @@
package model
import (
"github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/model"
"github.com/caos/zitadel/internal/v2/domain"
"time"
)
@ -64,3 +66,57 @@ func (p *LoginPolicyView) HasMultiFactors() bool {
}
return true
}
func (p *LoginPolicyView) ToLoginPolicyDomain() *domain.LoginPolicy {
return &domain.LoginPolicy{
ObjectRoot: models.ObjectRoot{
AggregateID: p.AggregateID,
CreationDate: p.CreationDate,
ChangeDate: p.ChangeDate,
Sequence: p.Sequence,
},
Default: p.Default,
AllowUsernamePassword: p.AllowUsernamePassword,
AllowRegister: p.AllowRegister,
AllowExternalIDP: p.AllowExternalIDP,
ForceMFA: p.ForceMFA,
PasswordlessType: passwordLessTypeToDomain(p.PasswordlessType),
SecondFactors: secondFactorsToDomain(p.SecondFactors),
MultiFactors: multiFactorsToDomain(p.MultiFactors),
}
}
func passwordLessTypeToDomain(passwordless PasswordlessType) domain.PasswordlessType {
switch passwordless {
case PasswordlessTypeNotAllowed:
return domain.PasswordlessTypeNotAllowed
case PasswordlessTypeAllowed:
return domain.PasswordlessTypeAllowed
default:
return domain.PasswordlessTypeNotAllowed
}
}
func secondFactorsToDomain(types []SecondFactorType) []domain.SecondFactorType {
secondfactors := make([]domain.SecondFactorType, len(types))
for i, secondfactorType := range types {
switch secondfactorType {
case SecondFactorTypeU2F:
secondfactors[i] = domain.SecondFactorTypeU2F
case SecondFactorTypeOTP:
secondfactors[i] = domain.SecondFactorTypeOTP
}
}
return secondfactors
}
func multiFactorsToDomain(types []MultiFactorType) []domain.MultiFactorType {
multifactors := make([]domain.MultiFactorType, len(types))
for i, multifactorType := range types {
switch multifactorType {
case MultiFactorTypeU2FWithPIN:
multifactors[i] = domain.MultiFactorTypeU2FWithPIN
}
}
return multifactors
}

View File

@ -9,7 +9,6 @@ import (
"github.com/caos/zitadel/internal/errors"
caos_errs "github.com/caos/zitadel/internal/errors"
es_int "github.com/caos/zitadel/internal/eventstore"
iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model"
"github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
global_model "github.com/caos/zitadel/internal/model"
org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing"
@ -194,26 +193,6 @@ func (repo *UserRepo) SearchMachineKeys(ctx context.Context, request *usr_model.
return result, nil
}
func (repo *UserRepo) AddMachineKey(ctx context.Context, key *usr_model.MachineKey) (*usr_model.MachineKey, error) {
return repo.UserEvents.AddMachineKey(ctx, key)
}
func (repo *UserRepo) RemoveMachineKey(ctx context.Context, userID, keyID string) error {
return repo.UserEvents.RemoveMachineKey(ctx, userID, keyID)
}
func (repo *UserRepo) ChangeUsername(ctx context.Context, userID, userName string) error {
orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if err != nil && errors.IsNotFound(err) {
orgPolicy, err = repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID)
}
if err != nil {
return err
}
orgPolicyView := iam_es_model.OrgIAMViewToModel(orgPolicy)
return repo.UserEvents.ChangeUsername(ctx, userID, userName, orgPolicyView)
}
func (repo *UserRepo) EmailByID(ctx context.Context, userID string) (*usr_model.Email, error) {
user, err := repo.UserByID(ctx, userID)
if err != nil {

View File

@ -27,6 +27,22 @@ func (repo *UserGrantRepo) UserGrantByID(ctx context.Context, grantID string) (*
return model.UserGrantToModel(grant), nil
}
func (repo *UserGrantRepo) UserGrantsByProjectID(ctx context.Context, projectID string) ([]*grant_model.UserGrantView, error) {
grants, err := repo.View.UserGrantsByProjectID(projectID)
if err != nil {
return nil, err
}
return model.UserGrantsToModel(grants), nil
}
func (repo *UserGrantRepo) UserGrantsByUserID(ctx context.Context, userID string) ([]*grant_model.UserGrantView, error) {
grants, err := repo.View.UserGrantsByUserID(userID)
if err != nil {
return nil, err
}
return model.UserGrantsToModel(grants), nil
}
func (repo *UserGrantRepo) SearchUserGrants(ctx context.Context, request *grant_model.UserGrantSearchRequest) (*grant_model.UserGrantSearchResponse, error) {
request.EnsureLimit(repo.SearchLimit)
sequence, sequenceErr := repo.View.GetLatestUserGrantSequence("")

View File

@ -25,8 +25,6 @@ type UserRepository interface {
SearchMachineKeys(ctx context.Context, request *model.MachineKeySearchRequest) (*model.MachineKeySearchResponse, error)
GetMachineKey(ctx context.Context, userID, keyID string) (*model.MachineKeyView, error)
AddMachineKey(ctx context.Context, key *model.MachineKey) (*model.MachineKey, error)
RemoveMachineKey(ctx context.Context, userID, keyID string) error
EmailByID(ctx context.Context, userID string) (*model.Email, error)

View File

@ -8,4 +8,6 @@ import (
type UserGrantRepository interface {
UserGrantByID(ctx context.Context, grantID string) (*model.UserGrantView, error)
SearchUserGrants(ctx context.Context, request *model.UserGrantSearchRequest) (*model.UserGrantSearchResponse, error)
UserGrantsByProjectID(ctx context.Context, projectID string) ([]*model.UserGrantView, error)
UserGrantsByUserID(ctx context.Context, userID string) ([]*model.UserGrantView, error)
}

View File

@ -5,6 +5,7 @@ import (
"github.com/caos/logging"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/notification/repository/eventsourcing"
"github.com/caos/zitadel/internal/v2/command"
"github.com/rakyll/statik/fs"
_ "github.com/caos/zitadel/internal/notification/statik"
@ -14,10 +15,10 @@ type Config struct {
Repository eventsourcing.Config
}
func Start(ctx context.Context, config Config, systemDefaults sd.SystemDefaults) {
func Start(ctx context.Context, config Config, systemDefaults sd.SystemDefaults, command *command.CommandSide) {
statikFS, err := fs.NewWithNamespace("notification")
logging.Log("CONFI-7usEW").OnError(err).Panic("unable to start listener")
_, err = eventsourcing.Start(config.Repository, statikFS, systemDefaults)
_, err = eventsourcing.Start(config.Repository, statikFS, systemDefaults, command)
logging.Log("MAIN-9uBxp").OnError(err).Panic("unable to start app")
}

View File

@ -1,6 +1,7 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/command"
"net/http"
"time"
@ -42,7 +43,7 @@ type EventstoreRepos struct {
IAMEvents *iam_es.IAMEventstore
}
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults, i18n *i18n.Translator, dir http.FileSystem) []query.Handler {
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es eventstore.Eventstore, command *command.CommandSide, repos EventstoreRepos, systemDefaults sd.SystemDefaults, i18n *i18n.Translator, dir http.FileSystem) []query.Handler {
aesCrypto, err := crypto.NewAESCrypto(systemDefaults.UserVerificationKey)
if err != nil {
logging.Log("HANDL-s90ew").WithError(err).Debug("error create new aes crypto")
@ -56,6 +57,7 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
),
newNotification(
handler{view, bulkLimit, configs.cycleDuration("Notification"), errorCount, es},
command,
repos.UserEvents,
systemDefaults,
aesCrypto,

View File

@ -3,6 +3,8 @@ package handler
import (
"context"
"encoding/json"
"github.com/caos/zitadel/internal/user/repository/view/model"
"github.com/caos/zitadel/internal/v2/command"
"net/http"
"time"
@ -32,6 +34,7 @@ const (
type Notification struct {
handler
command *command.CommandSide
userEvents *usr_event.UserEventstore
systemDefaults sd.SystemDefaults
AesCrypto crypto.EncryptionAlgorithm
@ -42,6 +45,7 @@ type Notification struct {
func newNotification(
handler handler,
command *command.CommandSide,
userEvents *usr_event.UserEventstore,
defaults sd.SystemDefaults,
aesCrypto crypto.EncryptionAlgorithm,
@ -50,6 +54,7 @@ func newNotification(
) *Notification {
h := &Notification{
handler: handler,
command: command,
userEvents: userEvents,
systemDefaults: defaults,
i18n: translator,
@ -135,7 +140,7 @@ func (n *Notification) handleInitUserCode(event *models.Event) (err error) {
return err
}
user, err := n.view.NotifyUserByID(event.AggregateID)
user, err := n.getUserByID(event.AggregateID)
if err != nil {
return err
}
@ -143,7 +148,7 @@ func (n *Notification) handleInitUserCode(event *models.Event) (err error) {
if err != nil {
return err
}
return n.userEvents.InitCodeSent(getSetNotifyContextData(event.ResourceOwner), event.AggregateID)
return n.command.HumanInitCodeSent(getSetNotifyContextData(event.ResourceOwner), event.ResourceOwner, event.AggregateID)
}
func (n *Notification) handlePasswordCode(event *models.Event) (err error) {
@ -163,7 +168,7 @@ func (n *Notification) handlePasswordCode(event *models.Event) (err error) {
return err
}
user, err := n.view.NotifyUserByID(event.AggregateID)
user, err := n.getUserByID(event.AggregateID)
if err != nil {
return err
}
@ -171,7 +176,7 @@ func (n *Notification) handlePasswordCode(event *models.Event) (err error) {
if err != nil {
return err
}
return n.userEvents.PasswordCodeSent(getSetNotifyContextData(event.ResourceOwner), event.AggregateID)
return n.command.PasswordCodeSent(getSetNotifyContextData(event.ResourceOwner), event.ResourceOwner, event.AggregateID)
}
func (n *Notification) handleEmailVerificationCode(event *models.Event) (err error) {
@ -191,7 +196,7 @@ func (n *Notification) handleEmailVerificationCode(event *models.Event) (err err
return err
}
user, err := n.view.NotifyUserByID(event.AggregateID)
user, err := n.getUserByID(event.AggregateID)
if err != nil {
return err
}
@ -199,7 +204,7 @@ func (n *Notification) handleEmailVerificationCode(event *models.Event) (err err
if err != nil {
return err
}
return n.userEvents.EmailVerificationCodeSent(getSetNotifyContextData(event.ResourceOwner), event.AggregateID)
return n.command.HumanEmailVerificationCodeSent(getSetNotifyContextData(event.ResourceOwner), event.ResourceOwner, event.AggregateID)
}
func (n *Notification) handlePhoneVerificationCode(event *models.Event) (err error) {
@ -213,7 +218,7 @@ func (n *Notification) handlePhoneVerificationCode(event *models.Event) (err err
if err != nil || alreadyHandled {
return nil
}
user, err := n.view.NotifyUserByID(event.AggregateID)
user, err := n.getUserByID(event.AggregateID)
if err != nil {
return err
}
@ -221,7 +226,7 @@ func (n *Notification) handlePhoneVerificationCode(event *models.Event) (err err
if err != nil {
return err
}
return n.userEvents.PhoneVerificationCodeSent(getSetNotifyContextData(event.ResourceOwner), event.AggregateID)
return n.command.HumanPhoneVerificationCodeSent(getSetNotifyContextData(event.ResourceOwner), event.ResourceOwner, event.AggregateID)
}
func (n *Notification) handleDomainClaimed(event *models.Event) (err error) {
@ -234,7 +239,7 @@ func (n *Notification) handleDomainClaimed(event *models.Event) (err error) {
logging.Log("HANDLE-Gghq2").WithError(err).Error("could not unmarshal event data")
return caos_errs.ThrowInternal(err, "HANDLE-7hgj3", "could not unmarshal event")
}
user, err := n.view.NotifyUserByID(event.AggregateID)
user, err := n.getUserByID(event.AggregateID)
if err != nil {
return err
}
@ -242,7 +247,7 @@ func (n *Notification) handleDomainClaimed(event *models.Event) (err error) {
if err != nil {
return err
}
return n.userEvents.DomainClaimedSent(getSetNotifyContextData(event.ResourceOwner), event.AggregateID)
return n.command.UserDomainClaimedSent(getSetNotifyContextData(event.ResourceOwner), event.ResourceOwner, event.AggregateID)
}
func (n *Notification) checkIfCodeAlreadyHandledOrExpired(event *models.Event, expiry time.Duration, eventTypes ...models.EventType) (bool, error) {
@ -306,3 +311,27 @@ func (n *Notification) getLabelPolicy(ctx context.Context) (*iam_model.LabelPoli
}
return iam_es_model.LabelPolicyViewToModel(policy), err
}
func (n *Notification) getUserByID(userID string) (*model.NotifyUser, error) {
user, usrErr := n.view.NotifyUserByID(userID)
if usrErr != nil && !caos_errs.IsNotFound(usrErr) {
return nil, usrErr
}
if user == nil {
user = &model.NotifyUser{}
}
events, err := n.getUserEvents(userID, user.Sequence)
if err != nil {
return user, usrErr
}
userCopy := *user
for _, event := range events {
if err := userCopy.AppendEvent(event); err != nil {
return user, nil
}
}
if userCopy.State == int32(model.UserStateDeleted) {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-3n8fs", "Errors.User.NotFound")
}
return &userCopy, nil
}

View File

@ -2,6 +2,7 @@ package eventsourcing
import (
es_iam "github.com/caos/zitadel/internal/iam/repository/eventsourcing"
"github.com/caos/zitadel/internal/v2/command"
"net/http"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
@ -29,7 +30,7 @@ type EsRepository struct {
spooler *es_spol.Spooler
}
func Start(conf Config, dir http.FileSystem, systemDefaults sd.SystemDefaults) (*EsRepository, error) {
func Start(conf Config, dir http.FileSystem, systemDefaults sd.SystemDefaults, command *command.CommandSide) (*EsRepository, error) {
es, err := es_int.Start(conf.Eventstore)
if err != nil {
return nil, err
@ -65,7 +66,7 @@ func Start(conf Config, dir http.FileSystem, systemDefaults sd.SystemDefaults) (
return nil, err
}
eventstoreRepos := handler.EventstoreRepos{UserEvents: user, OrgEvents: org, IAMEvents: iam}
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, eventstoreRepos, systemDefaults, translator, dir)
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, command, eventstoreRepos, systemDefaults, translator, dir)
return &EsRepository{
spool,

View File

@ -2,6 +2,7 @@ package spooler
import (
"database/sql"
"github.com/caos/zitadel/internal/v2/command"
"net/http"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
@ -19,12 +20,12 @@ type SpoolerConfig struct {
Handlers handler.Configs
}
func StartSpooler(c SpoolerConfig, es eventstore.Eventstore, view *view.View, sql *sql.DB, eventstoreRepos handler.EventstoreRepos, systemDefaults sd.SystemDefaults, i18n *i18n.Translator, dir http.FileSystem) *spooler.Spooler {
func StartSpooler(c SpoolerConfig, es eventstore.Eventstore, view *view.View, sql *sql.DB, command *command.CommandSide, eventstoreRepos handler.EventstoreRepos, systemDefaults sd.SystemDefaults, i18n *i18n.Translator, dir http.FileSystem) *spooler.Spooler {
spoolerConfig := spooler.Config{
Eventstore: es,
Locker: &locker{dbClient: sql},
ConcurrentWorkers: c.ConcurrentWorkers,
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, eventstoreRepos, systemDefaults, i18n, dir),
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, command, eventstoreRepos, systemDefaults, i18n, dir),
}
spool := spoolerConfig.New()
spool.Start()

View File

@ -38,6 +38,9 @@ Errors:
Address:
NotFound: Addresse nicht gefunden
NotChanged: Addresse wurde nicht geändert
Machine:
Key:
NotFound: Maschinen Key nicht gefunden
NotHuman: Der Benutzer muss eine Person sein
NotMachine: Der Benutzer muss technisch sein
NotAllowedToLink: Der Benutzer darf nicht mit einem externen Login Provider verlinkt werden
@ -53,6 +56,7 @@ Errors:
NotFound: Password nicht gefunden
Empty: Passwort ist leer
Invalid: Passwort ungültig
NotSet: Benutzer hat kein Passwort gesetzt
PasswordComplexityPolicy:
NotFound: Passwort Policy konnte nicht gefunden werden
MinLength: Passwort ist zu kurz

View File

@ -38,6 +38,9 @@ Errors:
Address:
NotFound: Address not found
NotChanged: Address not changed
Machine:
Key:
NotFound: Machine key not found
NotHuman: The User must be personal
NotMachine: The User must be technical
NotAllowedToLink: User is not allowed to link with external login provider
@ -53,6 +56,7 @@ Errors:
NotFound: Passoword not found
Empty: Password is empty
Invalid: Passwort is invalid
NotSet: User has not set a password
PasswordComplexityPolicy:
NotFound: Password policy not found
MinLength: Password is to short

View File

@ -1,17 +1,17 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
)
const (
queryAuthRequestID = "authRequestID"
)
func (l *Login) getAuthRequest(r *http.Request) (*model.AuthRequest, error) {
func (l *Login) getAuthRequest(r *http.Request) (*domain.AuthRequest, error) {
authRequestID := r.FormValue(queryAuthRequestID)
if authRequestID == "" {
return nil, nil
@ -20,7 +20,7 @@ func (l *Login) getAuthRequest(r *http.Request) (*model.AuthRequest, error) {
return l.authRepo.AuthRequestByID(r.Context(), authRequestID, userAgentID)
}
func (l *Login) getAuthRequestAndParseData(r *http.Request, data interface{}) (*model.AuthRequest, error) {
func (l *Login) getAuthRequestAndParseData(r *http.Request, data interface{}) (*domain.AuthRequest, error) {
authReq, err := l.getAuthRequest(r)
if err != nil {
return nil, err

View File

@ -1,12 +1,11 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
"github.com/caos/zitadel/internal/auth_request/model"
)
func (l *Login) redirectToCallback(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
func (l *Login) redirectToCallback(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
callback := l.oidcAuthCallbackURL + authReq.ID
http.Redirect(w, r, callback, http.StatusFound)
}

View File

@ -1,10 +1,10 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
)
const (
@ -26,7 +26,7 @@ func (l *Login) handleChangePassword(w http.ResponseWriter, r *http.Request) {
return
}
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
err = l.authRepo.ChangePassword(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, data.OldPassword, data.NewPassword, userAgentID)
err = l.command.ChangePassword(setContext(r.Context(), authReq.UserOrgID), authReq.UserOrgID, authReq.UserID, data.OldPassword, data.NewPassword, userAgentID)
if err != nil {
l.renderChangePassword(w, r, authReq, err)
return
@ -34,7 +34,7 @@ func (l *Login) handleChangePassword(w http.ResponseWriter, r *http.Request) {
l.renderChangePasswordDone(w, r, authReq)
}
func (l *Login) renderChangePassword(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
func (l *Login) renderChangePassword(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
var errType, errMessage string
if err != nil {
errMessage = l.getErrorMessage(r, err)
@ -63,7 +63,7 @@ func (l *Login) renderChangePassword(w http.ResponseWriter, r *http.Request, aut
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplChangePassword], data, nil)
}
func (l *Login) renderChangePasswordDone(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
func (l *Login) renderChangePasswordDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
var errType, errMessage string
data := l.getUserData(r, authReq, "Password Change Done", errType, errMessage)
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplChangePasswordDone], data, nil)

View File

@ -4,14 +4,11 @@ import (
"github.com/caos/oidc/pkg/oidc"
"github.com/caos/oidc/pkg/rp"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/errors"
caos_errors "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
iam_model "github.com/caos/zitadel/internal/iam/model"
org_model "github.com/caos/zitadel/internal/org/model"
usr_model "github.com/caos/zitadel/internal/user/model"
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
"strings"
"time"
@ -41,7 +38,7 @@ type externalNotFoundOptionData struct {
baseData
}
func (l *Login) handleExternalLoginStep(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, selectedIDPConfigID string) {
func (l *Login) handleExternalLoginStep(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, selectedIDPConfigID string) {
for _, idp := range authReq.AllowedExternalIDPs {
if idp.IDPConfigID == selectedIDPConfigID {
l.handleIDP(w, r, authReq, selectedIDPConfigID)
@ -65,7 +62,7 @@ func (l *Login) handleExternalLogin(w http.ResponseWriter, r *http.Request) {
l.handleIDP(w, r, authReq, data.IDPConfigID)
}
func (l *Login) handleIDP(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, selectedIDPConfigID string) {
func (l *Login) handleIDP(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, selectedIDPConfigID string) {
idpConfig, err := l.getIDPConfigByID(r, selectedIDPConfigID)
if err != nil {
l.renderError(w, r, authReq, err)
@ -84,7 +81,7 @@ func (l *Login) handleIDP(w http.ResponseWriter, r *http.Request, authReq *model
l.handleOIDCAuthorize(w, r, authReq, idpConfig, EndpointExternalLoginCallback)
}
func (l *Login) handleOIDCAuthorize(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, idpConfig *iam_model.IDPConfigView, callbackEndpoint string) {
func (l *Login) handleOIDCAuthorize(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, idpConfig *iam_model.IDPConfigView, callbackEndpoint string) {
provider := l.getRPConfig(w, r, authReq, idpConfig, callbackEndpoint)
http.Redirect(w, r, rp.AuthURL(authReq.ID, provider, rp.WithPrompt(oidc.PromptSelectAccount)), http.StatusFound)
}
@ -116,7 +113,7 @@ func (l *Login) handleExternalLoginCallback(w http.ResponseWriter, r *http.Reque
l.handleExternalUserAuthenticated(w, r, authReq, idpConfig, userAgentID, tokens)
}
func (l *Login) getRPConfig(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, idpConfig *iam_model.IDPConfigView, callbackEndpoint string) rp.RelayingParty {
func (l *Login) getRPConfig(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, idpConfig *iam_model.IDPConfigView, callbackEndpoint string) rp.RelayingParty {
oidcClientSecret, err := crypto.DecryptString(idpConfig.OIDCClientSecret, l.IDPConfigAesCrypto)
if err != nil {
l.renderError(w, r, authReq, err)
@ -130,9 +127,9 @@ func (l *Login) getRPConfig(w http.ResponseWriter, r *http.Request, authReq *mod
return provider
}
func (l *Login) handleExternalUserAuthenticated(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, idpConfig *iam_model.IDPConfigView, userAgentID string, tokens *oidc.Tokens) {
func (l *Login) handleExternalUserAuthenticated(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, idpConfig *iam_model.IDPConfigView, userAgentID string, tokens *oidc.Tokens) {
externalUser := l.mapTokenToLoginUser(tokens, idpConfig)
err := l.authRepo.CheckExternalUserLogin(r.Context(), authReq.ID, userAgentID, externalUser, model.BrowserInfoFromRequest(r))
err := l.authRepo.CheckExternalUserLogin(r.Context(), authReq.ID, userAgentID, externalUser, domain.BrowserInfoFromRequest(r))
if err != nil {
if errors.IsNotFound(err) {
err = nil
@ -143,7 +140,7 @@ func (l *Login) handleExternalUserAuthenticated(w http.ResponseWriter, r *http.R
l.renderNextStep(w, r, authReq)
}
func (l *Login) renderExternalNotFoundOption(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
func (l *Login) renderExternalNotFoundOption(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
var errType, errMessage string
if err != nil {
errMessage = l.getErrorMessage(r, err)
@ -176,7 +173,7 @@ func (l *Login) handleExternalNotFoundOptionCheck(w http.ResponseWriter, r *http
l.handleAutoRegister(w, r, authReq)
}
func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
iam, err := l.authRepo.GetIAM(r.Context())
if err != nil {
l.renderExternalNotFoundOption(w, r, authReq, err)
@ -184,13 +181,10 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR
}
resourceOwner := iam.GlobalOrgID
member := &org_model.OrgMember{
ObjectRoot: models.ObjectRoot{AggregateID: iam.GlobalOrgID},
Roles: []string{orgProjectCreatorRole},
}
memberRoles := []string{domain.RoleOrgProjectCreator}
if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != iam.GlobalOrgID {
member = nil
memberRoles = nil
resourceOwner = authReq.RequestedOrgID
}
@ -208,7 +202,7 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
user, externalIDP := l.mapExternalUserToLoginUser(orgIamPolicy, authReq.LinkingUsers[len(authReq.LinkingUsers)-1], idpConfig)
err = l.authRepo.AutoRegisterExternalUser(setContext(r.Context(), resourceOwner), user, externalIDP, member, authReq.ID, userAgentID, resourceOwner, model.BrowserInfoFromRequest(r))
err = l.authRepo.AutoRegisterExternalUser(setContext(r.Context(), resourceOwner), user, externalIDP, memberRoles, authReq.ID, userAgentID, resourceOwner, domain.BrowserInfoFromRequest(r))
if err != nil {
l.renderExternalNotFoundOption(w, r, authReq, err)
return
@ -216,7 +210,7 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR
l.renderNextStep(w, r, authReq)
}
func (l *Login) mapTokenToLoginUser(tokens *oidc.Tokens, idpConfig *iam_model.IDPConfigView) *model.ExternalUser {
func (l *Login) mapTokenToLoginUser(tokens *oidc.Tokens, idpConfig *iam_model.IDPConfigView) *domain.ExternalUser {
displayName := tokens.IDTokenClaims.GetPreferredUsername()
if displayName == "" && tokens.IDTokenClaims.GetEmail() != "" {
displayName = tokens.IDTokenClaims.GetEmail()
@ -228,7 +222,7 @@ func (l *Login) mapTokenToLoginUser(tokens *oidc.Tokens, idpConfig *iam_model.ID
}
}
externalUser := &model.ExternalUser{
externalUser := &domain.ExternalUser{
IDPConfigID: idpConfig.IDPConfigID,
ExternalUserID: tokens.IDTokenClaims.GetSubject(),
PreferredUsername: tokens.IDTokenClaims.GetPreferredUsername(),
@ -246,7 +240,7 @@ func (l *Login) mapTokenToLoginUser(tokens *oidc.Tokens, idpConfig *iam_model.ID
}
return externalUser
}
func (l *Login) mapExternalUserToLoginUser(orgIamPolicy *iam_model.OrgIAMPolicyView, linkingUser *model.ExternalUser, idpConfig *iam_model.IDPConfigView) (*usr_model.User, *usr_model.ExternalIDP) {
func (l *Login) mapExternalUserToLoginUser(orgIamPolicy *iam_model.OrgIAMPolicyView, linkingUser *domain.ExternalUser, idpConfig *iam_model.IDPConfigView) (*domain.Human, *domain.ExternalIDP) {
username := linkingUser.PreferredUsername
switch idpConfig.OIDCUsernameMapping {
case iam_model.OIDCMappingFieldEmail:
@ -262,23 +256,21 @@ func (l *Login) mapExternalUserToLoginUser(orgIamPolicy *iam_model.OrgIAMPolicyV
}
}
user := &usr_model.User{
UserName: username,
Human: &usr_model.Human{
Profile: &usr_model.Profile{
FirstName: linkingUser.FirstName,
LastName: linkingUser.LastName,
PreferredLanguage: linkingUser.PreferredLanguage,
NickName: linkingUser.NickName,
},
Email: &usr_model.Email{
EmailAddress: linkingUser.Email,
IsEmailVerified: linkingUser.IsEmailVerified,
},
human := &domain.Human{
Username: username,
Profile: &domain.Profile{
FirstName: linkingUser.FirstName,
LastName: linkingUser.LastName,
PreferredLanguage: linkingUser.PreferredLanguage,
NickName: linkingUser.NickName,
},
Email: &domain.Email{
EmailAddress: linkingUser.Email,
IsEmailVerified: linkingUser.IsEmailVerified,
},
}
if linkingUser.Phone != "" {
user.Phone = &usr_model.Phone{
human.Phone = &domain.Phone{
PhoneNumber: linkingUser.Phone,
IsPhoneVerified: linkingUser.IsPhoneVerified,
}
@ -292,10 +284,10 @@ func (l *Login) mapExternalUserToLoginUser(orgIamPolicy *iam_model.OrgIAMPolicyV
}
}
externalIDP := &usr_model.ExternalIDP{
IDPConfigID: idpConfig.IDPConfigID,
UserID: linkingUser.ExternalUserID,
DisplayName: displayName,
externalIDP := &domain.ExternalIDP{
IDPConfigID: idpConfig.IDPConfigID,
ExternalUserID: linkingUser.ExternalUserID,
DisplayName: displayName,
}
return user, externalIDP
return human, externalIDP
}

View File

@ -1,20 +1,15 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
"strings"
"github.com/caos/oidc/pkg/oidc"
"github.com/caos/oidc/pkg/rp"
"golang.org/x/text/language"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
caos_errors "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
iam_model "github.com/caos/zitadel/internal/iam/model"
org_model "github.com/caos/zitadel/internal/org/model"
usr_model "github.com/caos/zitadel/internal/user/model"
)
func (l *Login) handleExternalRegister(w http.ResponseWriter, r *http.Request) {
@ -73,20 +68,17 @@ func (l *Login) handleExternalRegisterCallback(w http.ResponseWriter, r *http.Re
l.handleExternalUserRegister(w, r, authReq, idpConfig, userAgentID, tokens)
}
func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, idpConfig *iam_model.IDPConfigView, userAgentID string, tokens *oidc.Tokens) {
func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, idpConfig *iam_model.IDPConfigView, userAgentID string, tokens *oidc.Tokens) {
iam, err := l.authRepo.GetIAM(r.Context())
if err != nil {
l.renderRegisterOption(w, r, authReq, err)
return
}
resourceOwner := iam.GlobalOrgID
member := &org_model.OrgMember{
ObjectRoot: models.ObjectRoot{AggregateID: iam.GlobalOrgID},
Roles: []string{orgProjectCreatorRole},
}
memberRoles := []string{domain.RoleOrgProjectCreator}
if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != iam.GlobalOrgID {
member = nil
memberRoles = nil
resourceOwner = authReq.RequestedOrgID
}
orgIamPolicy, err := l.getOrgIamPolicy(r, resourceOwner)
@ -94,8 +86,8 @@ func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Reques
l.renderRegisterOption(w, r, authReq, err)
return
}
user, externalIDP := l.mapTokenToLoginUserAndExternalIDP(orgIamPolicy, tokens, idpConfig)
_, err = l.authRepo.RegisterExternalUser(setContext(r.Context(), resourceOwner), user, externalIDP, member, resourceOwner)
user, externalIDP := l.mapTokenToLoginHumanAndExternalIDP(orgIamPolicy, tokens, idpConfig)
_, err = l.command.RegisterHuman(setContext(r.Context(), resourceOwner), resourceOwner, user, externalIDP, memberRoles)
if err != nil {
l.renderRegisterOption(w, r, authReq, err)
return
@ -103,7 +95,7 @@ func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Reques
l.renderNextStep(w, r, authReq)
}
func (l *Login) mapTokenToLoginUserAndExternalIDP(orgIamPolicy *iam_model.OrgIAMPolicyView, tokens *oidc.Tokens, idpConfig *iam_model.IDPConfigView) (*usr_model.User, *usr_model.ExternalIDP) {
func (l *Login) mapTokenToLoginHumanAndExternalIDP(orgIamPolicy *iam_model.OrgIAMPolicyView, tokens *oidc.Tokens, idpConfig *iam_model.IDPConfigView) (*domain.Human, *domain.ExternalIDP) {
username := tokens.IDTokenClaims.GetPreferredUsername()
switch idpConfig.OIDCUsernameMapping {
case iam_model.OIDCMappingFieldEmail:
@ -119,23 +111,22 @@ func (l *Login) mapTokenToLoginUserAndExternalIDP(orgIamPolicy *iam_model.OrgIAM
}
}
user := &usr_model.User{
UserName: username,
Human: &usr_model.Human{
Profile: &usr_model.Profile{
FirstName: tokens.IDTokenClaims.GetGivenName(),
LastName: tokens.IDTokenClaims.GetFamilyName(),
PreferredLanguage: language.Tag(tokens.IDTokenClaims.GetLocale()),
NickName: tokens.IDTokenClaims.GetNickname(),
},
Email: &usr_model.Email{
EmailAddress: tokens.IDTokenClaims.GetEmail(),
IsEmailVerified: tokens.IDTokenClaims.IsEmailVerified(),
},
human := &domain.Human{
Username: username,
Profile: &domain.Profile{
FirstName: tokens.IDTokenClaims.GetGivenName(),
LastName: tokens.IDTokenClaims.GetFamilyName(),
PreferredLanguage: tokens.IDTokenClaims.GetLocale(),
NickName: tokens.IDTokenClaims.GetNickname(),
},
Email: &domain.Email{
EmailAddress: tokens.IDTokenClaims.GetEmail(),
IsEmailVerified: tokens.IDTokenClaims.IsEmailVerified(),
},
}
if tokens.IDTokenClaims.GetPhoneNumber() != "" {
user.Phone = &usr_model.Phone{
human.Phone = &domain.Phone{
PhoneNumber: tokens.IDTokenClaims.GetPhoneNumber(),
IsPhoneVerified: tokens.IDTokenClaims.IsPhoneNumberVerified(),
}
@ -149,10 +140,10 @@ func (l *Login) mapTokenToLoginUserAndExternalIDP(orgIamPolicy *iam_model.OrgIAM
}
}
externalIDP := &usr_model.ExternalIDP{
IDPConfigID: idpConfig.IDPConfigID,
UserID: tokens.IDTokenClaims.GetSubject(),
DisplayName: displayName,
externalIDP := &domain.ExternalIDP{
IDPConfigID: idpConfig.IDPConfigID,
ExternalUserID: tokens.IDTokenClaims.GetSubject(),
DisplayName: displayName,
}
return user, externalIDP
return human, externalIDP
}

View File

@ -1,10 +1,10 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/errors"
)
@ -58,18 +58,18 @@ func (l *Login) handleInitPasswordCheck(w http.ResponseWriter, r *http.Request)
l.checkPWCode(w, r, authReq, data, nil)
}
func (l *Login) checkPWCode(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *initPasswordFormData, err error) {
func (l *Login) checkPWCode(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, data *initPasswordFormData, err error) {
if data.Password != data.PasswordConfirm {
err := errors.ThrowInvalidArgument(nil, "VIEW-KaGue", "Errors.User.Password.ConfirmationWrong")
l.renderInitPassword(w, r, authReq, data.UserID, data.Code, err)
return
}
userOrg := login
userOrg := ""
if authReq != nil {
userOrg = authReq.UserOrgID
}
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
err = l.authRepo.SetPassword(setContext(r.Context(), userOrg), data.UserID, data.Code, data.Password, userAgentID)
err = l.command.SetPassword(setContext(r.Context(), userOrg), userOrg, data.UserID, data.Code, data.Password, userAgentID)
if err != nil {
l.renderInitPassword(w, r, authReq, data.UserID, "", err)
return
@ -77,16 +77,25 @@ func (l *Login) checkPWCode(w http.ResponseWriter, r *http.Request, authReq *mod
l.renderInitPasswordDone(w, r, authReq)
}
func (l *Login) resendPasswordSet(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
func (l *Login) resendPasswordSet(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
if authReq == nil {
l.renderError(w, r, nil, errors.ThrowInternal(nil, "LOGIN-8sn7s", "Errors.AuthRequest.NotFound"))
return
}
userOrg := login
if authReq != nil {
userOrg = authReq.UserOrgID
}
err := l.authRepo.RequestPasswordReset(setContext(r.Context(), userOrg), authReq.LoginName)
user, err := l.authRepo.UserByLoginName(setContext(r.Context(), userOrg), authReq.LoginName)
if err != nil {
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
return
}
err = l.command.RequestSetPassword(setContext(r.Context(), userOrg), user.ID, user.ResourceOwner, domain.NotificationTypeEmail)
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
}
func (l *Login) renderInitPassword(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, userID, code string, err error) {
func (l *Login) renderInitPassword(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID, code string, err error) {
var errType, errMessage string
if err != nil {
errMessage = l.getErrorMessage(r, err)
@ -121,7 +130,7 @@ func (l *Login) renderInitPassword(w http.ResponseWriter, r *http.Request, authR
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplInitPassword], data, nil)
}
func (l *Login) renderInitPasswordDone(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
func (l *Login) renderInitPasswordDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
data := l.getUserData(r, authReq, "Password Init Done", "", "")
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplInitPasswordDone], data, nil)
}

View File

@ -1,10 +1,10 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
"strconv"
"github.com/caos/zitadel/internal/auth_request/model"
caos_errs "github.com/caos/zitadel/internal/errors"
)
@ -62,17 +62,17 @@ func (l *Login) handleInitUserCheck(w http.ResponseWriter, r *http.Request) {
l.checkUserInitCode(w, r, authReq, data, nil)
}
func (l *Login) checkUserInitCode(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *initUserFormData, err error) {
func (l *Login) checkUserInitCode(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, data *initUserFormData, err error) {
if data.Password != data.PasswordConfirm {
err := caos_errs.ThrowInvalidArgument(nil, "VIEW-fsdfd", "Errors.User.Password.ConfirmationWrong")
l.renderInitUser(w, r, authReq, data.UserID, data.Code, data.PasswordSet, err)
return
}
userOrgID := login
userOrgID := ""
if authReq != nil {
userOrgID = authReq.UserOrgID
}
err = l.authRepo.VerifyInitCode(setContext(r.Context(), userOrgID), data.UserID, data.Code, data.Password)
err = l.command.HumanVerifyInitCode(setContext(r.Context(), userOrgID), data.UserID, userOrgID, data.Code, data.Password)
if err != nil {
l.renderInitUser(w, r, authReq, data.UserID, "", data.PasswordSet, err)
return
@ -80,16 +80,16 @@ func (l *Login) checkUserInitCode(w http.ResponseWriter, r *http.Request, authRe
l.renderInitUserDone(w, r, authReq)
}
func (l *Login) resendUserInit(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, userID string, showPassword bool) {
userOrgID := login
func (l *Login) resendUserInit(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID string, showPassword bool) {
userOrgID := ""
if authReq != nil {
userOrgID = authReq.UserOrgID
}
err := l.authRepo.ResendInitVerificationMail(setContext(r.Context(), userOrgID), userID)
err := l.command.ResendInitialMail(setContext(r.Context(), userOrgID), userID, "", userOrgID)
l.renderInitUser(w, r, authReq, userID, "", showPassword, err)
}
func (l *Login) renderInitUser(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, userID, code string, passwordSet bool, err error) {
func (l *Login) renderInitUser(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID, code string, passwordSet bool, err error) {
var errType, errMessage string
if err != nil {
errMessage = l.getErrorMessage(r, err)
@ -124,7 +124,7 @@ func (l *Login) renderInitUser(w http.ResponseWriter, r *http.Request, authReq *
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplInitUser], data, nil)
}
func (l *Login) renderInitUserDone(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
func (l *Login) renderInitUserDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
data := l.getUserData(r, authReq, "User Init Done", "", "")
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplInitUserDone], data, nil)
}

View File

@ -2,22 +2,21 @@ package handler
import (
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
"github.com/caos/zitadel/internal/auth_request/model"
)
const (
tmplLinkUsersDone = "linkusersdone"
)
func (l *Login) linkUsers(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
func (l *Login) linkUsers(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
err = l.authRepo.LinkExternalUsers(setContext(r.Context(), authReq.UserOrgID), authReq.ID, userAgentID, model.BrowserInfoFromRequest(r))
err = l.authRepo.LinkExternalUsers(setContext(r.Context(), authReq.UserOrgID), authReq.ID, userAgentID, domain.BrowserInfoFromRequest(r))
l.renderLinkUsersDone(w, r, authReq, err)
}
func (l *Login) renderLinkUsersDone(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
func (l *Login) renderLinkUsersDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
var errType, errMessage string
data := l.getUserData(r, authReq, "Linking Users Done", errType, errMessage)
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplLinkUsersDone], data, nil)

View File

@ -1,10 +1,10 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
)
const (
@ -62,7 +62,7 @@ func (l *Login) handleLoginNameCheck(w http.ResponseWriter, r *http.Request) {
l.renderNextStep(w, r, authReq)
}
func (l *Login) renderLogin(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
func (l *Login) renderLogin(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
var errType, errMessage string
if err != nil {
errMessage = l.getErrorMessage(r, err)

View File

@ -1,9 +1,8 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
"github.com/caos/zitadel/internal/auth_request/model"
)
const (
@ -47,21 +46,21 @@ func (l *Login) handleMailVerificationCheck(w http.ResponseWriter, r *http.Reque
l.checkMailCode(w, r, authReq, data.UserID, data.Code)
return
}
userOrg := login
userOrg := ""
if authReq != nil {
userOrg = authReq.UserOrgID
}
err = l.authRepo.ResendEmailVerificationMail(setContext(r.Context(), userOrg), data.UserID)
err = l.command.CreateHumanEmailVerificationCode(setContext(r.Context(), userOrg), data.UserID, userOrg)
l.renderMailVerification(w, r, authReq, data.UserID, err)
}
func (l *Login) checkMailCode(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, userID, code string) {
userOrg := login
func (l *Login) checkMailCode(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID, code string) {
userOrg := ""
if authReq != nil {
userID = authReq.UserID
userOrg = authReq.UserOrgID
}
err := l.authRepo.VerifyEmail(setContext(r.Context(), userOrg), userID, code)
err := l.command.VerifyHumanEmail(setContext(r.Context(), userOrg), userID, code, userOrg)
if err != nil {
l.renderMailVerification(w, r, authReq, userID, err)
return
@ -69,7 +68,7 @@ func (l *Login) checkMailCode(w http.ResponseWriter, r *http.Request, authReq *m
l.renderMailVerified(w, r, authReq)
}
func (l *Login) renderMailVerification(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, userID string, err error) {
func (l *Login) renderMailVerification(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID string, err error) {
var errType, errMessage string
if err != nil {
errMessage = l.getErrorMessage(r, err)
@ -85,7 +84,7 @@ func (l *Login) renderMailVerification(w http.ResponseWriter, r *http.Request, a
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplMailVerification], data, nil)
}
func (l *Login) renderMailVerified(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
func (l *Login) renderMailVerified(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
data := mailVerificationData{
baseData: l.getBaseData(r, authReq, "Mail Verified", "", ""),
profileData: l.getProfileData(authReq),

View File

@ -1,9 +1,8 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
"github.com/caos/zitadel/internal/auth_request/model"
)
const (
@ -13,7 +12,7 @@ const (
type mfaInitDoneData struct {
}
func (l *Login) renderMFAInitDone(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *mfaDoneData) {
func (l *Login) renderMFAInitDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, data *mfaDoneData) {
var errType, errMessage string
data.baseData = l.getBaseData(r, authReq, "MFA Init Done", errType, errMessage)
data.profileData = l.getProfileData(authReq)

View File

@ -2,11 +2,11 @@ package handler
import (
"encoding/base64"
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
user_model "github.com/caos/zitadel/internal/user/model"
)
const (
@ -18,11 +18,11 @@ type u2fInitData struct {
MFAType model.MFAType
}
func (l *Login) renderRegisterU2F(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
func (l *Login) renderRegisterU2F(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
var errType, errMessage, credentialData string
var u2f *user_model.WebAuthNToken
var u2f *domain.WebAuthNToken
if err == nil {
u2f, err = l.authRepo.AddMFAU2F(setContext(r.Context(), authReq.UserOrgID), authReq.UserID)
u2f, err = l.command.HumanAddU2FSetup(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID, true)
}
if err != nil {
errMessage = l.getErrorMessage(r, err)
@ -54,12 +54,12 @@ func (l *Login) handleRegisterU2F(w http.ResponseWriter, r *http.Request) {
}
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
if err = l.authRepo.VerifyMFAU2FSetup(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, data.Name, userAgentID, credData); err != nil {
if err = l.command.HumanVerifyU2FSetup(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID, data.Name, userAgentID, credData); err != nil {
l.renderRegisterU2F(w, r, authReq, err)
return
}
done := &mfaDoneData{
MFAType: model.MFATypeU2F,
MFAType: domain.MFATypeU2F,
}
l.renderMFAInitDone(w, r, authReq, done)
}

View File

@ -2,13 +2,13 @@ package handler
import (
"bytes"
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
svg "github.com/ajstarks/svgo"
"github.com/boombuler/barcode/qr"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/qrcode"
)
@ -17,10 +17,10 @@ const (
)
type mfaInitVerifyData struct {
MFAType model.MFAType `schema:"mfaType"`
Code string `schema:"code"`
URL string `schema:"url"`
Secret string `schema:"secret"`
MFAType domain.MFAType `schema:"mfaType"`
Code string `schema:"code"`
URL string `schema:"url"`
Secret string `schema:"secret"`
}
func (l *Login) handleMFAInitVerify(w http.ResponseWriter, r *http.Request) {
@ -32,7 +32,7 @@ func (l *Login) handleMFAInitVerify(w http.ResponseWriter, r *http.Request) {
}
var verifyData *mfaVerifyData
switch data.MFAType {
case model.MFATypeOTP:
case domain.MFATypeOTP:
verifyData = l.handleOTPVerify(w, r, authReq, data)
}
@ -47,9 +47,9 @@ func (l *Login) handleMFAInitVerify(w http.ResponseWriter, r *http.Request) {
l.renderMFAInitDone(w, r, authReq, done)
}
func (l *Login) handleOTPVerify(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *mfaInitVerifyData) *mfaVerifyData {
func (l *Login) handleOTPVerify(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, data *mfaInitVerifyData) *mfaVerifyData {
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
err := l.authRepo.VerifyMFAOTPSetup(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, data.Code, userAgentID)
err := l.command.HumanCheckMFAOTPSetup(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, data.Code, userAgentID, authReq.UserOrgID)
if err == nil {
return nil
}
@ -64,14 +64,14 @@ func (l *Login) handleOTPVerify(w http.ResponseWriter, r *http.Request, authReq
return mfadata
}
func (l *Login) renderMFAInitVerify(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *mfaVerifyData, err error) {
func (l *Login) renderMFAInitVerify(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, data *mfaVerifyData, err error) {
var errType, errMessage string
if err != nil {
errMessage = l.getErrorMessage(r, err)
}
data.baseData = l.getBaseData(r, authReq, "MFA Init Verify", errType, errMessage)
data.profileData = l.getProfileData(authReq)
if data.MFAType == model.MFATypeOTP {
if data.MFAType == domain.MFATypeOTP {
code, err := generateQrCode(data.otpData.Url)
if err == nil {
data.otpData.QrCode = code

View File

@ -1,9 +1,9 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
"github.com/caos/zitadel/internal/auth_request/model"
caos_errs "github.com/caos/zitadel/internal/errors"
)
@ -12,8 +12,8 @@ const (
)
type mfaPromptData struct {
MFAProvider model.MFAType `schema:"provider"`
Skip bool `schema:"skip"`
MFAProvider domain.MFAType `schema:"provider"`
Skip bool `schema:"skip"`
}
func (l *Login) handleMFAPrompt(w http.ResponseWriter, r *http.Request) {
@ -29,7 +29,7 @@ func (l *Login) handleMFAPrompt(w http.ResponseWriter, r *http.Request) {
l.handleMFACreation(w, r, authReq, mfaVerifyData)
return
}
err = l.authRepo.SkipMFAInit(setContext(r.Context(), authReq.UserOrgID), authReq.UserID)
err = l.command.HumanSkipMFAInit(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID)
if err != nil {
l.renderError(w, r, authReq, err)
return
@ -48,7 +48,7 @@ func (l *Login) handleMFAPromptSelection(w http.ResponseWriter, r *http.Request)
l.renderNextStep(w, r, authReq)
}
func (l *Login) renderMFAPrompt(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, mfaPromptData *model.MFAPromptStep, err error) {
func (l *Login) renderMFAPrompt(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, mfaPromptData *domain.MFAPromptStep, err error) {
var errType, errMessage string
if err != nil {
errMessage = l.getErrorMessage(r, err)
@ -76,20 +76,20 @@ func (l *Login) renderMFAPrompt(w http.ResponseWriter, r *http.Request, authReq
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplMFAPrompt], data, nil)
}
func (l *Login) handleMFACreation(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *mfaVerifyData) {
func (l *Login) handleMFACreation(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, data *mfaVerifyData) {
switch data.MFAType {
case model.MFATypeOTP:
case domain.MFATypeOTP:
l.handleOTPCreation(w, r, authReq, data)
return
case model.MFATypeU2F:
case domain.MFATypeU2F:
l.renderRegisterU2F(w, r, authReq, nil)
return
}
l.renderError(w, r, authReq, caos_errs.ThrowPreconditionFailed(nil, "APP-Or3HO", "Errors.User.MFA.NoProviders"))
}
func (l *Login) handleOTPCreation(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *mfaVerifyData) {
otp, err := l.authRepo.AddMFAOTP(setContext(r.Context(), authReq.UserOrgID), authReq.UserID)
func (l *Login) handleOTPCreation(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, data *mfaVerifyData) {
otp, err := l.command.AddHumanOTP(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID)
if err != nil {
l.renderError(w, r, authReq, err)
return

View File

@ -1,6 +1,7 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
@ -25,7 +26,7 @@ func (l *Login) handleMFAVerify(w http.ResponseWriter, r *http.Request) {
}
if data.MFAType == model.MFATypeOTP {
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
err = l.authRepo.VerifyMFAOTP(setContext(r.Context(), authReq.UserOrgID), authReq.ID, authReq.UserID, data.Code, userAgentID, model.BrowserInfoFromRequest(r))
err = l.authRepo.VerifyMFAOTP(setContext(r.Context(), authReq.UserOrgID), authReq.ID, authReq.UserID, authReq.UserOrgID, data.Code, userAgentID, domain.BrowserInfoFromRequest(r))
}
if err != nil {
l.renderError(w, r, authReq, err)
@ -34,7 +35,7 @@ func (l *Login) handleMFAVerify(w http.ResponseWriter, r *http.Request) {
l.renderNextStep(w, r, authReq)
}
func (l *Login) renderMFAVerify(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, verificationStep *model.MFAVerificationStep, err error) {
func (l *Login) renderMFAVerify(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, verificationStep *domain.MFAVerificationStep, err error) {
if verificationStep == nil {
l.renderError(w, r, authReq, err)
return
@ -43,7 +44,7 @@ func (l *Login) renderMFAVerify(w http.ResponseWriter, r *http.Request, authReq
l.renderMFAVerifySelected(w, r, authReq, verificationStep, provider, err)
}
func (l *Login) renderMFAVerifySelected(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, verificationStep *model.MFAVerificationStep, selectedProvider model.MFAType, err error) {
func (l *Login) renderMFAVerifySelected(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, verificationStep *domain.MFAVerificationStep, selectedProvider domain.MFAType, err error) {
var errType, errMessage string
if err != nil {
errMessage = l.getErrorMessage(r, err)
@ -54,12 +55,12 @@ func (l *Login) renderMFAVerifySelected(w http.ResponseWriter, r *http.Request,
return
}
switch selectedProvider {
case model.MFATypeU2F:
l.renderU2FVerification(w, r, authReq, removeSelectedProviderFromList(verificationStep.MFAProviders, model.MFATypeU2F), nil)
case domain.MFATypeU2F:
l.renderU2FVerification(w, r, authReq, removeSelectedProviderFromList(verificationStep.MFAProviders, domain.MFATypeU2F), nil)
return
case model.MFATypeOTP:
data.MFAProviders = removeSelectedProviderFromList(verificationStep.MFAProviders, model.MFATypeOTP)
data.SelectedMFAProvider = model.MFATypeOTP
case domain.MFATypeOTP:
data.MFAProviders = removeSelectedProviderFromList(verificationStep.MFAProviders, domain.MFATypeOTP)
data.SelectedMFAProvider = domain.MFATypeOTP
default:
l.renderError(w, r, authReq, err)
return
@ -67,7 +68,7 @@ func (l *Login) renderMFAVerifySelected(w http.ResponseWriter, r *http.Request,
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplMFAVerify], data, nil)
}
func removeSelectedProviderFromList(providers []model.MFAType, selected model.MFAType) []model.MFAType {
func removeSelectedProviderFromList(providers []domain.MFAType, selected domain.MFAType) []domain.MFAType {
for i := len(providers) - 1; i >= 0; i-- {
if providers[i] == selected {
copy(providers[i:], providers[i+1:])

View File

@ -2,11 +2,10 @@ package handler
import (
"encoding/base64"
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
user_model "github.com/caos/zitadel/internal/user/model"
)
const (
@ -15,21 +14,21 @@ const (
type mfaU2FData struct {
webAuthNData
MFAProviders []model.MFAType
SelectedProvider model.MFAType
MFAProviders []domain.MFAType
SelectedProvider domain.MFAType
}
type mfaU2FFormData struct {
webAuthNFormData
SelectedProvider model.MFAType `schema:"provider"`
SelectedProvider domain.MFAType `schema:"provider"`
}
func (l *Login) renderU2FVerification(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, providers []model.MFAType, err error) {
func (l *Login) renderU2FVerification(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, providers []domain.MFAType, err error) {
var errType, errMessage, credentialData string
var webAuthNLogin *user_model.WebAuthNLogin
var webAuthNLogin *domain.WebAuthNLogin
if err == nil {
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
webAuthNLogin, err = l.authRepo.BeginMFAU2FLogin(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.ID, userAgentID)
webAuthNLogin, err = l.authRepo.BeginMFAU2FLogin(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID, authReq.ID, userAgentID)
}
if err != nil {
errMessage = l.getErrorMessage(r, err)
@ -55,7 +54,7 @@ func (l *Login) handleU2FVerification(w http.ResponseWriter, r *http.Request) {
l.renderError(w, r, authReq, err)
return
}
step, ok := authReq.PossibleSteps[0].(*model.MFAVerificationStep)
step, ok := authReq.PossibleSteps[0].(*domain.MFAVerificationStep)
if !ok {
l.renderError(w, r, authReq, err)
return
@ -70,7 +69,7 @@ func (l *Login) handleU2FVerification(w http.ResponseWriter, r *http.Request) {
return
}
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
err = l.authRepo.VerifyMFAU2F(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.ID, userAgentID, credData, model.BrowserInfoFromRequest(r))
err = l.authRepo.VerifyMFAU2F(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID, authReq.ID, userAgentID, credData, domain.BrowserInfoFromRequest(r))
if err != nil {
l.renderU2FVerification(w, r, authReq, step.MFAProviders, err)
return

View File

@ -1,10 +1,10 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
)
const (
@ -15,7 +15,7 @@ type passwordFormData struct {
Password string `schema:"password"`
}
func (l *Login) renderPassword(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
func (l *Login) renderPassword(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
var errType, errMessage string
if err != nil {
errMessage = l.getErrorMessage(r, err)
@ -32,7 +32,7 @@ func (l *Login) handlePasswordCheck(w http.ResponseWriter, r *http.Request) {
return
}
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
err = l.authRepo.VerifyPassword(setContext(r.Context(), authReq.UserOrgID), authReq.ID, authReq.UserID, data.Password, userAgentID, model.BrowserInfoFromRequest(r))
err = l.authRepo.VerifyPassword(setContext(r.Context(), authReq.UserOrgID), authReq.ID, authReq.UserID, authReq.UserOrgID, data.Password, userAgentID, domain.BrowserInfoFromRequest(r))
if err != nil {
l.renderPassword(w, r, authReq, err)
return

View File

@ -1,9 +1,8 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
"github.com/caos/zitadel/internal/auth_request/model"
)
const (
@ -16,11 +15,16 @@ func (l *Login) handlePasswordReset(w http.ResponseWriter, r *http.Request) {
l.renderError(w, r, authReq, err)
return
}
err = l.authRepo.RequestPasswordReset(setContext(r.Context(), authReq.UserOrgID), authReq.LoginName)
user, err := l.authRepo.UserByLoginName(setContext(r.Context(), authReq.UserOrgID), authReq.LoginName)
if err != nil {
l.renderPasswordResetDone(w, r, authReq, err)
return
}
err = l.command.RequestSetPassword(setContext(r.Context(), authReq.UserOrgID), user.ID, authReq.UserOrgID, domain.NotificationTypeEmail)
l.renderPasswordResetDone(w, r, authReq, err)
}
func (l *Login) renderPasswordResetDone(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
func (l *Login) renderPasswordResetDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
var errType, errMessage string
if err != nil {
errMessage = l.getErrorMessage(r, err)

View File

@ -2,11 +2,10 @@ package handler
import (
"encoding/base64"
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
user_model "github.com/caos/zitadel/internal/user/model"
)
const (
@ -23,12 +22,12 @@ type passwordlessFormData struct {
PasswordLogin bool `schema:"passwordlogin"`
}
func (l *Login) renderPasswordlessVerification(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
func (l *Login) renderPasswordlessVerification(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
var errType, errMessage, credentialData string
var webAuthNLogin *user_model.WebAuthNLogin
var webAuthNLogin *domain.WebAuthNLogin
if err == nil {
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
webAuthNLogin, err = l.authRepo.BeginPasswordlessLogin(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.ID, userAgentID)
webAuthNLogin, err = l.authRepo.BeginPasswordlessLogin(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID, authReq.ID, userAgentID)
}
if err != nil {
errMessage = l.getErrorMessage(r, err)
@ -67,7 +66,7 @@ func (l *Login) handlePasswordlessVerification(w http.ResponseWriter, r *http.Re
return
}
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
err = l.authRepo.VerifyPasswordless(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.ID, userAgentID, credData, model.BrowserInfoFromRequest(r))
err = l.authRepo.VerifyPasswordless(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID, authReq.ID, userAgentID, credData, domain.BrowserInfoFromRequest(r))
if err != nil {
l.renderPasswordlessVerification(w, r, authReq, err)
return

View File

@ -1,20 +1,16 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
"golang.org/x/text/language"
"github.com/caos/zitadel/internal/auth_request/model"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
org_model "github.com/caos/zitadel/internal/org/model"
usr_model "github.com/caos/zitadel/internal/user/model"
)
const (
tmplRegister = "register"
orgProjectCreatorRole = "ORG_PROJECT_CREATOR"
tmplRegister = "register"
)
type registerFormData struct {
@ -68,15 +64,13 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) {
}
resourceOwner := iam.GlobalOrgID
member := &org_model.OrgMember{
ObjectRoot: models.ObjectRoot{AggregateID: iam.GlobalOrgID},
Roles: []string{orgProjectCreatorRole},
}
memberRoles := []string{domain.RoleOrgProjectCreator}
if authRequest.RequestedOrgID != "" && authRequest.RequestedOrgID != iam.GlobalOrgID {
member = nil
memberRoles = nil
resourceOwner = authRequest.RequestedOrgID
}
user, err := l.authRepo.Register(setContext(r.Context(), resourceOwner), data.toUserModel(), member, resourceOwner)
user, err := l.command.RegisterHuman(setContext(r.Context(), resourceOwner), resourceOwner, data.toHumanDomain(), nil, memberRoles)
if err != nil {
l.renderRegister(w, r, authRequest, data, err)
return
@ -89,7 +83,7 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) {
l.renderNextStep(w, r, authRequest)
}
func (l *Login) renderRegister(w http.ResponseWriter, r *http.Request, authRequest *model.AuthRequest, formData *registerFormData, err error) {
func (l *Login) renderRegister(w http.ResponseWriter, r *http.Request, authRequest *domain.AuthRequest, formData *registerFormData, err error) {
var errType, errMessage string
if err != nil {
errMessage = l.getErrorMessage(r, err)
@ -142,21 +136,19 @@ func (l *Login) renderRegister(w http.ResponseWriter, r *http.Request, authReque
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplRegister], data, funcs)
}
func (d registerFormData) toUserModel() *usr_model.User {
return &usr_model.User{
Human: &usr_model.Human{
Profile: &usr_model.Profile{
FirstName: d.Firstname,
LastName: d.Lastname,
PreferredLanguage: language.Make(d.Language),
Gender: usr_model.Gender(d.Gender),
},
Password: &usr_model.Password{
SecretString: d.Password,
},
Email: &usr_model.Email{
EmailAddress: d.Email,
},
func (d registerFormData) toHumanDomain() *domain.Human {
return &domain.Human{
Profile: &domain.Profile{
FirstName: d.Firstname,
LastName: d.Lastname,
PreferredLanguage: language.Make(d.Language),
Gender: domain.Gender(d.Gender),
},
Password: &domain.Password{
SecretString: d.Password,
},
Email: &domain.Email{
EmailAddress: d.Email,
},
}
}

View File

@ -1,7 +1,7 @@
package handler
import (
"github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
)
@ -27,7 +27,7 @@ func (l *Login) handleRegisterOption(w http.ResponseWriter, r *http.Request) {
l.renderRegisterOption(w, r, authRequest, nil)
}
func (l *Login) renderRegisterOption(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
func (l *Login) renderRegisterOption(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
var errType, errMessage string
if err != nil {
errMessage = l.getErrorMessage(r, err)

View File

@ -1,10 +1,10 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
auth_model "github.com/caos/zitadel/internal/auth/model"
"github.com/caos/zitadel/internal/auth_request/model"
caos_errs "github.com/caos/zitadel/internal/errors"
org_model "github.com/caos/zitadel/internal/org/model"
usr_model "github.com/caos/zitadel/internal/user/model"
@ -78,7 +78,7 @@ func (l *Login) handleRegisterOrgCheck(w http.ResponseWriter, r *http.Request) {
l.renderNextStep(w, r, authRequest)
}
func (l *Login) renderRegisterOrg(w http.ResponseWriter, r *http.Request, authRequest *model.AuthRequest, formData *registerOrgFormData, err error) {
func (l *Login) renderRegisterOrg(w http.ResponseWriter, r *http.Request, authRequest *domain.AuthRequest, formData *registerOrgFormData, err error) {
var errType, errMessage string
if err != nil {
errMessage = l.getErrorMessage(r, err)

View File

@ -3,6 +3,7 @@ package handler
import (
"errors"
"fmt"
"github.com/caos/zitadel/internal/v2/domain"
"html/template"
"net/http"
"path"
@ -15,7 +16,6 @@ import (
"github.com/caos/zitadel/internal/auth_request/model"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/i18n"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/renderer"
)
@ -153,7 +153,7 @@ func CreateRenderer(pathPrefix string, staticDir http.FileSystem, cookieName str
"hasExternalLogin": func() bool {
return false
},
"idpProviderClass": func(stylingType iam_model.IDPStylingType) string {
"idpProviderClass": func(stylingType domain.IDPConfigStylingType) string {
return stylingType.GetCSSClass()
},
}
@ -167,7 +167,7 @@ func CreateRenderer(pathPrefix string, staticDir http.FileSystem, cookieName str
return r
}
func (l *Login) renderNextStep(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
func (l *Login) renderNextStep(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
authReq, err := l.authRepo.AuthRequestByID(r.Context(), authReq.ID, userAgentID)
if err != nil {
@ -181,7 +181,7 @@ func (l *Login) renderNextStep(w http.ResponseWriter, r *http.Request, authReq *
l.chooseNextStep(w, r, authReq, 0, nil)
}
func (l *Login) renderError(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
func (l *Login) renderError(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
if authReq == nil || len(authReq.PossibleSteps) == 0 {
l.renderInternalError(w, r, authReq, caos_errs.ThrowInternal(err, "APP-OVOiT", "no possible steps"))
return
@ -189,54 +189,54 @@ func (l *Login) renderError(w http.ResponseWriter, r *http.Request, authReq *mod
l.chooseNextStep(w, r, authReq, 0, err)
}
func (l *Login) chooseNextStep(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, stepNumber int, err error) {
func (l *Login) chooseNextStep(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, stepNumber int, err error) {
switch step := authReq.PossibleSteps[stepNumber].(type) {
case *model.LoginStep:
case *domain.LoginStep:
if len(authReq.PossibleSteps) > 1 {
l.chooseNextStep(w, r, authReq, 1, err)
return
}
l.renderLogin(w, r, authReq, err)
case *model.SelectUserStep:
case *domain.SelectUserStep:
l.renderUserSelection(w, r, authReq, step)
case *model.InitPasswordStep:
case *domain.InitPasswordStep:
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
case *model.PasswordStep:
case *domain.PasswordStep:
l.renderPassword(w, r, authReq, nil)
case *model.PasswordlessStep:
case *domain.PasswordlessStep:
l.renderPasswordlessVerification(w, r, authReq, nil)
case *model.MFAVerificationStep:
case *domain.MFAVerificationStep:
l.renderMFAVerify(w, r, authReq, step, err)
case *model.RedirectToCallbackStep:
case *domain.RedirectToCallbackStep:
if len(authReq.PossibleSteps) > 1 {
l.chooseNextStep(w, r, authReq, 1, err)
return
}
l.redirectToCallback(w, r, authReq)
case *model.ChangePasswordStep:
case *domain.ChangePasswordStep:
l.renderChangePassword(w, r, authReq, err)
case *model.VerifyEMailStep:
case *domain.VerifyEMailStep:
l.renderMailVerification(w, r, authReq, "", err)
case *model.MFAPromptStep:
case *domain.MFAPromptStep:
l.renderMFAPrompt(w, r, authReq, step, err)
case *model.InitUserStep:
case *domain.InitUserStep:
l.renderInitUser(w, r, authReq, "", "", step.PasswordSet, nil)
case *model.ChangeUsernameStep:
case *domain.ChangeUsernameStep:
l.renderChangeUsername(w, r, authReq, nil)
case *model.LinkUsersStep:
case *domain.LinkUsersStep:
l.linkUsers(w, r, authReq, err)
case *model.ExternalNotFoundOptionStep:
case *domain.ExternalNotFoundOptionStep:
l.renderExternalNotFoundOption(w, r, authReq, err)
case *model.ExternalLoginStep:
case *domain.ExternalLoginStep:
l.handleExternalLoginStep(w, r, authReq, step.SelectedIDPConfigID)
case *model.GrantRequiredStep:
case *domain.GrantRequiredStep:
l.renderInternalError(w, r, authReq, caos_errs.ThrowPreconditionFailed(nil, "APP-asb43", "Errors.User.GrantRequired"))
default:
l.renderInternalError(w, r, authReq, caos_errs.ThrowInternal(nil, "APP-ds3QF", "step no possible"))
}
}
func (l *Login) renderInternalError(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
func (l *Login) renderInternalError(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
var msg string
if err != nil {
msg = err.Error()
@ -245,7 +245,7 @@ func (l *Login) renderInternalError(w http.ResponseWriter, r *http.Request, auth
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplError], data, nil)
}
func (l *Login) getUserData(r *http.Request, authReq *model.AuthRequest, title string, errType, errMessage string) userData {
func (l *Login) getUserData(r *http.Request, authReq *domain.AuthRequest, title string, errType, errMessage string) userData {
userData := userData{
baseData: l.getBaseData(r, authReq, title, errType, errMessage),
profileData: l.getProfileData(authReq),
@ -256,7 +256,7 @@ func (l *Login) getUserData(r *http.Request, authReq *model.AuthRequest, title s
return userData
}
func (l *Login) getBaseData(r *http.Request, authReq *model.AuthRequest, title string, errType, errMessage string) baseData {
func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, title string, errType, errMessage string) baseData {
baseData := baseData{
errorData: errorData{
ErrType: errType,
@ -279,7 +279,7 @@ func (l *Login) getBaseData(r *http.Request, authReq *model.AuthRequest, title s
return baseData
}
func (l *Login) getProfileData(authReq *model.AuthRequest) profileData {
func (l *Login) getProfileData(authReq *domain.AuthRequest) profileData {
var loginName, displayName string
if authReq != nil {
loginName = authReq.LoginName
@ -309,7 +309,7 @@ func (l *Login) getThemeMode(r *http.Request) string {
return "" //TODO: impl
}
func (l *Login) getOrgID(authReq *model.AuthRequest) string {
func (l *Login) getOrgID(authReq *domain.AuthRequest) string {
if authReq == nil {
return ""
}
@ -319,14 +319,14 @@ func (l *Login) getOrgID(authReq *model.AuthRequest) string {
return authReq.UserOrgID
}
func (l *Login) getOrgName(authReq *model.AuthRequest) string {
func (l *Login) getOrgName(authReq *domain.AuthRequest) string {
if authReq == nil {
return ""
}
return authReq.RequestedOrgName
}
func getRequestID(authReq *model.AuthRequest, r *http.Request) string {
func getRequestID(authReq *domain.AuthRequest, r *http.Request) string {
if authReq != nil {
return authReq.ID
}
@ -357,8 +357,8 @@ type baseData struct {
AuthReqID string
CSRF template.HTML
Nonce string
LoginPolicy *iam_model.LoginPolicyView
IDPProviders []*iam_model.IDPProviderView
LoginPolicy *domain.LoginPolicy
IDPProviders []*domain.IDPProvider
}
type errorData struct {
@ -370,8 +370,8 @@ type userData struct {
baseData
profileData
PasswordChecked string
MFAProviders []model.MFAType
SelectedMFAProvider model.MFAType
MFAProviders []domain.MFAType
SelectedMFAProvider domain.MFAType
Linking bool
}
@ -393,28 +393,28 @@ type passwordData struct {
type userSelectionData struct {
baseData
Users []model.UserSelection
Users []domain.UserSelection
Linking bool
}
type mfaData struct {
baseData
profileData
MFAProviders []model.MFAType
MFAProviders []domain.MFAType
MFARequired bool
}
type mfaVerifyData struct {
baseData
profileData
MFAType model.MFAType
MFAType domain.MFAType
otpData
}
type mfaDoneData struct {
baseData
profileData
MFAType model.MFAType
MFAType domain.MFAType
}
type otpData struct {

View File

@ -1,10 +1,10 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
)
const (
@ -15,7 +15,7 @@ type userSelectionFormData struct {
UserID string `schema:"userID"`
}
func (l *Login) renderUserSelection(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, selectionData *model.SelectUserStep) {
func (l *Login) renderUserSelection(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, selectionData *domain.SelectUserStep) {
data := userSelectionData{
baseData: l.getBaseData(r, authReq, "Select User", "", ""),
Users: selectionData.Users,

View File

@ -1,9 +1,8 @@
package handler
import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http"
"github.com/caos/zitadel/internal/auth_request/model"
)
const (
@ -15,7 +14,7 @@ type changeUsernameData struct {
Username string `schema:"username"`
}
func (l *Login) renderChangeUsername(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
func (l *Login) renderChangeUsername(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
var errType, errMessage string
if err != nil {
errMessage = l.getErrorMessage(r, err)
@ -31,7 +30,7 @@ func (l *Login) handleChangeUsername(w http.ResponseWriter, r *http.Request) {
l.renderError(w, r, authReq, err)
return
}
err = l.authRepo.ChangeUsername(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, data.Username)
err = l.command.ChangeUsername(setContext(r.Context(), authReq.UserOrgID), authReq.UserOrgID, authReq.UserID, data.Username)
if err != nil {
l.renderChangeUsername(w, r, authReq, err)
return
@ -39,7 +38,7 @@ func (l *Login) handleChangeUsername(w http.ResponseWriter, r *http.Request) {
l.renderChangeUsernameDone(w, r, authReq)
}
func (l *Login) renderChangeUsernameDone(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
func (l *Login) renderChangeUsernameDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
var errType, errMessage string
data := l.getUserData(r, authReq, "Username Change Done", errType, errMessage)
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplChangeUsernameDone], data, nil)

View File

@ -12,6 +12,8 @@
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
{{template "error-message" .}}
<div class="actions">
<button class="primary right" type="submit">{{t "Actions.Next"}}</button>
</div>

View File

@ -1,6 +1,7 @@
package model
import (
"github.com/caos/zitadel/internal/v2/domain"
"time"
"golang.org/x/text/language"
@ -117,21 +118,21 @@ func (r *UserSearchRequest) AppendMyOrgQuery(orgID string) {
r.Queries = append(r.Queries, &UserSearchQuery{Key: UserSearchKeyResourceOwner, Method: model.SearchMethodEquals, Value: orgID})
}
func (u *UserView) MFATypesSetupPossible(level req_model.MFALevel, policy *iam_model.LoginPolicyView) []req_model.MFAType {
types := make([]req_model.MFAType, 0)
func (u *UserView) MFATypesSetupPossible(level domain.MFALevel, policy *domain.LoginPolicy) []domain.MFAType {
types := make([]domain.MFAType, 0)
switch level {
default:
fallthrough
case req_model.MFALevelSecondFactor:
case domain.MFALevelSecondFactor:
if policy.HasSecondFactors() {
for _, mfaType := range policy.SecondFactors {
switch mfaType {
case iam_model.SecondFactorTypeOTP:
case domain.SecondFactorTypeOTP:
if u.OTPState != MFAStateReady {
types = append(types, req_model.MFATypeOTP)
types = append(types, domain.MFATypeOTP)
}
case iam_model.SecondFactorTypeU2F:
types = append(types, req_model.MFATypeU2F)
case domain.SecondFactorTypeU2F:
types = append(types, domain.MFATypeU2F)
}
}
}
@ -140,24 +141,24 @@ func (u *UserView) MFATypesSetupPossible(level req_model.MFALevel, policy *iam_m
return types
}
func (u *UserView) MFATypesAllowed(level req_model.MFALevel, policy *iam_model.LoginPolicyView) ([]req_model.MFAType, bool) {
types := make([]req_model.MFAType, 0)
func (u *UserView) MFATypesAllowed(level domain.MFALevel, policy *domain.LoginPolicy) ([]domain.MFAType, bool) {
types := make([]domain.MFAType, 0)
required := true
switch level {
default:
required = policy.ForceMFA
fallthrough
case req_model.MFALevelSecondFactor:
case domain.MFALevelSecondFactor:
if policy.HasSecondFactors() {
for _, mfaType := range policy.SecondFactors {
switch mfaType {
case iam_model.SecondFactorTypeOTP:
case domain.SecondFactorTypeOTP:
if u.OTPState == MFAStateReady {
types = append(types, req_model.MFATypeOTP)
types = append(types, domain.MFATypeOTP)
}
case iam_model.SecondFactorTypeU2F:
case domain.SecondFactorTypeU2F:
if u.IsU2FReady() {
types = append(types, req_model.MFATypeU2F)
types = append(types, domain.MFATypeU2F)
}
}
}

View File

@ -39,6 +39,7 @@ type NotifyUser struct {
VerifiedPhone string `json:"-" gorm:"column:verified_phone"`
PasswordSet bool `json:"-" gorm:"column:password_set"`
Sequence uint64 `json:"-" gorm:"column:sequence"`
State int32 `json:"-"`
}
func NotifyUserFromModel(user *model.NotifyUser) *NotifyUser {
@ -144,6 +145,8 @@ func (u *NotifyUser) AppendEvent(event *models.Event) (err error) {
case es_model.UserPasswordChanged,
es_model.HumanPasswordChanged:
err = u.setPasswordData(event)
case es_model.UserRemoved:
u.State = int32(UserStateDeleted)
}
return err
}

View File

@ -38,7 +38,7 @@ func writeModelToLoginPolicy(wm *LoginPolicyWriteModel) *domain.LoginPolicy {
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
AllowUsernamePassword: wm.AllowUserNamePassword,
AllowRegister: wm.AllowRegister,
AllowExternalIdp: wm.AllowExternalIDP,
AllowExternalIDP: wm.AllowExternalIDP,
ForceMFA: wm.ForceMFA,
PasswordlessType: wm.PasswordlessType,
}

View File

@ -108,7 +108,7 @@ func (wm *IAMIDPOIDCConfigWriteModel) NewChangedEvent(
if userNameMapping.Valid() && wm.UserNameMapping != userNameMapping {
changes = append(changes, idpconfig.ChangeUserNameMapping(userNameMapping))
}
if reflect.DeepEqual(wm.Scopes, scopes) {
if !reflect.DeepEqual(wm.Scopes, scopes) {
changes = append(changes, idpconfig.ChangeScopes(scopes))
}
if len(changes) == 0 {

View File

@ -44,7 +44,7 @@ func (r *CommandSide) addDefaultLoginPolicy(ctx context.Context, iamAgg *iam_rep
return caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LoginPolicy.AlreadyExists")
}
iamAgg.PushEvents(iam_repo.NewLoginPolicyAddedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIdp, policy.ForceMFA, policy.PasswordlessType))
iamAgg.PushEvents(iam_repo.NewLoginPolicyAddedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, policy.PasswordlessType))
return nil
}
@ -72,7 +72,7 @@ func (r *CommandSide) changeDefaultLoginPolicy(ctx context.Context, iamAgg *iam_
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return caos_errs.ThrowNotFound(nil, "IAM-M0sif", "Errors.IAM.LoginPolicy.NotFound")
}
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIdp, policy.ForceMFA, policy.PasswordlessType)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, policy.PasswordlessType)
if !hasChanged {
return caos_errs.ThrowPreconditionFailed(nil, "IAM-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged")
}

View File

@ -19,7 +19,7 @@ func (r *CommandSide) AddLoginPolicy(ctx context.Context, policy *domain.LoginPo
}
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.WriteModel)
orgAgg.PushEvents(org.NewLoginPolicyAddedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIdp, policy.ForceMFA, policy.PasswordlessType))
orgAgg.PushEvents(org.NewLoginPolicyAddedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, policy.PasswordlessType))
err = r.eventstore.PushAggregate(ctx, addedPolicy, orgAgg)
if err != nil {
@ -38,7 +38,7 @@ func (r *CommandSide) ChangeLoginPolicy(ctx context.Context, policy *domain.Logi
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "Org-M0sif", "Errors.Org.LoginPolicy.NotFound")
}
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIdp, policy.ForceMFA, domain.PasswordlessType(policy.PasswordlessType))
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, domain.PasswordlessType(policy.PasswordlessType))
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-5M9vdd", "Errors.Org.LoginPolicy.NotChanged")
}

View File

@ -2,6 +2,7 @@ package command
import (
"context"
"github.com/caos/zitadel/internal/eventstore/v2"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/v2/domain"
@ -144,7 +145,7 @@ func (r *CommandSide) ReactivateProject(ctx context.Context, projectID string, r
return r.eventstore.PushAggregate(ctx, existingProject, projectAgg)
}
func (r *CommandSide) RemoveProject(ctx context.Context, projectID, resourceOwner string) error {
func (r *CommandSide) RemoveProject(ctx context.Context, projectID, resourceOwner string, cascadingGrantIDs ...string) error {
if projectID == "" || resourceOwner == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-66hM9", "Errors.Project.ProjectIDMissing")
}
@ -157,11 +158,21 @@ func (r *CommandSide) RemoveProject(ctx context.Context, projectID, resourceOwne
return caos_errs.ThrowNotFound(nil, "COMMAND-3M9sd", "Errors.Project.NotFound")
}
aggregates := make([]eventstore.Aggregater, 0)
projectAgg := ProjectAggregateFromWriteModel(&existingProject.WriteModel)
projectAgg.PushEvents(project.NewProjectRemovedEvent(ctx, existingProject.Name, existingProject.ResourceOwner))
//TODO: Remove User Grants by ProjectID
aggregates = append(aggregates, projectAgg)
return r.eventstore.PushAggregate(ctx, existingProject, projectAgg)
for _, grantID := range cascadingGrantIDs {
grantAgg, _, err := r.removeUserGrant(ctx, grantID, "", true)
if err != nil {
return err
}
aggregates = append(aggregates, grantAgg)
}
_, err = r.eventstore.PushAggregates(ctx, aggregates...)
return err
}
func (r *CommandSide) getProjectWriteModelByID(ctx context.Context, projectID, resourceOwner string) (*ProjectWriteModel, error) {

View File

@ -91,7 +91,7 @@ func (r *CommandSide) SetupStep1(ctx context.Context, step1 *Step1) error {
&domain.LoginPolicy{
AllowUsernamePassword: step1.DefaultLoginPolicy.AllowUsernamePassword,
AllowRegister: step1.DefaultLoginPolicy.AllowRegister,
AllowExternalIdp: step1.DefaultLoginPolicy.AllowExternalIdp,
AllowExternalIDP: step1.DefaultLoginPolicy.AllowExternalIdp,
})
if err != nil {
return err

View File

@ -2,6 +2,10 @@ package command
import (
"context"
auth_req_model "github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/eventstore/models"
"strings"
"time"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/telemetry/tracing"
@ -21,7 +25,7 @@ func (r *CommandSide) ChangeUsername(ctx context.Context, orgID, userID, userNam
return caos_errs.ThrowNotFound(nil, "COMMAND-5N9ds", "Errors.User.NotFound")
}
if existingUser.UserName == userName {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.UsernameNotChanged")
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6m9gs", "Errors.User.UsernameNotChanged")
}
orgIAMPolicy, err := r.getOrgIAMPolicy(ctx, orgID)
@ -89,7 +93,7 @@ func (r *CommandSide) LockUser(ctx context.Context, userID, resourceOwner string
return caos_errs.ThrowNotFound(nil, "COMMAND-5M9fs", "Errors.User.NotFound")
}
if existingUser.UserState != domain.UserStateActive && existingUser.UserState != domain.UserStateInitial {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.ShouldBeActiveOrInitial")
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3NN8v", "Errors.User.ShouldBeActiveOrInitial")
}
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
userAgg.PushEvents(user.NewUserLockedEvent(ctx))
@ -139,6 +143,69 @@ func (r *CommandSide) RemoveUser(ctx context.Context, userID, resourceOwner stri
return r.eventstore.PushAggregate(ctx, existingUser, userAgg)
}
func (r *CommandSide) CreateUserToken(ctx context.Context, orgID, agentID, clientID, userID string, audience, scopes []string, lifetime time.Duration) (*domain.Token, error) {
if orgID == "" || userID == "" {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-55n8M", "Errors.IDMissing")
}
existingUser, err := r.userWriteModelByID(ctx, userID, orgID)
if err != nil {
return nil, err
}
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-1d6Gg", "Errors.User.NotFound")
}
for _, scope := range scopes {
if strings.HasPrefix(scope, auth_req_model.ProjectIDScope) && strings.HasSuffix(scope, auth_req_model.AudSuffix) {
audience = append(audience, strings.TrimSuffix(strings.TrimPrefix(scope, auth_req_model.ProjectIDScope), auth_req_model.AudSuffix))
}
}
preferredLanguage := ""
existingHuman, err := r.getHumanWriteModelByID(ctx, userID, orgID)
if existingHuman != nil {
preferredLanguage = existingHuman.PreferredLanguage.String()
}
now := time.Now().UTC()
tokenID, err := r.idGenerator.Next()
if err != nil {
return nil, err
}
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
userAgg.PushEvents(user.NewUserTokenAddedEvent(ctx, tokenID, clientID, agentID, preferredLanguage, audience, scopes, now.Add(lifetime)))
err = r.eventstore.PushAggregate(ctx, existingUser, userAgg)
if err != nil {
return nil, err
}
return &domain.Token{
ObjectRoot: models.ObjectRoot{
AggregateID: userID,
},
TokenID: tokenID,
UserAgentID: agentID,
ApplicationID: clientID,
Audience: audience,
Scopes: scopes,
Expiration: now.Add(lifetime),
PreferredLanguage: preferredLanguage,
}, nil
}
func (r *CommandSide) UserDomainClaimedSent(ctx context.Context, orgID, userID string) (err error) {
existingUser, err := r.userWriteModelByID(ctx, userID, orgID)
if err != nil {
return err
}
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-5m9gK", "Errors.User.NotFound")
}
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
userAgg.PushEvents(user.NewDomainClaimedSentEvent(ctx))
return r.eventstore.PushAggregate(ctx, existingUser, userAgg)
}
func (r *CommandSide) checkUserExists(ctx context.Context, userID, resourceOwner string) error {
userWriteModel, err := r.userWriteModelByID(ctx, userID, resourceOwner)
if err != nil {

View File

@ -2,6 +2,7 @@ package command
import (
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
)
func writeModelToHuman(wm *HumanWriteModel) *domain.Human {
@ -77,6 +78,15 @@ func writeModelToMachine(wm *MachineWriteModel) *domain.Machine {
}
}
func keyWriteModelToMachineKey(wm *MachineKeyWriteModel) *domain.MachineKey {
return &domain.MachineKey{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
KeyID: wm.KeyID,
Type: wm.KeyType,
ExpirationDate: wm.ExpirationDate,
}
}
func readModelToU2FTokens(wm *HumanU2FTokensReadModel) []*domain.WebAuthNToken {
tokens := make([]*domain.WebAuthNToken, len(wm.WebAuthNTokens))
for i, token := range wm.WebAuthNTokens {
@ -107,3 +117,19 @@ func writeModelToWebAuthN(wm *HumanWebAuthNWriteModel) *domain.WebAuthNToken {
State: wm.State,
}
}
func authRequestDomainToAuthRequestInfo(authRequest *domain.AuthRequest) *user.AuthRequestInfo {
info := &user.AuthRequestInfo{
ID: authRequest.ID,
UserAgentID: authRequest.AgentID,
SelectedIDPConfigID: authRequest.SelectedIDPConfigID,
}
if authRequest.BrowserInfo != nil {
info.BrowserInfo = &user.BrowserInfo{
UserAgent: authRequest.BrowserInfo.UserAgent,
AcceptLanguage: authRequest.BrowserInfo.AcceptLanguage,
RemoteIP: authRequest.BrowserInfo.RemoteIP,
}
}
return info
}

View File

@ -189,7 +189,7 @@ func (r *CommandSide) BulkRemoveUserGrant(ctx context.Context, grantIDs []string
}
func (r *CommandSide) removeUserGrant(ctx context.Context, grantID, resourceOwner string, cascade bool) (_ *usergrant.Aggregate, _ *UserGrantWriteModel, err error) {
if grantID == "" || resourceOwner == "" {
if grantID == "" {
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-J9sc5", "Errors.UserGrant.IDMissing")
}
@ -205,7 +205,6 @@ func (r *CommandSide) removeUserGrant(ctx context.Context, grantID, resourceOwne
return nil, nil, caos_errs.ThrowNotFound(nil, "COMMAND-1My0t", "Errors.UserGrant.NotFound")
}
//TODO: Remove Uniqueness
removeUserGrant := NewUserGrantWriteModel(grantID, resourceOwner)
userGrantAgg := UserGrantAggregateFromWriteModel(&removeUserGrant.WriteModel)
if !cascade {

View File

@ -62,9 +62,12 @@ func (wm *UserGrantWriteModel) Reduce() error {
}
func (wm *UserGrantWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, usergrant.AggregateType).
AggregateIDs(wm.AggregateID).
ResourceOwner(wm.ResourceOwner)
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, usergrant.AggregateType).
AggregateIDs(wm.AggregateID)
if wm.ResourceOwner != "" {
query.ResourceOwner(wm.ResourceOwner)
}
return query
}
func UserGrantAggregateFromWriteModel(wm *eventstore.WriteModel) *usergrant.Aggregate {

View File

@ -2,6 +2,7 @@ package command
import (
"context"
"github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/eventstore/v2"
caos_errs "github.com/caos/zitadel/internal/errors"
@ -27,9 +28,6 @@ func (r *CommandSide) AddHuman(ctx context.Context, orgID string, human *domain.
}
err = r.eventstore.PushAggregate(ctx, addedHuman, userAgg)
if err != nil {
if caos_errs.IsErrorAlreadyExists(err) {
return nil, caos_errs.ThrowAlreadyExists(err, "COMMAND-4kSff", "Errors.User.AlreadyExists")
}
return nil, err
}
@ -43,19 +41,36 @@ func (r *CommandSide) addHuman(ctx context.Context, orgID string, human *domain.
return r.createHuman(ctx, orgID, human, nil, false)
}
func (r *CommandSide) RegisterHuman(ctx context.Context, orgID string, human *domain.Human, externalIDP *domain.ExternalIDP) (*domain.Human, error) {
func (r *CommandSide) RegisterHuman(ctx context.Context, orgID string, human *domain.Human, externalIDP *domain.ExternalIDP, orgMemberRoles []string) (*domain.Human, error) {
aggregates := make([]eventstore.Aggregater, 2)
userAgg, addedHuman, err := r.registerHuman(ctx, orgID, human, externalIDP)
if err != nil {
return nil, err
}
err = r.eventstore.PushAggregate(ctx, addedHuman, userAgg)
if err != nil {
if caos_errs.IsErrorAlreadyExists(err) {
return nil, caos_errs.ThrowAlreadyExists(err, "COMMAND-4kSff", "Errors.User.AlreadyExists")
aggregates[0] = userAgg
orgMemberWriteModel := NewOrgMemberWriteModel(orgID, addedHuman.AggregateID)
orgAgg := OrgAggregateFromWriteModel(&orgMemberWriteModel.WriteModel)
if orgMemberRoles != nil {
orgMember := &domain.Member{
ObjectRoot: models.ObjectRoot{
AggregateID: orgID,
},
UserID: userAgg.ID(),
Roles: orgMemberRoles,
}
return nil, err
r.addOrgMember(ctx, orgAgg, orgMemberWriteModel, orgMember)
}
aggregates[1] = orgAgg
eventReader, err := r.eventstore.PushAggregates(ctx, aggregates...)
if err != nil {
return nil, err
}
addedHuman.AppendEvents(eventReader...)
addedHuman.Reduce()
return writeModelToHuman(addedHuman), nil
}
@ -82,7 +97,7 @@ func (r *CommandSide) createHuman(ctx context.Context, orgID string, human *doma
}
addedHuman := NewHumanWriteModel(human.AggregateID, orgID)
if err := human.CheckOrgIAMPolicy(human.Username, orgIAMPolicy); err != nil {
if err := human.CheckOrgIAMPolicy(orgIAMPolicy); err != nil {
return nil, nil, err
}
human.SetNamesAsDisplayname()
@ -100,11 +115,10 @@ func (r *CommandSide) createHuman(ctx context.Context, orgID string, human *doma
userAgg.PushEvents(createEvent)
if externalIDP != nil {
if !externalIDP.IsValid() {
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4Dj9s", "Errors.User.ExternalIDP.Invalid")
err = r.addHumanExternalIDP(ctx, userAgg, externalIDP)
if err != nil {
return nil, nil, err
}
//TODO: check if idpconfig exists
userAgg.PushEvents(user.NewHumanExternalIDPAddedEvent(ctx, externalIDP.IDPConfigID, externalIDP.DisplayName))
}
if human.IsInitialState() {
initCode, err := domain.NewInitUserCode(r.initializeUserCode)
@ -121,7 +135,7 @@ func (r *CommandSide) createHuman(ctx context.Context, orgID string, human *doma
if err != nil {
return nil, nil, err
}
user.NewHumanPhoneCodeAddedEvent(ctx, phoneCode.Code, phoneCode.Expiry)
userAgg.PushEvents(user.NewHumanPhoneCodeAddedEvent(ctx, phoneCode.Code, phoneCode.Expiry))
} else if human.Phone != nil && human.PhoneNumber != "" && human.IsPhoneVerified {
userAgg.PushEvents(user.NewHumanPhoneVerifiedEvent(ctx))
}
@ -129,32 +143,21 @@ func (r *CommandSide) createHuman(ctx context.Context, orgID string, human *doma
return userAgg, addedHuman, nil
}
func (r *CommandSide) ResendInitialMail(ctx context.Context, userID, email, resourceowner string) (err error) {
func (r *CommandSide) HumanSkipMFAInit(ctx context.Context, userID, resourceowner string) (err error) {
if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.UserIDMissing")
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2xpX9", "Errors.User.UserIDMissing")
}
existingEmail, err := r.emailWriteModel(ctx, userID, resourceowner)
existingHuman, err := r.getHumanWriteModelByID(ctx, userID, resourceowner)
if err != nil {
return err
}
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9df", "Errors.User.NotFound")
if existingHuman.UserState == domain.UserStateUnspecified || existingHuman.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-m9cV8", "Errors.User.NotFound")
}
if existingEmail.UserState != domain.UserStateInitial {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sd", "Errors.User.AlreadyInitialised")
}
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
if email != "" && existingEmail.Email != email {
changedEvent, _ := existingEmail.NewChangedEvent(ctx, email)
userAgg.PushEvents(changedEvent)
}
initCode, err := domain.NewInitUserCode(r.initializeUserCode)
if err != nil {
return err
}
userAgg.PushEvents(user.NewHumanInitialCodeAddedEvent(ctx, initCode.Code, initCode.Expiry))
return r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
userAgg := UserAggregateFromWriteModel(&existingHuman.WriteModel)
userAgg.PushEvents(user.NewHumanMFAInitSkippedEvent(ctx))
return r.eventstore.PushAggregate(ctx, existingHuman, userAgg)
}
func createAddHumanEvent(ctx context.Context, orgID string, human *domain.Human, userLoginMustBeDomain bool) *user.HumanAddedEvent {
@ -219,6 +222,28 @@ func createRegisterHumanEvent(ctx context.Context, orgID string, human *domain.H
return addEvent
}
func (r *CommandSide) HumansSignOut(ctx context.Context, agentID string, userIDs []string) error {
if agentID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M0ds", "Errors.User.UserIDMissing")
}
aggregates := make([]eventstore.Aggregater, len(userIDs))
for i, userID := range userIDs {
existingUser, err := r.getHumanWriteModelByID(ctx, userID, "")
if err != nil {
return err
}
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
continue
}
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
userAgg.PushEvents(user.NewHumanSignedOutEvent(ctx, agentID))
aggregates[i] = userAgg
}
_, err := r.eventstore.PushAggregates(ctx, aggregates...)
return err
}
func (r *CommandSide) getHumanWriteModelByID(ctx context.Context, userID, resourceowner string) (*HumanWriteModel, error) {
humanWriteModel := NewHumanWriteModel(userID, resourceowner)
err := r.eventstore.FilterToQueryReducer(ctx, humanWriteModel)

View File

@ -24,7 +24,7 @@ func (r *CommandSide) ChangeHumanEmail(ctx context.Context, email *domain.Email)
}
changedEvent, hasChanged := existingEmail.NewChangedEvent(ctx, email.EmailAddress)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.Email.NotChanged")
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2b7fM", "Errors.User.Email.NotChanged")
}
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
userAgg.PushEvents(changedEvent)
@ -60,7 +60,7 @@ func (r *CommandSide) VerifyHumanEmail(ctx context.Context, userID, code, resour
return err
}
if existingCode.Code == nil || existingCode.UserState == domain.UserStateUnspecified || existingCode.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9fs", "Errors.User.Code.NotFound")
return caos_errs.ThrowNotFound(nil, "COMMAND-3n8ud", "Errors.User.Code.NotFound")
}
userAgg := UserAggregateFromWriteModel(&existingCode.WriteModel)
@ -103,6 +103,19 @@ func (r *CommandSide) CreateHumanEmailVerificationCode(ctx context.Context, user
return r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
}
func (r *CommandSide) HumanEmailVerificationCodeSent(ctx context.Context, orgID, userID string) (err error) {
existingEmail, err := r.emailWriteModel(ctx, userID, orgID)
if err != nil {
return err
}
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-6n8uH", "Errors.User.Email.NotFound")
}
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
userAgg.PushEvents(user.NewHumanEmailCodeSentEvent(ctx))
return r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
}
func (r *CommandSide) emailWriteModel(ctx context.Context, userID, resourceOwner string) (writeModel *HumanEmailWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()

View File

@ -66,9 +66,12 @@ func (wm *HumanEmailWriteModel) Reduce() error {
}
func (wm *HumanEmailWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID).
ResourceOwner(wm.ResourceOwner)
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID)
if wm.ResourceOwner != "" {
query.ResourceOwner(wm.ResourceOwner)
}
return query
}
func (wm *HumanEmailWriteModel) NewChangedEvent(

View File

@ -3,11 +3,40 @@ package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/telemetry/tracing"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
)
func (r *CommandSide) BulkAddedHumanExternalIDP(ctx context.Context, userID, resourceOwner string, externalIDPs []*domain.ExternalIDP) error {
if len(externalIDPs) == 0 {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Ek9s", "Errors.User.ExternalIDP.MinimumExternalIDPNeeded")
}
aggregates := make([]eventstore.Aggregater, len(externalIDPs))
for i, externalIDP := range externalIDPs {
externalIDPWriteModel := NewHumanExternalIDPWriteModel(userID, externalIDP.IDPConfigID, externalIDP.ExternalUserID, resourceOwner)
userAgg := UserAggregateFromWriteModel(&externalIDPWriteModel.WriteModel)
err := r.addHumanExternalIDP(ctx, userAgg, externalIDP)
if err != nil {
return err
}
aggregates[i] = userAgg
}
_, err := r.eventstore.PushAggregates(ctx, aggregates...)
return err
}
func (r *CommandSide) addHumanExternalIDP(ctx context.Context, userAgg *user.Aggregate, externalIDP *domain.ExternalIDP) error {
if !externalIDP.IsValid() {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6m9Kd", "Errors.User.ExternalIDP.Invalid")
}
//TODO: check if idpconfig exists
userAgg.PushEvents(user.NewHumanExternalIDPAddedEvent(ctx, externalIDP.IDPConfigID, externalIDP.DisplayName, externalIDP.ExternalUserID))
return nil
}
func (r *CommandSide) RemoveHumanExternalIDP(ctx context.Context, externalIDP *domain.ExternalIDP) error {
return r.removeHumanExternalIDP(ctx, externalIDP, false)
}
@ -37,6 +66,24 @@ func (r *CommandSide) removeHumanExternalIDP(ctx context.Context, externalIDP *d
return r.eventstore.PushAggregate(ctx, existingExternalIDP, userAgg)
}
func (r *CommandSide) HumanExternalLoginChecked(ctx context.Context, orgID, userID string, authRequest *domain.AuthRequest) (err error) {
if userID == "" {
return caos_errs.ThrowNotFound(nil, "COMMAND-5n8sM", "Errors.IDMissing")
}
existingHuman, err := r.getHumanWriteModelByID(ctx, userID, orgID)
if err != nil {
return err
}
if existingHuman.UserState == domain.UserStateUnspecified || existingHuman.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-dn88J", "Errors.User.NotFound")
}
userAgg := UserAggregateFromWriteModel(&existingHuman.WriteModel)
userAgg.PushEvents(user.NewHumanExternalIDPCheckSucceededEvent(ctx, authRequestDomainToAuthRequestInfo(authRequest)))
return r.eventstore.PushAggregate(ctx, existingHuman, userAgg)
}
func (r *CommandSide) externalIDPWriteModelByID(ctx context.Context, userID, idpConfigID, externalUserID, resourceOwner string) (writeModel *HumanExternalIDPWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()

View File

@ -0,0 +1,104 @@
package command
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/crypto"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
)
//ResendInitialMail resend inital mail and changes email if provided
func (r *CommandSide) ResendInitialMail(ctx context.Context, userID, email, resourceOwner string) (err error) {
if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2n8vs", "Errors.User.UserIDMissing")
}
existingCode, err := r.getHumanInitWriteModelByID(ctx, userID, resourceOwner)
if err != nil {
return err
}
if existingCode.UserState == domain.UserStateUnspecified || existingCode.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9df", "Errors.User.NotFound")
}
if existingCode.UserState != domain.UserStateInitial {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sd", "Errors.User.AlreadyInitialised")
}
userAgg := UserAggregateFromWriteModel(&existingCode.WriteModel)
if email != "" && existingCode.Email != email {
changedEvent, _ := existingCode.NewChangedEvent(ctx, email)
userAgg.PushEvents(changedEvent)
}
initCode, err := domain.NewInitUserCode(r.initializeUserCode)
if err != nil {
return err
}
userAgg.PushEvents(user.NewHumanInitialCodeAddedEvent(ctx, initCode.Code, initCode.Expiry))
return r.eventstore.PushAggregate(ctx, existingCode, userAgg)
}
func (r *CommandSide) HumanVerifyInitCode(ctx context.Context, userID, resourceOwner, code, passwordString string) error {
if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-mkM9f", "Errors.User.UserIDMissing")
}
if code == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-44G8s", "Errors.User.Code.Empty")
}
existingCode, err := r.getHumanInitWriteModelByID(ctx, userID, resourceOwner)
if err != nil {
return err
}
if existingCode.Code == nil || existingCode.UserState == domain.UserStateUnspecified || existingCode.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-mmn5f", "Errors.User.Code.NotFound")
}
userAgg := UserAggregateFromWriteModel(&existingCode.WriteModel)
err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, r.initializeUserCode)
if err != nil {
userAgg.PushEvents(user.NewHumanInitializedCheckFailedEvent(ctx))
err = r.eventstore.PushAggregate(ctx, existingCode, userAgg)
logging.LogWithFields("COMMAND-Dg2z5", "userID", userAgg.ID()).OnError(err).Error("NewHumanInitializedCheckFailedEvent push failed")
return caos_errs.ThrowInvalidArgument(err, "COMMAND-11v6G", "Errors.User.Code.Invalid")
}
userAgg.PushEvents(user.NewHumanInitializedCheckSucceededEvent(ctx))
if !existingCode.IsEmailVerified {
userAgg.PushEvents(user.NewHumanEmailVerifiedEvent(ctx))
}
if passwordString != "" {
passwordWriteModel := NewHumanPasswordWriteModel(userID, existingCode.ResourceOwner)
password := &domain.Password{
SecretString: passwordString,
ChangeRequired: false,
}
err = r.changePassword(ctx, existingCode.ResourceOwner, userID, "", password, userAgg, passwordWriteModel)
if err != nil {
return err
}
}
return r.eventstore.PushAggregate(ctx, existingCode, userAgg)
}
func (r *CommandSide) HumanInitCodeSent(ctx context.Context, orgID, userID string) (err error) {
existingInitCode, err := r.getHumanInitWriteModelByID(ctx, userID, orgID)
if err != nil {
return err
}
if existingInitCode.UserState == domain.UserStateUnspecified || existingInitCode.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-556zg", "Errors.User.Code.NotFound")
}
userAgg := UserAggregateFromWriteModel(&existingInitCode.WriteModel)
userAgg.PushEvents(user.NewHumanInitialCodeSentEvent(ctx))
return r.eventstore.PushAggregate(ctx, existingInitCode, userAgg)
}
func (r *CommandSide) getHumanInitWriteModelByID(ctx context.Context, userID, resourceowner string) (*HumanInitCodeWriteModel, error) {
initWriteModel := NewHumanInitCodeWriteModel(userID, resourceowner)
err := r.eventstore.FilterToQueryReducer(ctx, initWriteModel)
if err != nil {
return nil, err
}
return initWriteModel, nil
}

View File

@ -0,0 +1,89 @@
package command
import (
"context"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
"time"
)
type HumanInitCodeWriteModel struct {
eventstore.WriteModel
Email string
IsEmailVerified bool
Code *crypto.CryptoValue
CodeCreationDate time.Time
CodeExpiry time.Duration
UserState domain.UserState
}
func NewHumanInitCodeWriteModel(userID, resourceOwner string) *HumanInitCodeWriteModel {
return &HumanInitCodeWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: userID,
ResourceOwner: resourceOwner,
},
}
}
func (wm *HumanInitCodeWriteModel) AppendEvents(events ...eventstore.EventReader) {
wm.WriteModel.AppendEvents(events...)
}
func (wm *HumanInitCodeWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *user.HumanAddedEvent:
wm.Email = e.EmailAddress
wm.UserState = domain.UserStateInitial
case *user.HumanRegisteredEvent:
wm.Email = e.EmailAddress
wm.UserState = domain.UserStateInitial
case *user.HumanEmailChangedEvent:
wm.Email = e.EmailAddress
wm.IsEmailVerified = false
case *user.HumanEmailVerifiedEvent:
wm.IsEmailVerified = true
wm.Code = nil
if wm.UserState == domain.UserStateInitial {
wm.UserState = domain.UserStateActive
}
case *user.HumanInitialCodeAddedEvent:
wm.Code = e.Code
wm.CodeCreationDate = e.CreationDate()
wm.CodeExpiry = e.Expiry
case *user.HumanInitializedCheckSucceededEvent:
wm.Code = nil
case *user.UserRemovedEvent:
wm.UserState = domain.UserStateDeleted
}
}
return wm.WriteModel.Reduce()
}
func (wm *HumanInitCodeWriteModel) Query() *eventstore.SearchQueryBuilder {
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID)
if wm.ResourceOwner != "" {
query.ResourceOwner(wm.ResourceOwner)
}
return query
}
func (wm *HumanInitCodeWriteModel) NewChangedEvent(
ctx context.Context,
email string,
) (*user.HumanEmailChangedEvent, bool) {
hasChanged := false
changedEvent := user.NewHumanEmailChangedEvent(ctx)
if wm.Email != email {
hasChanged = true
changedEvent.EmailAddress = email
}
return changedEvent, hasChanged
}

View File

@ -2,6 +2,7 @@ package command
import (
"context"
"github.com/caos/logging"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/telemetry/tracing"
@ -15,14 +16,17 @@ func (r *CommandSide) AddHumanOTP(ctx context.Context, userID, resourceowner str
}
human, err := r.getHuman(ctx, userID, resourceowner)
if err != nil {
logging.Log("COMMAND-DAqe1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get human for loginname")
return nil, err
}
org, err := r.getOrg(ctx, human.ResourceOwner)
if err != nil {
logging.Log("COMMAND-Cm0ds").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get org for loginname")
return nil, err
}
orgPolicy, err := r.getOrgIAMPolicy(ctx, org.AggregateID)
if err != nil {
logging.Log("COMMAND-y5zv9").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get org policy for loginname")
return nil, err
}
otpWriteModel, err := r.otpWriteModelByID(ctx, userID, resourceowner)
@ -58,7 +62,7 @@ func (r *CommandSide) AddHumanOTP(ctx context.Context, userID, resourceowner str
}, nil
}
func (r *CommandSide) CheckMFAOTPSetup(ctx context.Context, userID, code, userAgentID, resourceowner string) error {
func (r *CommandSide) HumanCheckMFAOTPSetup(ctx context.Context, userID, code, userAgentID, resourceowner string) error {
if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-8N9ds", "Errors.User.UserIDMissing")
}
@ -84,7 +88,32 @@ func (r *CommandSide) CheckMFAOTPSetup(ctx context.Context, userID, code, userAg
return r.eventstore.PushAggregate(ctx, existingOTP, userAgg)
}
func (r *CommandSide) RemoveHumanOTP(ctx context.Context, userID, resourceOwner string) error {
func (r *CommandSide) HumanCheckMFAOTP(ctx context.Context, userID, code, resourceowner string, authRequest *domain.AuthRequest) error {
if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-8N9ds", "Errors.User.UserIDMissing")
}
existingOTP, err := r.otpWriteModelByID(ctx, userID, resourceowner)
if err != nil {
return err
}
if existingOTP.State != domain.MFAStateReady {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3Mif9s", "Errors.User.MFA.OTP.NotReady")
}
userAgg := UserAggregateFromWriteModel(&existingOTP.WriteModel)
err = domain.VerifyMFAOTP(code, existingOTP.Secret, r.multifactors.OTP.CryptoMFA)
if err == nil {
userAgg.PushEvents(
user.NewHumanOTPCheckSucceededEvent(ctx, authRequestDomainToAuthRequestInfo(authRequest)),
)
return r.eventstore.PushAggregate(ctx, existingOTP, userAgg)
}
userAgg.PushEvents(user.NewHumanOTPCheckFailedEvent(ctx, authRequestDomainToAuthRequestInfo(authRequest)))
pushErr := r.eventstore.PushAggregate(ctx, existingOTP, userAgg)
logging.Log("COMMAND-9fj7s").OnError(pushErr).Error("error create password check failed event")
return err
}
func (r *CommandSide) HumanRemoveOTP(ctx context.Context, userID, resourceOwner string) error {
if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-5M0sd", "Errors.User.UserIDMissing")
}

View File

@ -45,7 +45,10 @@ func (wm *HumanOTPWriteModel) Reduce() error {
}
func (wm *HumanOTPWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID).
ResourceOwner(wm.ResourceOwner)
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID)
if wm.ResourceOwner != "" {
query.ResourceOwner(wm.ResourceOwner)
}
return query
}

View File

@ -2,6 +2,7 @@ package command
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/crypto"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/telemetry/tracing"
@ -21,7 +22,34 @@ func (r *CommandSide) SetOneTimePassword(ctx context.Context, orgID, userID, pas
SecretString: passwordString,
ChangeRequired: true,
}
return r.changePassword(ctx, orgID, userID, "", password, existingPassword)
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
return r.changePassword(ctx, orgID, userID, "", password, userAgg, existingPassword)
}
func (r *CommandSide) SetPassword(ctx context.Context, orgID, userID, code, passwordString, userAgentID string) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
existingCode, err := r.passwordWriteModel(ctx, userID, orgID)
if err != nil {
return err
}
if existingCode.Code == nil || existingCode.UserState == domain.UserStateUnspecified || existingCode.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9fs", "Errors.User.Code.NotFound")
}
err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, r.emailVerificationCode)
if err != nil {
return err
}
password := &domain.Password{
SecretString: passwordString,
ChangeRequired: false,
}
userAgg := UserAggregateFromWriteModel(&existingCode.WriteModel)
return r.changePassword(ctx, existingCode.ResourceOwner, userID, userAgentID, password, userAgg, existingCode)
}
func (r *CommandSide) ChangePassword(ctx context.Context, orgID, userID, oldPassword, newPassword, userAgentID string) (err error) {
@ -32,7 +60,7 @@ func (r *CommandSide) ChangePassword(ctx context.Context, orgID, userID, oldPass
if err != nil {
return err
}
if existingPassword.Secret != nil {
if existingPassword.Secret == nil {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Fds3s", "Errors.User.Password.Empty")
}
ctx, spanPasswordComparison := tracing.NewNamedSpan(ctx, "crypto.CompareHash")
@ -44,12 +72,14 @@ func (r *CommandSide) ChangePassword(ctx context.Context, orgID, userID, oldPass
}
password := &domain.Password{
SecretString: newPassword,
ChangeRequired: true,
ChangeRequired: false,
}
return r.changePassword(ctx, orgID, userID, userAgentID, password, existingPassword)
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
return r.changePassword(ctx, orgID, userID, userAgentID, password, userAgg, existingPassword)
}
func (r *CommandSide) changePassword(ctx context.Context, orgID, userID, userAgentID string, password *domain.Password, existingPassword *HumanPasswordWriteModel) (err error) {
func (r *CommandSide) changePassword(ctx context.Context, orgID, userID, userAgentID string, password *domain.Password, userAgg *user.Aggregate, existingPassword *HumanPasswordWriteModel) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
@ -69,7 +99,6 @@ func (r *CommandSide) changePassword(ctx context.Context, orgID, userID, userAge
if err := password.HashPasswordIfExisting(pwPolicy, r.userPasswordAlg); err != nil {
return err
}
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
userAgg.PushEvents(user.NewHumanPasswordChangedEvent(ctx, password.SecretCrypto, password.ChangeRequired, userAgentID))
return r.eventstore.PushAggregate(ctx, existingPassword, userAgg)
}
@ -94,6 +123,54 @@ func (r *CommandSide) RequestSetPassword(ctx context.Context, userID, resourceOw
return r.eventstore.PushAggregate(ctx, existingHuman, userAgg)
}
func (r *CommandSide) PasswordCodeSent(ctx context.Context, orgID, userID string) (err error) {
existingPassword, err := r.passwordWriteModel(ctx, userID, orgID)
if err != nil {
return err
}
if existingPassword.UserState == domain.UserStateUnspecified || existingPassword.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-3n77z", "Errors.User.NotFound")
}
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
userAgg.PushEvents(user.NewHumanPasswordCodeSentEvent(ctx))
return r.eventstore.PushAggregate(ctx, existingPassword, userAgg)
}
func (r *CommandSide) HumanCheckPassword(ctx context.Context, orgID, userID, password string, authRequest *domain.AuthRequest) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
if password == "" {
return caos_errs.ThrowNotFound(nil, "COMMAND-3n8fs", "Errors.User.Password.Empty")
}
existingPassword, err := r.passwordWriteModel(ctx, userID, orgID)
if err != nil {
return err
}
if existingPassword.UserState == domain.UserStateUnspecified || existingPassword.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-3n77z", "Errors.User.NotFound")
}
if existingPassword.Secret == nil {
return caos_errs.ThrowNotFound(nil, "COMMAND-3n77z", "Errors.User.Password.NotSet")
}
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
ctx, spanPasswordComparison := tracing.NewNamedSpan(ctx, "crypto.CompareHash")
err = crypto.CompareHash(existingPassword.Secret, []byte(password), r.userPasswordAlg)
spanPasswordComparison.EndWithError(err)
if err == nil {
userAgg.PushEvents(user.NewHumanPasswordCheckSucceededEvent(ctx, authRequestDomainToAuthRequestInfo(authRequest)))
return r.eventstore.PushAggregate(ctx, existingPassword, userAgg)
}
userAgg.PushEvents(user.NewHumanPasswordCheckFailedEvent(ctx, authRequestDomainToAuthRequestInfo(authRequest)))
err = r.eventstore.PushAggregate(ctx, existingPassword, userAgg)
logging.Log("COMMAND-9fj7s").OnError(err).Error("error create password check failed event")
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-452ad", "Errors.User.Password.Invalid")
}
func (r *CommandSide) passwordWriteModel(ctx context.Context, userID, resourceOwner string) (writeModel *HumanPasswordWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()

View File

@ -5,6 +5,7 @@ import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
"time"
)
type HumanPasswordWriteModel struct {
@ -13,6 +14,10 @@ type HumanPasswordWriteModel struct {
Secret *crypto.CryptoValue
SecretChangeRequired bool
Code *crypto.CryptoValue
CodeCreationDate time.Time
CodeExpiry time.Duration
UserState domain.UserState
}
@ -43,6 +48,11 @@ func (wm *HumanPasswordWriteModel) Reduce() error {
case *user.HumanPasswordChangedEvent:
wm.Secret = e.Secret
wm.SecretChangeRequired = e.ChangeRequired
wm.Code = nil
case *user.HumanPasswordCodeAddedEvent:
wm.Code = e.Code
wm.CodeCreationDate = e.CreationDate()
wm.CodeExpiry = e.Expiry
case *user.HumanEmailVerifiedEvent:
if wm.UserState == domain.UserStateInitial {
wm.UserState = domain.UserStateActive
@ -55,7 +65,10 @@ func (wm *HumanPasswordWriteModel) Reduce() error {
}
func (wm *HumanPasswordWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID).
ResourceOwner(wm.ResourceOwner)
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID)
if wm.ResourceOwner != "" {
query.ResourceOwner(wm.ResourceOwner)
}
return query
}

View File

@ -93,7 +93,7 @@ func (r *CommandSide) CreateHumanPhoneVerificationCode(ctx context.Context, user
return err
}
if existingPhone.State == domain.PhoneStateUnspecified || existingPhone.State == domain.PhoneStateRemoved {
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9fs", "Errors.User.Phone.NotFound")
return caos_errs.ThrowNotFound(nil, "COMMAND-2b7Hf", "Errors.User.Phone.NotFound")
}
if existingPhone.IsPhoneVerified {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sf", "Errors.User.Phone.AlreadyVerified")
@ -107,6 +107,19 @@ func (r *CommandSide) CreateHumanPhoneVerificationCode(ctx context.Context, user
return r.eventstore.PushAggregate(ctx, existingPhone, userAgg)
}
func (r *CommandSide) HumanPhoneVerificationCodeSent(ctx context.Context, orgID, userID string) (err error) {
existingPhone, err := r.phoneWriteModelByID(ctx, userID, orgID)
if err != nil {
return err
}
if existingPhone.State == domain.PhoneStateUnspecified || existingPhone.State == domain.PhoneStateRemoved {
return caos_errs.ThrowNotFound(nil, "COMMAND-66n8J", "Errors.User.Phone.NotFound")
}
userAgg := UserAggregateFromWriteModel(&existingPhone.WriteModel)
userAgg.PushEvents(user.NewHumanPhoneCodeSentEvent(ctx))
return r.eventstore.PushAggregate(ctx, existingPhone, userAgg)
}
func (r *CommandSide) RemoveHumanPhone(ctx context.Context, userID, resourceOwner string) error {
if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M0ds", "Errors.User.UserIDMissing")

View File

@ -33,7 +33,35 @@ func (r *CommandSide) getHumanPasswordlessTokens(ctx context.Context, userID, re
return readModelToPasswordlessTokens(tokenReadModel), nil
}
func (r *CommandSide) AddHumanU2F(ctx context.Context, userID, resourceowner string, isLoginUI bool) (*domain.WebAuthNToken, error) {
func (r *CommandSide) getHumanU2FLogin(ctx context.Context, userID, authReqID, resourceowner string) (*domain.WebAuthNLogin, error) {
tokenReadModel := NewHumanU2FLoginReadModel(userID, authReqID, resourceowner)
err := r.eventstore.FilterToQueryReducer(ctx, tokenReadModel)
if err != nil {
return nil, err
}
if tokenReadModel.State == domain.UserStateDeleted {
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-5m88U", "Errors.User.NotFound")
}
return &domain.WebAuthNLogin{
Challenge: tokenReadModel.Challenge,
}, nil
}
func (r *CommandSide) getHumanPasswordlessLogin(ctx context.Context, userID, authReqID, resourceowner string) (*domain.WebAuthNLogin, error) {
tokenReadModel := NewHumanPasswordlessLoginReadModel(userID, authReqID, resourceowner)
err := r.eventstore.FilterToQueryReducer(ctx, tokenReadModel)
if err != nil {
return nil, err
}
if tokenReadModel.State == domain.UserStateDeleted {
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-fm84R", "Errors.User.NotFound")
}
return &domain.WebAuthNLogin{
Challenge: tokenReadModel.Challenge,
}, nil
}
func (r *CommandSide) HumanAddU2FSetup(ctx context.Context, userID, resourceowner string, isLoginUI bool) (*domain.WebAuthNToken, error) {
u2fTokens, err := r.getHumanU2FTokens(ctx, userID, resourceowner)
if err != nil {
return nil, err
@ -55,7 +83,7 @@ func (r *CommandSide) AddHumanU2F(ctx context.Context, userID, resourceowner str
return createdWebAuthN, nil
}
func (r *CommandSide) AddHumanPasswordless(ctx context.Context, userID, resourceowner string, isLoginUI bool) (*domain.WebAuthNToken, error) {
func (r *CommandSide) HumanAddPasswordlessSetup(ctx context.Context, userID, resourceowner string, isLoginUI bool) (*domain.WebAuthNToken, error) {
passwordlessTokens, err := r.getHumanPasswordlessTokens(ctx, userID, resourceowner)
if err != nil {
return nil, err
@ -64,7 +92,7 @@ func (r *CommandSide) AddHumanPasswordless(ctx context.Context, userID, resource
if err != nil {
return nil, err
}
userAgg.PushEvents(usr_repo.NewHumanU2FAddedEvent(ctx, addWebAuthN.WebauthNTokenID, webAuthN.Challenge))
userAgg.PushEvents(usr_repo.NewHumanPasswordlessAddedEvent(ctx, addWebAuthN.WebauthNTokenID, webAuthN.Challenge))
err = r.eventstore.PushAggregate(ctx, addWebAuthN, userAgg)
if err != nil {
@ -114,7 +142,7 @@ func (r *CommandSide) addHumanWebAuthN(ctx context.Context, userID, resourceowne
return addWebAuthN, userAgg, webAuthN, nil
}
func (r *CommandSide) VerifyHumanU2F(ctx context.Context, userID, resourceowner, tokenName, userAgentID string, credentialData []byte) error {
func (r *CommandSide) HumanVerifyU2FSetup(ctx context.Context, userID, resourceowner, tokenName, userAgentID string, credentialData []byte) error {
u2fTokens, err := r.getHumanU2FTokens(ctx, userID, resourceowner)
if err != nil {
return err
@ -139,7 +167,7 @@ func (r *CommandSide) VerifyHumanU2F(ctx context.Context, userID, resourceowner,
return r.eventstore.PushAggregate(ctx, verifyWebAuthN, userAgg)
}
func (r *CommandSide) VerifyHumanPasswordless(ctx context.Context, userID, resourceowner, tokenName, userAgentID string, credentialData []byte) error {
func (r *CommandSide) HumanHumanPasswordlessSetup(ctx context.Context, userID, resourceowner, tokenName, userAgentID string, credentialData []byte) error {
u2fTokens, err := r.getHumanPasswordlessTokens(ctx, userID, resourceowner)
if err != nil {
return err
@ -149,7 +177,7 @@ func (r *CommandSide) VerifyHumanPasswordless(ctx context.Context, userID, resou
return err
}
userAgg.PushEvents(
usr_repo.NewHumanU2FVerifiedEvent(
usr_repo.NewHumanPasswordlessVerifiedEvent(
ctx,
verifyWebAuthN.WebauthNTokenID,
webAuthN.WebAuthNTokenName,
@ -186,12 +214,156 @@ func (r *CommandSide) verifyHumanWebAuthN(ctx context.Context, userID, resourceo
return verifyWebAuthN, userAgg, webAuthN, nil
}
func (r *CommandSide) RemoveHumanU2F(ctx context.Context, userID, webAuthNID, resourceOwner string) error {
func (r *CommandSide) HumanBeginU2FLogin(ctx context.Context, userID, resourceOwner string, authRequest *domain.AuthRequest, isLoginUI bool) (*domain.WebAuthNLogin, error) {
u2fTokens, err := r.getHumanU2FTokens(ctx, userID, resourceOwner)
if err != nil {
return nil, err
}
writeModel, userAgg, webAuthNLogin, err := r.beginWebAuthNLogin(ctx, userID, resourceOwner, u2fTokens, isLoginUI)
if err != nil {
return nil, err
}
userAgg.PushEvents(
usr_repo.NewHumanU2FBeginLoginEvent(
ctx,
webAuthNLogin.Challenge,
authRequestDomainToAuthRequestInfo(authRequest),
),
)
err = r.eventstore.PushAggregate(ctx, writeModel, userAgg)
return webAuthNLogin, err
}
func (r *CommandSide) HumanBeginPasswordlessLogin(ctx context.Context, userID, resourceOwner string, authRequest *domain.AuthRequest, isLoginUI bool) (*domain.WebAuthNLogin, error) {
u2fTokens, err := r.getHumanPasswordlessTokens(ctx, userID, resourceOwner)
if err != nil {
return nil, err
}
writeModel, userAgg, webAuthNLogin, err := r.beginWebAuthNLogin(ctx, userID, resourceOwner, u2fTokens, isLoginUI)
if err != nil {
return nil, err
}
userAgg.PushEvents(
usr_repo.NewHumanPasswordlessBeginLoginEvent(
ctx,
webAuthNLogin.Challenge,
authRequestDomainToAuthRequestInfo(authRequest),
),
)
err = r.eventstore.PushAggregate(ctx, writeModel, userAgg)
return webAuthNLogin, err
}
func (r *CommandSide) beginWebAuthNLogin(ctx context.Context, userID, resourceOwner string, tokens []*domain.WebAuthNToken, isLoginUI bool) (*HumanWebAuthNWriteModel, *usr_repo.Aggregate, *domain.WebAuthNLogin, error) {
if userID == "" {
return nil, nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-hh8K9", "Errors.IDMissing")
}
human, err := r.getHuman(ctx, userID, resourceOwner)
if err != nil {
return nil, nil, nil, err
}
webAuthNLogin, err := r.webauthn.BeginLogin(human, domain.UserVerificationRequirementDiscouraged, isLoginUI, tokens...)
if err != nil {
return nil, nil, nil, err
}
writeModel, err := r.webauthNWriteModelByID(ctx, userID, "", resourceOwner)
if err != nil {
return nil, nil, nil, err
}
userAgg := UserAggregateFromWriteModel(&writeModel.WriteModel)
return writeModel, userAgg, webAuthNLogin, nil
}
func (r *CommandSide) HumanFinishU2FLogin(ctx context.Context, userID, resourceOwner string, credentialData []byte, authRequest *domain.AuthRequest, isLoginUI bool) error {
webAuthNLogin, err := r.getHumanU2FLogin(ctx, userID, authRequest.ID, resourceOwner)
if err != nil {
return err
}
u2fTokens, err := r.getHumanU2FTokens(ctx, userID, resourceOwner)
if err != nil {
return err
}
writeModel, userAgg, token, signCount, err := r.finishWebAuthNLogin(ctx, userID, resourceOwner, credentialData, webAuthNLogin, u2fTokens, isLoginUI)
if err != nil {
return err
}
userAgg.PushEvents(
usr_repo.NewHumanU2FSignCountChangedEvent(
ctx,
token.WebAuthNTokenID,
signCount,
),
)
return r.eventstore.PushAggregate(ctx, writeModel, userAgg)
}
func (r *CommandSide) HumanFinishPasswordlessLogin(ctx context.Context, userID, resourceOwner string, credentialData []byte, authRequest *domain.AuthRequest, isLoginUI bool) error {
webAuthNLogin, err := r.getHumanPasswordlessLogin(ctx, userID, authRequest.ID, resourceOwner)
if err != nil {
return err
}
passwordlessTokens, err := r.getHumanPasswordlessTokens(ctx, userID, resourceOwner)
if err != nil {
return err
}
writeModel, userAgg, token, signCount, err := r.finishWebAuthNLogin(ctx, userID, resourceOwner, credentialData, webAuthNLogin, passwordlessTokens, isLoginUI)
if err != nil {
return err
}
userAgg.PushEvents(
usr_repo.NewHumanPasswordlessSignCountChangedEvent(
ctx,
token.WebAuthNTokenID,
signCount,
),
)
return r.eventstore.PushAggregate(ctx, writeModel, userAgg)
}
func (r *CommandSide) finishWebAuthNLogin(ctx context.Context, userID, resourceOwner string, credentialData []byte, webAuthN *domain.WebAuthNLogin, tokens []*domain.WebAuthNToken, isLoginUI bool) (*HumanWebAuthNWriteModel, *usr_repo.Aggregate, *domain.WebAuthNToken, uint32, error) {
if userID == "" {
return nil, nil, nil, 0, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-hh8K9", "Errors.IDMissing")
}
human, err := r.getHuman(ctx, userID, resourceOwner)
if err != nil {
return nil, nil, nil, 0, err
}
keyID, signCount, err := r.webauthn.FinishLogin(human, webAuthN, credentialData, isLoginUI, tokens...)
if err != nil && keyID == nil {
return nil, nil, nil, 0, err
}
_, token := domain.GetTokenByKeyID(tokens, keyID)
if token == nil {
return nil, nil, nil, 0, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3b7zs", "Errors.User.WebAuthN.NotFound")
}
writeModel, err := r.webauthNWriteModelByID(ctx, userID, "", resourceOwner)
if err != nil {
return nil, nil, nil, 0, err
}
userAgg := UserAggregateFromWriteModel(&writeModel.WriteModel)
return writeModel, userAgg, token, signCount, nil
}
func (r *CommandSide) HumanRemoveU2F(ctx context.Context, userID, webAuthNID, resourceOwner string) error {
event := usr_repo.NewHumanU2FRemovedEvent(ctx, webAuthNID)
return r.removeHumanWebAuthN(ctx, userID, webAuthNID, resourceOwner, event)
}
func (r *CommandSide) RemoveHumanPasswordless(ctx context.Context, userID, webAuthNID, resourceOwner string) error {
func (r *CommandSide) HumanRemovePasswordless(ctx context.Context, userID, webAuthNID, resourceOwner string) error {
event := usr_repo.NewHumanPasswordlessRemovedEvent(ctx, webAuthNID)
return r.removeHumanWebAuthN(ctx, userID, webAuthNID, resourceOwner, event)
}

View File

@ -39,6 +39,14 @@ func (wm *HumanWebAuthNWriteModel) AppendEvents(events ...eventstore.EventReader
if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(e)
}
case *user.HumanWebAuthNVerifiedEvent:
if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(e)
}
case *user.HumanWebAuthNSignCountChangedEvent:
if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(e)
}
case *user.HumanWebAuthNRemovedEvent:
if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(e)
@ -56,6 +64,8 @@ func (wm *HumanWebAuthNWriteModel) Reduce() error {
wm.appendAddedEvent(e)
case *user.HumanWebAuthNVerifiedEvent:
wm.appendVerifiedEvent(e)
case *user.HumanWebAuthNSignCountChangedEvent:
wm.SignCount = e.SignCount
case *user.HumanWebAuthNRemovedEvent:
wm.State = domain.MFAStateRemoved
case *user.UserRemovedEvent:
@ -104,34 +114,35 @@ func NewHumanU2FTokensReadModel(userID, resourceOwner string) *HumanU2FTokensRea
}
func (wm *HumanU2FTokensReadModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *user.HumanWebAuthNAddedEvent:
wm.WriteModel.AppendEvents(e)
case *user.HumanWebAuthNVerifiedEvent:
wm.WriteModel.AppendEvents(e)
case *user.HumanWebAuthNRemovedEvent:
wm.WriteModel.AppendEvents(e)
case *user.UserRemovedEvent:
wm.WriteModel.AppendEvents(e)
}
}
wm.WriteModel.AppendEvents(events...)
}
func (wm *HumanU2FTokensReadModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *user.HumanWebAuthNAddedEvent:
case *user.HumanU2FAddedEvent:
token := &HumanWebAuthNWriteModel{}
token.appendAddedEvent(e)
wm.WebAuthNTokens = append(wm.WebAuthNTokens, token)
case *user.HumanWebAuthNVerifiedEvent:
token.appendAddedEvent(&e.HumanWebAuthNAddedEvent)
token.WriteModel = eventstore.WriteModel{
AggregateID: e.AggregateID(),
}
replaced := false
for i, existingTokens := range wm.WebAuthNTokens {
if existingTokens.State == domain.MFAStateNotReady {
wm.WebAuthNTokens[i] = token
replaced = true
}
}
if !replaced {
wm.WebAuthNTokens = append(wm.WebAuthNTokens, token)
}
case *user.HumanU2FVerifiedEvent:
idx, token := wm.WebAuthNTokenByID(e.WebAuthNTokenID)
if idx < 0 {
continue
}
token.appendVerifiedEvent(e)
case *user.HumanWebAuthNRemovedEvent:
token.appendVerifiedEvent(&e.HumanWebAuthNVerifiedEvent)
case *user.HumanU2FRemovedEvent:
idx, _ := wm.WebAuthNTokenByID(e.WebAuthNTokenID)
if idx < 0 {
continue
@ -153,8 +164,7 @@ func (rm *HumanU2FTokensReadModel) Query() *eventstore.SearchQueryBuilder {
EventTypes(
user.HumanU2FTokenAddedType,
user.HumanU2FTokenVerifiedType,
user.HumanU2FTokenRemovedType,
user.UserV1MFAOTPRemovedType)
user.HumanU2FTokenRemovedType)
}
@ -190,17 +200,29 @@ func (wm *HumanPasswordlessTokensReadModel) AppendEvents(events ...eventstore.Ev
func (wm *HumanPasswordlessTokensReadModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *user.HumanWebAuthNAddedEvent:
case *user.HumanPasswordlessAddedEvent:
token := &HumanWebAuthNWriteModel{}
token.appendAddedEvent(e)
wm.WebAuthNTokens = append(wm.WebAuthNTokens, token)
case *user.HumanWebAuthNVerifiedEvent:
token.appendAddedEvent(&e.HumanWebAuthNAddedEvent)
token.WriteModel = eventstore.WriteModel{
AggregateID: e.AggregateID(),
}
replaced := false
for i, existingTokens := range wm.WebAuthNTokens {
if existingTokens.State == domain.MFAStateNotReady {
wm.WebAuthNTokens[i] = token
replaced = true
}
}
if !replaced {
wm.WebAuthNTokens = append(wm.WebAuthNTokens, token)
}
case *user.HumanPasswordlessVerifiedEvent:
idx, token := wm.WebAuthNTokenByID(e.WebAuthNTokenID)
if idx < 0 {
continue
}
token.appendVerifiedEvent(e)
case *user.HumanWebAuthNRemovedEvent:
token.appendVerifiedEvent(&e.HumanWebAuthNVerifiedEvent)
case *user.HumanPasswordlessRemovedEvent:
idx, _ := wm.WebAuthNTokenByID(e.WebAuthNTokenID)
if idx < 0 {
continue
@ -222,8 +244,7 @@ func (rm *HumanPasswordlessTokensReadModel) Query() *eventstore.SearchQueryBuild
EventTypes(
user.HumanPasswordlessTokenAddedType,
user.HumanPasswordlessTokenVerifiedType,
user.HumanPasswordlessTokenRemovedType,
user.UserV1MFAOTPRemovedType)
user.HumanPasswordlessTokenRemovedType)
}
@ -235,3 +256,114 @@ func (wm *HumanPasswordlessTokensReadModel) WebAuthNTokenByID(id string) (idx in
}
return -1, nil
}
type HumanU2FLoginReadModel struct {
eventstore.WriteModel
AuthReqID string
Challenge string
State domain.UserState
}
func NewHumanU2FLoginReadModel(userID, authReqID, resourceOwner string) *HumanU2FLoginReadModel {
return &HumanU2FLoginReadModel{
WriteModel: eventstore.WriteModel{
AggregateID: userID,
ResourceOwner: resourceOwner,
},
AuthReqID: authReqID,
}
}
func (wm *HumanU2FLoginReadModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *user.HumanU2FBeginLoginEvent:
if e.AuthRequestInfo.ID != wm.AuthReqID {
continue
}
wm.WriteModel.AppendEvents(e)
case *user.UserRemovedEvent:
wm.WriteModel.AppendEvents(e)
}
}
}
func (wm *HumanU2FLoginReadModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *user.HumanU2FBeginLoginEvent:
wm.Challenge = e.Challenge
wm.State = domain.UserStateActive
case *user.UserRemovedEvent:
wm.State = domain.UserStateDeleted
}
}
return wm.WriteModel.Reduce()
}
func (rm *HumanU2FLoginReadModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(rm.AggregateID).
ResourceOwner(rm.ResourceOwner).
EventTypes(
user.HumanU2FTokenBeginLoginType,
user.UserRemovedType,
)
}
type HumanPasswordlessLoginReadModel struct {
eventstore.WriteModel
AuthReqID string
Challenge string
State domain.UserState
}
func NewHumanPasswordlessLoginReadModel(userID, authReqID, resourceOwner string) *HumanPasswordlessLoginReadModel {
return &HumanPasswordlessLoginReadModel{
WriteModel: eventstore.WriteModel{
AggregateID: userID,
ResourceOwner: resourceOwner,
},
AuthReqID: authReqID,
}
}
func (wm *HumanPasswordlessLoginReadModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *user.HumanPasswordlessBeginLoginEvent:
if e.AuthRequestInfo.ID != wm.AuthReqID {
continue
}
wm.WriteModel.AppendEvents(e)
case *user.UserRemovedEvent:
wm.WriteModel.AppendEvents(e)
}
}
}
func (wm *HumanPasswordlessLoginReadModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *user.HumanPasswordlessBeginLoginEvent:
wm.Challenge = e.Challenge
wm.State = domain.UserStateActive
case *user.UserRemovedEvent:
wm.State = domain.UserStateDeleted
}
}
return wm.WriteModel.Reduce()
}
func (rm *HumanPasswordlessLoginReadModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(rm.AggregateID).
ResourceOwner(rm.ResourceOwner).
EventTypes(
user.HumanPasswordlessTokenBeginLoginType,
user.UserRemovedType,
)
}

View File

@ -51,7 +51,7 @@ func (r *CommandSide) ChangeMachine(ctx context.Context, machine *domain.Machine
changedEvent, hasChanged := existingUser.NewChangedEvent(ctx, machine.Name, machine.Description)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.Email.NotChanged")
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2n8vs", "Errors.User.Email.NotChanged")
}
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
userAgg.PushEvents(changedEvent)

View File

@ -0,0 +1,83 @@
package command
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/telemetry/tracing"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
"time"
)
const (
yearLayout = "2006-01-02"
defaultExpirationDate = "9999-01-01"
)
func (r *CommandSide) AddUserMachineKey(ctx context.Context, machineKey *domain.MachineKey, resourceOwner string) (*domain.MachineKey, error) {
err := r.checkUserExists(ctx, machineKey.AggregateID, resourceOwner)
if err != nil {
return nil, err
}
keyID, err := r.idGenerator.Next()
if err != nil {
return nil, err
}
keyWriteModel := NewMachineKeyWriteModel(machineKey.AggregateID, keyID, resourceOwner)
userAgg := UserAggregateFromWriteModel(&keyWriteModel.WriteModel)
err = r.eventstore.FilterToQueryReducer(ctx, keyWriteModel)
if err != nil {
return nil, err
}
if machineKey.ExpirationDate.IsZero() {
machineKey.ExpirationDate, err = time.Parse(yearLayout, defaultExpirationDate)
if err != nil {
logging.Log("COMMAND9-v8jMi").WithError(err).Warn("unable to set default date")
return nil, errors.ThrowInternal(err, "COMMAND-38jfus", "Errors.Internal")
}
}
if machineKey.ExpirationDate.Before(time.Now()) {
return nil, errors.ThrowInvalidArgument(nil, "COMMAND-38vns", "Errors.MachineKey.ExpireBeforeNow")
}
machineKey.GenerateNewMachineKeyPair(r.machineKeySize)
userAgg.PushEvents(user.NewMachineKeyAddedEvent(ctx, keyID, machineKey.Type, machineKey.ExpirationDate, machineKey.PublicKey))
err = r.eventstore.PushAggregate(ctx, keyWriteModel, userAgg)
if err != nil {
return nil, err
}
key := keyWriteModelToMachineKey(keyWriteModel)
key.PrivateKey = machineKey.PrivateKey
return key, nil
}
func (r *CommandSide) RemoveUserMachineKey(ctx context.Context, userID, keyID, resourceOwner string) error {
keyWriteModel, err := r.machineKeyWriteModelByID(ctx, userID, keyID, resourceOwner)
if err != nil {
return err
}
if keyWriteModel.State == domain.MachineKeyStateUnspecified || keyWriteModel.State == domain.MachineKeyStateRemoved {
return errors.ThrowNotFound(nil, "COMMAND-4m77G", "Errors.User.Machine.Key.NotFound")
}
userAgg := UserAggregateFromWriteModel(&keyWriteModel.WriteModel)
userAgg.PushEvents(user.NewMachineKeyRemovedEvent(ctx, keyID))
return r.eventstore.PushAggregate(ctx, keyWriteModel, userAgg)
}
func (r *CommandSide) machineKeyWriteModelByID(ctx context.Context, userID, keyID, resourceOwner string) (writeModel *MachineKeyWriteModel, err error) {
if userID == "" {
return nil, errors.ThrowInvalidArgument(nil, "COMMAND-4n8vs", "Errors.User.UserIDMissing")
}
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel = NewMachineKeyWriteModel(userID, keyID, resourceOwner)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}

View File

@ -0,0 +1,74 @@
package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
"time"
)
type MachineKeyWriteModel struct {
eventstore.WriteModel
KeyID string
KeyType domain.MachineKeyType
ExpirationDate time.Time
State domain.MachineKeyState
}
func NewMachineKeyWriteModel(userID, keyID, resourceOwner string) *MachineKeyWriteModel {
return &MachineKeyWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: userID,
ResourceOwner: resourceOwner,
},
KeyID: keyID,
}
}
func (wm *MachineKeyWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *user.MachineKeyAddedEvent:
if wm.KeyID != e.KeyID {
continue
}
wm.WriteModel.AppendEvents(e)
case *user.MachineKeyRemovedEvent:
if wm.KeyID != e.KeyID {
continue
}
wm.WriteModel.AppendEvents(e)
case *user.UserRemovedEvent:
wm.WriteModel.AppendEvents(e)
}
}
}
func (wm *MachineKeyWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *user.MachineKeyAddedEvent:
wm.KeyID = e.KeyID
wm.KeyType = e.KeyType
wm.ExpirationDate = e.ExpirationDate
wm.State = domain.MachineKeyStateActive
case *user.MachineKeyRemovedEvent:
wm.State = domain.MachineKeyStateRemoved
case *user.UserRemovedEvent:
wm.State = domain.MachineKeyStateRemoved
}
}
return wm.WriteModel.Reduce()
}
func (wm *MachineKeyWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID).
ResourceOwner(wm.ResourceOwner).
EventTypes(
user.MachineKeyAddedEventType,
user.MachineKeyRemovedEventType,
user.UserRemovedType)
}

View File

@ -39,6 +39,8 @@ func (wm *UserWriteModel) Reduce() error {
case *user.HumanRegisteredEvent:
wm.UserName = e.UserName
wm.UserState = domain.UserStateInitial
case *user.HumanInitializedCheckSucceededEvent:
wm.UserState = domain.UserStateActive
case *user.MachineAddedEvent:
wm.UserName = e.UserName
wm.UserState = domain.UserStateActive

View File

@ -0,0 +1,150 @@
package domain
import (
"github.com/caos/zitadel/internal/errors"
"golang.org/x/text/language"
"strings"
"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
MaxAuthAge uint32
Request Request
levelOfAssurance LevelOfAssurance
UserID string
LoginName string
DisplayName string
UserOrgID string
RequestedOrgID string
RequestedOrgName string
SelectedIDPConfigID string
LinkingUsers []*ExternalUser
PossibleSteps []NextStep
PasswordVerified bool
MFAsVerified []MFAType
Audience []string
AuthTime time.Time
Code string
LoginPolicy *LoginPolicy
AllowedExternalIDPs []*IDPProvider
}
type ExternalUser struct {
IDPConfigID string
ExternalUserID string
DisplayName string
PreferredUsername string
FirstName string
LastName string
NickName string
Email string
IsEmailVerified bool
PreferredLanguage language.Tag
Phone string
IsPhoneVerified bool
}
type Prompt int32
const (
PromptUnspecified Prompt = iota
PromptNone
PromptLogin
PromptConsent
PromptSelectAccount
)
type LevelOfAssurance int
const (
LevelOfAssuranceNone LevelOfAssurance = iota
)
type MFAType int
const (
MFATypeOTP MFAType = iota
MFATypeU2F
MFATypeU2FUserVerification
)
type MFALevel int
const (
MFALevelNotSetUp MFALevel = iota
MFALevelSecondFactor
MFALevelMultiFactor
MFALevelMultiFactorCertified
)
func NewAuthRequestFromType(requestType AuthRequestType) (*AuthRequest, error) {
request, ok := authRequestTypeMapping[requestType]
if !ok {
return nil, errors.ThrowInvalidArgument(nil, "DOMAIN-ds2kl", "invalid request type")
}
return &AuthRequest{Request: request}, nil
}
func (a *AuthRequest) WithCurrentInfo(info *BrowserInfo) *AuthRequest {
a.BrowserInfo = info
return a
}
func (a *AuthRequest) SetUserInfo(userID, loginName, displayName, userOrgID string) {
a.UserID = userID
a.LoginName = loginName
a.DisplayName = displayName
a.UserOrgID = userOrgID
}
func (a *AuthRequest) MFALevel() MFALevel {
return -1
//PLANNED: check a.PossibleLOAs (and Prompt Login?)
}
func (a *AuthRequest) AppendAudIfNotExisting(aud string) {
for _, a := range a.Audience {
if a == aud {
return
}
}
a.Audience = append(a.Audience, aud)
}
func (a *AuthRequest) GetScopeProjectIDsForAud() []string {
projectIDs := make([]string, 0)
switch request := a.Request.(type) {
case *AuthRequestOIDC:
for _, scope := range request.Scopes {
if strings.HasPrefix(scope, ProjectIDScope) && strings.HasSuffix(scope, AudSuffix) {
projectIDs = append(projectIDs, strings.TrimSuffix(strings.TrimPrefix(scope, ProjectIDScope), AudSuffix))
}
}
}
return projectIDs
}
func (a *AuthRequest) GetScopeOrgPrimaryDomain() string {
switch request := a.Request.(type) {
case *AuthRequestOIDC:
for _, scope := range request.Scopes {
if strings.HasPrefix(scope, OrgDomainPrimaryScope) {
return strings.TrimPrefix(scope, OrgDomainPrimaryScope)
}
}
}
return ""
}

View File

@ -0,0 +1,22 @@
package domain
import (
"net"
net_http "net/http"
http_util "github.com/caos/zitadel/internal/api/http"
)
type BrowserInfo struct {
UserAgent string
AcceptLanguage string
RemoteIP net.IP
}
func BrowserInfoFromRequest(r *net_http.Request) *BrowserInfo {
return &BrowserInfo{
UserAgent: r.Header.Get(http_util.UserAgentHeader),
AcceptLanguage: r.Header.Get(http_util.AcceptLanguage),
RemoteIP: http_util.RemoteIPFromRequest(r),
}
}

View File

@ -60,15 +60,15 @@ func (u *Human) IsValid() bool {
return u.Profile != nil && u.FirstName != "" && u.LastName != "" && u.Email != nil && u.Email.IsValid() && u.Phone == nil || (u.Phone != nil && u.Phone.PhoneNumber != "" && u.Phone.IsValid())
}
func (u *Human) CheckOrgIAMPolicy(userName string, policy *OrgIAMPolicy) error {
func (u *Human) CheckOrgIAMPolicy(policy *OrgIAMPolicy) error {
if policy == nil {
return caos_errors.ThrowPreconditionFailed(nil, "DOMAIN-zSH7j", "Errors.Users.OrgIamPolicyNil")
}
if policy.UserLoginMustBeDomain && strings.Contains(userName, "@") {
if policy.UserLoginMustBeDomain && strings.Contains(u.Username, "@") {
return caos_errors.ThrowPreconditionFailed(nil, "DOMAIN-se4sJ", "Errors.User.EmailAsUsernameNotAllowed")
}
if !policy.UserLoginMustBeDomain && u.Profile != nil && userName == "" && u.Email != nil {
userName = u.EmailAddress
if !policy.UserLoginMustBeDomain && u.Profile != nil && u.Username == "" && u.Email != nil {
u.Username = u.EmailAddress
}
return nil
}

View File

@ -1,6 +1,9 @@
package domain
import es_models "github.com/caos/zitadel/internal/eventstore/models"
import (
"bytes"
es_models "github.com/caos/zitadel/internal/eventstore/models"
)
type WebAuthNToken struct {
es_models.ObjectRoot
@ -55,3 +58,12 @@ func GetTokenToVerify(tokens []*WebAuthNToken) (int, *WebAuthNToken) {
}
return -1, nil
}
func GetTokenByKeyID(tokens []*WebAuthNToken, keyID []byte) (int, *WebAuthNToken) {
for i, token := range tokens {
if bytes.Compare(token.KeyID, keyID) == 0 {
return i, token
}
}
return -1, nil
}

Some files were not shown because too many files have changed in this diff Show More