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
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") logging.Log("MAIN-s9KOw").OnError(err).Fatal("error starting authz repo")
var authRepo *auth_es.EsRepository var authRepo *auth_es.EsRepository
if *authEnabled || *oidcEnabled || *loginEnabled { 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") 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) startUI(ctx, conf, authRepo, command, query)
if *notificationEnabled { if *notificationEnabled {
notification.Start(ctx, conf.Notification, conf.SystemDefaults) notification.Start(ctx, conf.Notification, conf.SystemDefaults, command)
} }
<-ctx.Done() <-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)) apis.RegisterServer(ctx, auth.CreateServer(command, query, authRepo))
} }
if *oidcEnabled { 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.RegisterHandler("/oauth/v2", op.HttpHandler())
} }
apis.Start(ctx) 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.OrgRepo
eventstore.IAMRepository eventstore.IAMRepository
eventstore.AdministratorRepo eventstore.AdministratorRepo
eventstore.UserRepo
} }
func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, roles []string) (*EsRepository, error) { 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{ AdministratorRepo: eventstore.AdministratorRepo{
View: view, View: view,
}, },
UserRepo: eventstore.UserRepo{
UserEvents: user,
OrgEvents: org,
View: view,
SystemDefaults: systemDefaults,
},
}, nil }, nil
} }

View File

@@ -12,7 +12,7 @@ import (
func loginPolicyToDomain(policy *admin.DefaultLoginPolicyRequest) *domain.LoginPolicy { func loginPolicyToDomain(policy *admin.DefaultLoginPolicyRequest) *domain.LoginPolicy {
return &domain.LoginPolicy{ return &domain.LoginPolicy{
AllowUsernamePassword: policy.AllowUsernamePassword, AllowUsernamePassword: policy.AllowUsernamePassword,
AllowExternalIdp: policy.AllowExternalIdp, AllowExternalIDP: policy.AllowExternalIdp,
AllowRegister: policy.AllowRegister, AllowRegister: policy.AllowRegister,
ForceMFA: policy.ForceMfa, ForceMFA: policy.ForceMfa,
PasswordlessType: passwordlessTypeToDomain(policy.PasswordlessType), PasswordlessType: passwordlessTypeToDomain(policy.PasswordlessType),
@@ -22,7 +22,7 @@ func loginPolicyToDomain(policy *admin.DefaultLoginPolicyRequest) *domain.LoginP
func loginPolicyFromDomain(policy *domain.LoginPolicy) *admin.DefaultLoginPolicy { func loginPolicyFromDomain(policy *domain.LoginPolicy) *admin.DefaultLoginPolicy {
return &admin.DefaultLoginPolicy{ return &admin.DefaultLoginPolicy{
AllowUsernamePassword: policy.AllowUsernamePassword, AllowUsernamePassword: policy.AllowUsernamePassword,
AllowExternalIdp: policy.AllowExternalIdp, AllowExternalIdp: policy.AllowExternalIDP,
AllowRegister: policy.AllowRegister, AllowRegister: policy.AllowRegister,
ForceMfa: policy.ForceMFA, ForceMfa: policy.ForceMFA,
PasswordlessType: passwordlessTypeFromDomain(policy.PasswordlessType), 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) { func (s *Server) VerifyMfaOTP(ctx context.Context, request *auth.VerifyMfaOtp) (*empty.Empty, error) {
ctxData := authz.GetCtxData(ctx) 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 return &empty.Empty{}, err
} }
func (s *Server) RemoveMfaOTP(ctx context.Context, _ *empty.Empty) (_ *empty.Empty, err error) { func (s *Server) RemoveMfaOTP(ctx context.Context, _ *empty.Empty) (_ *empty.Empty, err error) {
ctxData := authz.GetCtxData(ctx) 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 return &empty.Empty{}, err
} }
func (s *Server) AddMyMfaU2F(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNResponse, err error) { func (s *Server) AddMyMfaU2F(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNResponse, err error) {
ctxData := authz.GetCtxData(ctx) 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 { if err != nil {
return nil, err 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) { func (s *Server) VerifyMyMfaU2F(ctx context.Context, request *auth.VerifyWebAuthN) (*empty.Empty, error) {
ctxData := authz.GetCtxData(ctx) 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 return &empty.Empty{}, err
} }
func (s *Server) RemoveMyMfaU2F(ctx context.Context, id *auth.WebAuthNTokenID) (*empty.Empty, error) { func (s *Server) RemoveMyMfaU2F(ctx context.Context, id *auth.WebAuthNTokenID) (*empty.Empty, error) {
ctxData := authz.GetCtxData(ctx) 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 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) { func (s *Server) AddMyPasswordless(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNResponse, err error) {
ctxData := authz.GetCtxData(ctx) 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 { if err != nil {
return nil, err 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) { func (s *Server) VerifyMyPasswordless(ctx context.Context, request *auth.VerifyWebAuthN) (*empty.Empty, error) {
ctxData := authz.GetCtxData(ctx) 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 return &empty.Empty{}, err
} }
func (s *Server) RemoveMyPasswordless(ctx context.Context, id *auth.WebAuthNTokenID) (*empty.Empty, error) { func (s *Server) RemoveMyPasswordless(ctx context.Context, id *auth.WebAuthNTokenID) (*empty.Empty, error) {
ctxData := authz.GetCtxData(ctx) 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 return &empty.Empty{}, err
} }

View File

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

View File

@@ -2,7 +2,6 @@ package management
import ( import (
"context" "context"
"github.com/golang/protobuf/ptypes/empty" "github.com/golang/protobuf/ptypes/empty"
"github.com/caos/zitadel/internal/api/authz" "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) { 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 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) { 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) { 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) { 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) { 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) { 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 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 ( import (
"context" "context"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/pkg/grpc/management" "github.com/caos/zitadel/pkg/grpc/management"
"github.com/golang/protobuf/ptypes/empty" "github.com/golang/protobuf/ptypes/empty"
) )
func (s *Server) AddMachineKey(ctx context.Context, req *management.AddMachineKeyRequest) (*management.AddMachineKeyResponse, error) { 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 { if err != nil {
return nil, err return nil, err
} }
return addMachineKeyFromModel(key), nil return addMachineKeyFromDomain(key), nil
} }
func (s *Server) DeleteMachineKey(ctx context.Context, req *management.MachineKeyIDRequest) (*empty.Empty, error) { 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 return &empty.Empty{}, err
} }

View File

@@ -2,6 +2,7 @@ package management
import ( import (
"encoding/json" "encoding/json"
"google.golang.org/protobuf/types/known/timestamppb"
"time" "time"
"github.com/caos/zitadel/internal/api/authz" "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{} expirationDate := time.Time{}
if key.ExpirationDate != nil { if key.ExpirationDate != nil {
var err error 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") logging.Log("MANAG-iNshR").OnError(err).Debug("unable to parse expiration date")
} }
return &usr_model.MachineKey{ return &domain.MachineKey{
ExpirationDate: expirationDate, ExpirationDate: expirationDate,
Type: machineKeyTypeToModel(key.Type), Type: machineKeyTypeToDomain(key.Type),
ObjectRoot: models.ObjectRoot{AggregateID: key.UserId}, ObjectRoot: models.ObjectRoot{AggregateID: key.UserId},
} }
} }
func addMachineKeyFromModel(key *usr_model.MachineKey) *management.AddMachineKeyResponse { func addMachineKeyFromDomain(key *domain.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")
detail, err := json.Marshal(struct { detail, err := json.Marshal(struct {
Type string `json:"type"` Type string `json:"type"`
KeyID string `json:"keyId"` KeyID string `json:"keyId"`
@@ -112,20 +107,29 @@ func addMachineKeyFromModel(key *usr_model.MachineKey) *management.AddMachineKey
return &management.AddMachineKeyResponse{ return &management.AddMachineKeyResponse{
Id: key.KeyID, Id: key.KeyID,
CreationDate: creationDate, CreationDate: timestamppb.New(key.CreationDate),
ExpirationDate: expirationDate, ExpirationDate: timestamppb.New(key.ExpirationDate),
Sequence: key.Sequence, Sequence: key.Sequence,
KeyDetails: detail, 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 { switch typ {
case management.MachineKeyType_MACHINEKEY_JSON: case management.MachineKeyType_MACHINEKEY_JSON:
return usr_model.MachineKeyTypeJSON return domain.MachineKeyTypeJSON
default: 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 userAgentID = authReq.AgentID
applicationID = authReq.ApplicationID 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 { if err != nil {
return "", time.Time{}, err return "", time.Time{}, err
} }
@@ -113,7 +113,11 @@ func (o *OPStorage) TerminateSession(ctx context.Context, userID, clientID strin
if !ok { if !ok {
return errors.ThrowPreconditionFailed(nil, "OIDC-fso7F", "no user agent id") 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) { 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 ( import (
"context" "context"
"github.com/caos/zitadel/internal/v2/domain"
"net" "net"
"time" "time"
@@ -10,7 +11,6 @@ import (
"golang.org/x/text/language" "golang.org/x/text/language"
http_utils "github.com/caos/zitadel/internal/api/http" http_utils "github.com/caos/zitadel/internal/api/http"
"github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
) )
@@ -22,7 +22,7 @@ const (
) )
type AuthRequest struct { type AuthRequest struct {
*model.AuthRequest *domain.AuthRequest
} }
func (a *AuthRequest) GetID() string { func (a *AuthRequest) GetID() string {
@@ -92,26 +92,26 @@ func (a *AuthRequest) GetSubject() string {
func (a *AuthRequest) Done() bool { func (a *AuthRequest) Done() bool {
for _, step := range a.PossibleSteps { for _, step := range a.PossibleSteps {
if step.Type() == model.NextStepRedirectToCallback { if step.Type() == domain.NextStepRedirectToCallback {
return true return true
} }
} }
return false return false
} }
func (a *AuthRequest) oidc() *model.AuthRequestOIDC { func (a *AuthRequest) oidc() *domain.AuthRequestOIDC {
return a.Request.(*model.AuthRequestOIDC) return a.Request.(*domain.AuthRequestOIDC)
} }
func AuthRequestFromBusiness(authReq *model.AuthRequest) (_ op.AuthRequest, err error) { func AuthRequestFromBusiness(authReq *domain.AuthRequest) (_ op.AuthRequest, err error) {
if _, ok := authReq.Request.(*model.AuthRequestOIDC); !ok { if _, ok := authReq.Request.(*domain.AuthRequestOIDC); !ok {
return nil, errors.ThrowInvalidArgument(nil, "OIDC-Haz7A", "auth request is not of type oidc") return nil, errors.ThrowInvalidArgument(nil, "OIDC-Haz7A", "auth request is not of type oidc")
} }
return &AuthRequest{authReq}, nil return &AuthRequest{authReq}, nil
} }
func CreateAuthRequestToBusiness(ctx context.Context, authReq *oidc.AuthRequest, userAgentID, userID string) *model.AuthRequest { func CreateAuthRequestToBusiness(ctx context.Context, authReq *oidc.AuthRequest, userAgentID, userID string) *domain.AuthRequest {
return &model.AuthRequest{ return &domain.AuthRequest{
AgentID: userAgentID, AgentID: userAgentID,
BrowserInfo: ParseBrowserInfoFromContext(ctx), BrowserInfo: ParseBrowserInfoFromContext(ctx),
ApplicationID: authReq.ClientID, ApplicationID: authReq.ClientID,
@@ -123,7 +123,7 @@ func CreateAuthRequestToBusiness(ctx context.Context, authReq *oidc.AuthRequest,
LoginHint: authReq.LoginHint, LoginHint: authReq.LoginHint,
MaxAuthAge: authReq.MaxAge, MaxAuthAge: authReq.MaxAge,
UserID: userID, UserID: userID,
Request: &model.AuthRequestOIDC{ Request: &domain.AuthRequestOIDC{
Scopes: authReq.Scopes, Scopes: authReq.Scopes,
ResponseType: ResponseTypeToBusiness(authReq.ResponseType), ResponseType: ResponseTypeToBusiness(authReq.ResponseType),
Nonce: authReq.Nonce, 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) userAgent, acceptLang := HttpHeadersFromContext(ctx)
ip := IpFromContext(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) { func HttpHeadersFromContext(ctx context.Context) (userAgent, acceptLang string) {
@@ -160,22 +160,22 @@ func IpFromContext(ctx context.Context) net.IP {
return net.ParseIP(ipString) return net.ParseIP(ipString)
} }
func PromptToBusiness(prompt oidc.Prompt) model.Prompt { func PromptToBusiness(prompt oidc.Prompt) domain.Prompt {
switch prompt { switch prompt {
case oidc.PromptNone: case oidc.PromptNone:
return model.PromptNone return domain.PromptNone
case oidc.PromptLogin: case oidc.PromptLogin:
return model.PromptLogin return domain.PromptLogin
case oidc.PromptConsent: case oidc.PromptConsent:
return model.PromptConsent return domain.PromptConsent
case oidc.PromptSelectAccount: case oidc.PromptSelectAccount:
return model.PromptSelectAccount return domain.PromptSelectAccount
default: default:
return model.PromptUnspecified return domain.PromptUnspecified
} }
} }
func ACRValuesToBusiness(values []string) []model.LevelOfAssurance { func ACRValuesToBusiness(values []string) []domain.LevelOfAssurance {
return nil return nil
} }
@@ -190,52 +190,52 @@ func UILocalesToBusiness(tags []language.Tag) []string {
return locales return locales
} }
func ResponseTypeToBusiness(responseType oidc.ResponseType) model.OIDCResponseType { func ResponseTypeToBusiness(responseType oidc.ResponseType) domain.OIDCResponseType {
switch responseType { switch responseType {
case oidc.ResponseTypeCode: case oidc.ResponseTypeCode:
return model.OIDCResponseTypeCode return domain.OIDCResponseTypeCode
case oidc.ResponseTypeIDTokenOnly: case oidc.ResponseTypeIDTokenOnly:
return model.OIDCResponseTypeIdToken return domain.OIDCResponseTypeIDToken
case oidc.ResponseTypeIDToken: case oidc.ResponseTypeIDToken:
return model.OIDCResponseTypeIdTokenToken return domain.OIDCResponseTypeIDTokenToken
default: default:
return model.OIDCResponseTypeCode return domain.OIDCResponseTypeCode
} }
} }
func ResponseTypeToOIDC(responseType model.OIDCResponseType) oidc.ResponseType { func ResponseTypeToOIDC(responseType domain.OIDCResponseType) oidc.ResponseType {
switch responseType { switch responseType {
case model.OIDCResponseTypeCode: case domain.OIDCResponseTypeCode:
return oidc.ResponseTypeCode return oidc.ResponseTypeCode
case model.OIDCResponseTypeIdTokenToken: case domain.OIDCResponseTypeIDTokenToken:
return oidc.ResponseTypeIDToken return oidc.ResponseTypeIDToken
case model.OIDCResponseTypeIdToken: case domain.OIDCResponseTypeIDToken:
return oidc.ResponseTypeIDTokenOnly return oidc.ResponseTypeIDTokenOnly
default: default:
return oidc.ResponseTypeCode return oidc.ResponseTypeCode
} }
} }
func CodeChallengeToBusiness(challenge string, method oidc.CodeChallengeMethod) *model.OIDCCodeChallenge { func CodeChallengeToBusiness(challenge string, method oidc.CodeChallengeMethod) *domain.OIDCCodeChallenge {
if challenge == "" { if challenge == "" {
return nil return nil
} }
challengeMethod := model.CodeChallengeMethodPlain challengeMethod := domain.CodeChallengeMethodPlain
if method == oidc.CodeChallengeMethodS256 { if method == oidc.CodeChallengeMethodS256 {
challengeMethod = model.CodeChallengeMethodS256 challengeMethod = domain.CodeChallengeMethodS256
} }
return &model.OIDCCodeChallenge{ return &domain.OIDCCodeChallenge{
Challenge: challenge, Challenge: challenge,
Method: challengeMethod, Method: challengeMethod,
} }
} }
func CodeChallengeToOIDC(challenge *model.OIDCCodeChallenge) *oidc.CodeChallenge { func CodeChallengeToOIDC(challenge *domain.OIDCCodeChallenge) *oidc.CodeChallenge {
if challenge == nil { if challenge == nil {
return nil return nil
} }
challengeMethod := oidc.CodeChallengeMethodPlain challengeMethod := oidc.CodeChallengeMethodPlain
if challenge.Method == model.CodeChallengeMethodS256 { if challenge.Method == domain.CodeChallengeMethodS256 {
challengeMethod = oidc.CodeChallengeMethodS256 challengeMethod = oidc.CodeChallengeMethodS256
} }
return &oidc.CodeChallenge{ 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 { switch mfaType {
case model.MFATypeOTP: case domain.MFATypeOTP:
return amrOTP return amrOTP
case model.MFATypeU2F, case domain.MFATypeU2F,
model.MFATypeU2FUserVerification: domain.MFATypeU2FUserVerification:
return amrUserPresence return amrUserPresence
default: default:
return "" return ""

View File

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

View File

@@ -2,32 +2,30 @@ package repository
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/auth_request/model" "github.com/caos/zitadel/internal/v2/domain"
org_model "github.com/caos/zitadel/internal/org/model"
user_model "github.com/caos/zitadel/internal/user/model"
) )
type AuthRequestRepository interface { type AuthRequestRepository interface {
CreateAuthRequest(ctx context.Context, request *model.AuthRequest) (*model.AuthRequest, error) CreateAuthRequest(ctx context.Context, request *domain.AuthRequest) (*domain.AuthRequest, error)
AuthRequestByID(ctx context.Context, id, userAgentID string) (*model.AuthRequest, error) AuthRequestByID(ctx context.Context, id, userAgentID string) (*domain.AuthRequest, error)
AuthRequestByIDCheckLoggedIn(ctx context.Context, id, userAgentID string) (*model.AuthRequest, error) AuthRequestByIDCheckLoggedIn(ctx context.Context, id, userAgentID string) (*domain.AuthRequest, error)
AuthRequestByCode(ctx context.Context, code string) (*model.AuthRequest, error) AuthRequestByCode(ctx context.Context, code string) (*domain.AuthRequest, error)
SaveAuthCode(ctx context.Context, id, code, userAgentID string) error SaveAuthCode(ctx context.Context, id, code, userAgentID string) error
DeleteAuthRequest(ctx context.Context, id string) error DeleteAuthRequest(ctx context.Context, id string) error
CheckLoginName(ctx context.Context, id, loginName, userAgentID 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 SelectUser(ctx context.Context, id, userID, userAgentID string) error
SelectExternalIDP(ctx context.Context, authReqID, idpConfigID, 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 VerifyMFAOTP(ctx context.Context, authRequestID, userID, resourceOwner, code, userAgentID string, info *domain.BrowserInfo) error
BeginMFAU2FLogin(ctx context.Context, userID, authRequestID, userAgentID string) (*user_model.WebAuthNLogin, error) BeginMFAU2FLogin(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string) (*domain.WebAuthNLogin, error)
VerifyMFAU2F(ctx context.Context, userID, authRequestID, userAgentID string, credentialData []byte, info *model.BrowserInfo) error VerifyMFAU2F(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string, credentialData []byte, info *domain.BrowserInfo) error
BeginPasswordlessLogin(ctx context.Context, userID, authRequestID, userAgentID string) (*user_model.WebAuthNLogin, error) BeginPasswordlessLogin(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string) (*domain.WebAuthNLogin, error)
VerifyPasswordless(ctx context.Context, userID, authRequestID, userAgentID string, credentialData []byte, info *model.BrowserInfo) 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 LinkExternalUsers(ctx context.Context, authReqID, userAgentID string, info *domain.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 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 ResetLinkingUsers(ctx context.Context, authReqID, userAgentID string) error
} }

View File

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

View File

@@ -2,12 +2,8 @@ package eventstore
import ( import (
"context" "context"
"strings"
"github.com/caos/logging" "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/errors"
"github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/telemetry/tracing" "github.com/caos/zitadel/internal/telemetry/tracing"
usr_model "github.com/caos/zitadel/internal/user/model" usr_model "github.com/caos/zitadel/internal/user/model"
user_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" user_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
@@ -22,34 +18,6 @@ type TokenRepo struct {
View *view.View 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) { func (repo *TokenRepo) IsTokenValid(ctx context.Context, userID, tokenID string) (bool, error) {
token, err := repo.TokenByID(ctx, userID, tokenID) token, err := repo.TokenByID(ctx, userID, tokenID)
if err == nil { if err == nil {

View File

@@ -36,14 +36,6 @@ func (repo *UserRepo) Health(ctx context.Context) error {
return repo.UserEvents.Health(ctx) 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) { 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 policyResourceOwner := authz.GetCtxData(ctx).OrgID
if resourceOwner != "" { if resourceOwner != "" {
@@ -133,18 +125,6 @@ func (repo *UserRepo) MyEmail(ctx context.Context) (*model.Email, error) {
return user.GetEmail() 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) { func (repo *UserRepo) MyPhone(ctx context.Context) (*model.Phone, error) {
user, err := repo.UserByID(ctx, authz.GetCtxData(ctx).UserID) user, err := repo.UserByID(ctx, authz.GetCtxData(ctx).UserID)
if err != nil { if err != nil {
@@ -156,21 +136,6 @@ func (repo *UserRepo) MyPhone(ctx context.Context) (*model.Phone, error) {
return user.GetPhone() 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) { func (repo *UserRepo) MyAddress(ctx context.Context) (*model.Address, error) {
user, err := repo.UserByID(ctx, authz.GetCtxData(ctx).UserID) user, err := repo.UserByID(ctx, authz.GetCtxData(ctx).UserID)
if err != nil { if err != nil {
@@ -182,34 +147,6 @@ func (repo *UserRepo) MyAddress(ctx context.Context) (*model.Address, error) {
return user.GetAddress() 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) { func (repo *UserRepo) MyUserMFAs(ctx context.Context) ([]*model.MultiFactor, error) {
user, err := repo.UserByID(ctx, authz.GetCtxData(ctx).UserID) user, err := repo.UserByID(ctx, authz.GetCtxData(ctx).UserID)
if err != nil { if err != nil {
@@ -225,142 +162,24 @@ func (repo *UserRepo) MyUserMFAs(ctx context.Context) ([]*model.MultiFactor, err
return mfas, nil 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) { func (repo *UserRepo) GetPasswordless(ctx context.Context, userID string) ([]*model.WebAuthNToken, error) {
return repo.UserEvents.GetPasswordless(ctx, userID) 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) { func (repo *UserRepo) GetMyPasswordless(ctx context.Context) ([]*model.WebAuthNToken, error) {
return repo.UserEvents.GetPasswordless(ctx, authz.GetCtxData(ctx).UserID) return repo.UserEvents.GetPasswordless(ctx, authz.GetCtxData(ctx).UserID)
} }
func (repo *UserRepo) VerifyPasswordlessSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error { func (repo *UserRepo) UserSessionUserIDsByAgentID(ctx context.Context, agentID string) ([]string, 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 {
userSessions, err := repo.View.UserSessionsByAgentID(agentID) userSessions, err := repo.View.UserSessionsByAgentID(agentID)
if err != nil { if err != nil {
return err return nil, err
} }
userIDs := make([]string, len(userSessions)) userIDs := make([]string, len(userSessions))
for i, session := range userSessions { for i, session := range userSessions {
userIDs[i] = session.UserID 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) { 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 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) { 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) changes, err := repo.UserEvents.UserChanges(ctx, authz.GetCtxData(ctx).UserID, lastSequence, limit, sortAscending)
if err != nil { if err != nil {
@@ -405,19 +245,6 @@ func (repo *UserRepo) MyUserChanges(ctx context.Context, lastSequence uint64, li
return changes, nil 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 { func checkIDs(ctx context.Context, obj es_models.ObjectRoot) error {
if obj.AggregateID != authz.GetCtxData(ctx).UserID { if obj.AggregateID != authz.GetCtxData(ctx).UserID {
return errors.ThrowPermissionDenied(nil, "EVENT-kFi9w", "object does not belong to user") 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_org "github.com/caos/zitadel/internal/org/repository/eventsourcing"
es_proj "github.com/caos/zitadel/internal/project/repository/eventsourcing" es_proj "github.com/caos/zitadel/internal/project/repository/eventsourcing"
es_user "github.com/caos/zitadel/internal/user/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" "github.com/caos/zitadel/internal/v2/query"
) )
@@ -46,7 +47,7 @@ type EsRepository struct {
eventstore.IAMRepository 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) es, err := es_int.Start(conf.Eventstore)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -131,12 +132,14 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, au
SystemDefaults: systemDefaults, SystemDefaults: systemDefaults,
}, },
eventstore.AuthRequestRepo{ eventstore.AuthRequestRepo{
Command: command,
UserEvents: user, UserEvents: user,
OrgEvents: org, OrgEvents: org,
AuthRequests: authReq, AuthRequests: authReq,
View: view, View: view,
UserSessionViewProvider: view, UserSessionViewProvider: view,
UserViewProvider: view, UserViewProvider: view,
UserCommandProvider: command,
UserEventProvider: user, UserEventProvider: user,
OrgViewProvider: view, OrgViewProvider: view,
IDPProviderViewProvider: view, IDPProviderViewProvider: view,

View File

@@ -3,11 +3,9 @@ package repository
import ( import (
"context" "context"
usr_model "github.com/caos/zitadel/internal/user/model" usr_model "github.com/caos/zitadel/internal/user/model"
"time"
) )
type TokenRepository interface { 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) IsTokenValid(ctx context.Context, userID, tokenID string) (bool, error)
TokenByID(ctx context.Context, userID, tokenID string) (*usr_model.TokenView, error) TokenByID(ctx context.Context, userID, tokenID string) (*usr_model.TokenView, error)
} }

View File

@@ -3,43 +3,18 @@ package repository
import ( import (
"context" "context"
org_model "github.com/caos/zitadel/internal/org/model"
"github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/model"
) )
type UserRepository interface { 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 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) 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 UserSessionUserIDsByAgentID(ctx context.Context, agentID string) ([]string, error)
SignOut(ctx context.Context, agentID string) error
UserByID(ctx context.Context, userID string) (*model.UserView, 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) MachineKeyByID(ctx context.Context, keyID string) (*model.MachineKeyView, error)
} }
@@ -59,8 +34,6 @@ type myUserRepo interface {
MyUserMFAs(ctx context.Context) ([]*model.MultiFactor, error) MyUserMFAs(ctx context.Context) ([]*model.MultiFactor, error)
AddMyMFAU2F(ctx context.Context) (*model.WebAuthNToken, error)
GetMyPasswordless(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) MyUserChanges(ctx context.Context, lastSequence uint64, limit uint64, sortAscending bool) (*model.UserChanges, error)

View File

@@ -1,5 +1,9 @@
package model package model
import (
"github.com/caos/zitadel/internal/v2/domain"
)
type NextStep interface { type NextStep interface {
Type() NextStepType Type() NextStepType
} }
@@ -164,3 +168,45 @@ const (
MFALevelMultiFactor MFALevelMultiFactor
MFALevelMultiFactorCertified 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" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/config/types" "github.com/caos/zitadel/internal/config/types"
caos_errs "github.com/caos/zitadel/internal/errors" 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) 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) 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) 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()) 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) 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 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 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) query := fmt.Sprintf("SELECT request, request_type FROM auth.auth_requests WHERE %s = $1", key)
err := c.client.QueryRow(query, value).Scan(&b, &requestType) err := c.client.QueryRow(query, value).Scan(&b, &requestType)
if err != nil { 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") return nil, caos_errs.ThrowInternal(err, "CACHE-as3kj", "Errors.Internal")
} }
request, err := model.NewAuthRequestFromType(requestType) request, err := domain.NewAuthRequestFromType(requestType)
if err == nil { if err == nil {
err = json.Unmarshal(b, request) err = json.Unmarshal(b, request)
} }
@@ -79,7 +79,7 @@ func (c *AuthRequestCache) getAuthRequest(key, value string) (*model.AuthRequest
return request, nil 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) b, err := json.Marshal(request)
if err != nil { if err != nil {
return caos_errs.ThrowInternal(err, "CACHE-os0GH", "Errors.Internal") 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 // 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() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateAuthRequest", arg0, arg1) ret := m.ctrl.Call(m, "UpdateAuthRequest", arg0, arg1)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)

View File

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

View File

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

View File

@@ -188,7 +188,7 @@ func (db *CRDB) handleUniqueConstraints(ctx context.Context, tx *sql.Tx, uniqueC
logging.LogWithFields("SQL-M0vsf", logging.LogWithFields("SQL-M0vsf",
"unique_type", uniqueConstraint.UniqueType, "unique_type", uniqueConstraint.UniqueType,
"unique_field", uniqueConstraint.UniqueField).WithError(err).Info("delete unique constraint failed") "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 { if len(factory.eventTypes) == 1 {
return repository.NewFilter(repository.FieldEventType, factory.eventTypes[0], repository.OperationEquals) 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 { func (factory *SearchQueryBuilder) aggregateTypeFilter() *repository.Filter {
if len(factory.aggregateTypes) == 1 { if len(factory.aggregateTypes) == 1 {
return repository.NewFilter(repository.FieldAggregateType, factory.aggregateTypes[0], repository.OperationEquals) 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 { func (factory *SearchQueryBuilder) eventSequenceFilter() *repository.Filter {

View File

@@ -2,6 +2,7 @@ package model
import ( import (
"github.com/caos/zitadel/internal/model" "github.com/caos/zitadel/internal/model"
"github.com/caos/zitadel/internal/v2/domain"
"time" "time"
) )
@@ -60,3 +61,63 @@ func (r *IDPProviderSearchRequest) EnsureLimit(limit uint64) {
func (r *IDPProviderSearchRequest) AppendAggregateIDQuery(aggregateID string) { func (r *IDPProviderSearchRequest) AppendAggregateIDQuery(aggregateID string) {
r.Queries = append(r.Queries, &IDPProviderSearchQuery{Key: IDPProviderSearchKeyAggregateID, Method: model.SearchMethodEquals, Value: aggregateID}) 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 package model
import ( import (
"github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/model" "github.com/caos/zitadel/internal/model"
"github.com/caos/zitadel/internal/v2/domain"
"time" "time"
) )
@@ -64,3 +66,57 @@ func (p *LoginPolicyView) HasMultiFactors() bool {
} }
return true 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" "github.com/caos/zitadel/internal/errors"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
es_int "github.com/caos/zitadel/internal/eventstore" 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" "github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
global_model "github.com/caos/zitadel/internal/model" global_model "github.com/caos/zitadel/internal/model"
org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" 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 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) { func (repo *UserRepo) EmailByID(ctx context.Context, userID string) (*usr_model.Email, error) {
user, err := repo.UserByID(ctx, userID) user, err := repo.UserByID(ctx, userID)
if err != nil { if err != nil {

View File

@@ -27,6 +27,22 @@ func (repo *UserGrantRepo) UserGrantByID(ctx context.Context, grantID string) (*
return model.UserGrantToModel(grant), nil 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) { func (repo *UserGrantRepo) SearchUserGrants(ctx context.Context, request *grant_model.UserGrantSearchRequest) (*grant_model.UserGrantSearchResponse, error) {
request.EnsureLimit(repo.SearchLimit) request.EnsureLimit(repo.SearchLimit)
sequence, sequenceErr := repo.View.GetLatestUserGrantSequence("") sequence, sequenceErr := repo.View.GetLatestUserGrantSequence("")

View File

@@ -25,8 +25,6 @@ type UserRepository interface {
SearchMachineKeys(ctx context.Context, request *model.MachineKeySearchRequest) (*model.MachineKeySearchResponse, error) SearchMachineKeys(ctx context.Context, request *model.MachineKeySearchRequest) (*model.MachineKeySearchResponse, error)
GetMachineKey(ctx context.Context, userID, keyID string) (*model.MachineKeyView, 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) EmailByID(ctx context.Context, userID string) (*model.Email, error)

View File

@@ -8,4 +8,6 @@ import (
type UserGrantRepository interface { type UserGrantRepository interface {
UserGrantByID(ctx context.Context, grantID string) (*model.UserGrantView, error) UserGrantByID(ctx context.Context, grantID string) (*model.UserGrantView, error)
SearchUserGrants(ctx context.Context, request *model.UserGrantSearchRequest) (*model.UserGrantSearchResponse, 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" "github.com/caos/logging"
sd "github.com/caos/zitadel/internal/config/systemdefaults" sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/notification/repository/eventsourcing" "github.com/caos/zitadel/internal/notification/repository/eventsourcing"
"github.com/caos/zitadel/internal/v2/command"
"github.com/rakyll/statik/fs" "github.com/rakyll/statik/fs"
_ "github.com/caos/zitadel/internal/notification/statik" _ "github.com/caos/zitadel/internal/notification/statik"
@@ -14,10 +15,10 @@ type Config struct {
Repository eventsourcing.Config 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") statikFS, err := fs.NewWithNamespace("notification")
logging.Log("CONFI-7usEW").OnError(err).Panic("unable to start listener") 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") logging.Log("MAIN-9uBxp").OnError(err).Panic("unable to start app")
} }

View File

@@ -1,6 +1,7 @@
package handler package handler
import ( import (
"github.com/caos/zitadel/internal/v2/command"
"net/http" "net/http"
"time" "time"
@@ -42,7 +43,7 @@ type EventstoreRepos struct {
IAMEvents *iam_es.IAMEventstore 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) aesCrypto, err := crypto.NewAESCrypto(systemDefaults.UserVerificationKey)
if err != nil { if err != nil {
logging.Log("HANDL-s90ew").WithError(err).Debug("error create new aes crypto") 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( newNotification(
handler{view, bulkLimit, configs.cycleDuration("Notification"), errorCount, es}, handler{view, bulkLimit, configs.cycleDuration("Notification"), errorCount, es},
command,
repos.UserEvents, repos.UserEvents,
systemDefaults, systemDefaults,
aesCrypto, aesCrypto,

View File

@@ -3,6 +3,8 @@ package handler
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"github.com/caos/zitadel/internal/user/repository/view/model"
"github.com/caos/zitadel/internal/v2/command"
"net/http" "net/http"
"time" "time"
@@ -32,6 +34,7 @@ const (
type Notification struct { type Notification struct {
handler handler
command *command.CommandSide
userEvents *usr_event.UserEventstore userEvents *usr_event.UserEventstore
systemDefaults sd.SystemDefaults systemDefaults sd.SystemDefaults
AesCrypto crypto.EncryptionAlgorithm AesCrypto crypto.EncryptionAlgorithm
@@ -42,6 +45,7 @@ type Notification struct {
func newNotification( func newNotification(
handler handler, handler handler,
command *command.CommandSide,
userEvents *usr_event.UserEventstore, userEvents *usr_event.UserEventstore,
defaults sd.SystemDefaults, defaults sd.SystemDefaults,
aesCrypto crypto.EncryptionAlgorithm, aesCrypto crypto.EncryptionAlgorithm,
@@ -50,6 +54,7 @@ func newNotification(
) *Notification { ) *Notification {
h := &Notification{ h := &Notification{
handler: handler, handler: handler,
command: command,
userEvents: userEvents, userEvents: userEvents,
systemDefaults: defaults, systemDefaults: defaults,
i18n: translator, i18n: translator,
@@ -135,7 +140,7 @@ func (n *Notification) handleInitUserCode(event *models.Event) (err error) {
return err return err
} }
user, err := n.view.NotifyUserByID(event.AggregateID) user, err := n.getUserByID(event.AggregateID)
if err != nil { if err != nil {
return err return err
} }
@@ -143,7 +148,7 @@ func (n *Notification) handleInitUserCode(event *models.Event) (err error) {
if err != nil { if err != nil {
return err 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) { func (n *Notification) handlePasswordCode(event *models.Event) (err error) {
@@ -163,7 +168,7 @@ func (n *Notification) handlePasswordCode(event *models.Event) (err error) {
return err return err
} }
user, err := n.view.NotifyUserByID(event.AggregateID) user, err := n.getUserByID(event.AggregateID)
if err != nil { if err != nil {
return err return err
} }
@@ -171,7 +176,7 @@ func (n *Notification) handlePasswordCode(event *models.Event) (err error) {
if err != nil { if err != nil {
return err 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) { func (n *Notification) handleEmailVerificationCode(event *models.Event) (err error) {
@@ -191,7 +196,7 @@ func (n *Notification) handleEmailVerificationCode(event *models.Event) (err err
return err return err
} }
user, err := n.view.NotifyUserByID(event.AggregateID) user, err := n.getUserByID(event.AggregateID)
if err != nil { if err != nil {
return err return err
} }
@@ -199,7 +204,7 @@ func (n *Notification) handleEmailVerificationCode(event *models.Event) (err err
if err != nil { if err != nil {
return err 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) { 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 { if err != nil || alreadyHandled {
return nil return nil
} }
user, err := n.view.NotifyUserByID(event.AggregateID) user, err := n.getUserByID(event.AggregateID)
if err != nil { if err != nil {
return err return err
} }
@@ -221,7 +226,7 @@ func (n *Notification) handlePhoneVerificationCode(event *models.Event) (err err
if err != nil { if err != nil {
return err 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) { 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") logging.Log("HANDLE-Gghq2").WithError(err).Error("could not unmarshal event data")
return caos_errs.ThrowInternal(err, "HANDLE-7hgj3", "could not unmarshal event") 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 { if err != nil {
return err return err
} }
@@ -242,7 +247,7 @@ func (n *Notification) handleDomainClaimed(event *models.Event) (err error) {
if err != nil { if err != nil {
return err 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) { 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 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 ( import (
es_iam "github.com/caos/zitadel/internal/iam/repository/eventsourcing" es_iam "github.com/caos/zitadel/internal/iam/repository/eventsourcing"
"github.com/caos/zitadel/internal/v2/command"
"net/http" "net/http"
sd "github.com/caos/zitadel/internal/config/systemdefaults" sd "github.com/caos/zitadel/internal/config/systemdefaults"
@@ -29,7 +30,7 @@ type EsRepository struct {
spooler *es_spol.Spooler 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) es, err := es_int.Start(conf.Eventstore)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -65,7 +66,7 @@ func Start(conf Config, dir http.FileSystem, systemDefaults sd.SystemDefaults) (
return nil, err return nil, err
} }
eventstoreRepos := handler.EventstoreRepos{UserEvents: user, OrgEvents: org, IAMEvents: iam} 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{ return &EsRepository{
spool, spool,

View File

@@ -2,6 +2,7 @@ package spooler
import ( import (
"database/sql" "database/sql"
"github.com/caos/zitadel/internal/v2/command"
"net/http" "net/http"
sd "github.com/caos/zitadel/internal/config/systemdefaults" sd "github.com/caos/zitadel/internal/config/systemdefaults"
@@ -19,12 +20,12 @@ type SpoolerConfig struct {
Handlers handler.Configs 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{ spoolerConfig := spooler.Config{
Eventstore: es, Eventstore: es,
Locker: &locker{dbClient: sql}, Locker: &locker{dbClient: sql},
ConcurrentWorkers: c.ConcurrentWorkers, 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 := spoolerConfig.New()
spool.Start() spool.Start()

View File

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

View File

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

View File

@@ -1,17 +1,17 @@
package handler package handler
import ( import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http" "net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware" http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
) )
const ( const (
queryAuthRequestID = "authRequestID" 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) authRequestID := r.FormValue(queryAuthRequestID)
if authRequestID == "" { if authRequestID == "" {
return nil, nil 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) 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) authReq, err := l.getAuthRequest(r)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -1,12 +1,11 @@
package handler package handler
import ( import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http" "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 callback := l.oidcAuthCallbackURL + authReq.ID
http.Redirect(w, r, callback, http.StatusFound) http.Redirect(w, r, callback, http.StatusFound)
} }

View File

@@ -1,10 +1,10 @@
package handler package handler
import ( import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http" "net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware" http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
) )
const ( const (
@@ -26,7 +26,7 @@ func (l *Login) handleChangePassword(w http.ResponseWriter, r *http.Request) {
return return
} }
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context()) 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 { if err != nil {
l.renderChangePassword(w, r, authReq, err) l.renderChangePassword(w, r, authReq, err)
return return
@@ -34,7 +34,7 @@ func (l *Login) handleChangePassword(w http.ResponseWriter, r *http.Request) {
l.renderChangePasswordDone(w, r, authReq) 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 var errType, errMessage string
if err != nil { if err != nil {
errMessage = l.getErrorMessage(r, err) 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) 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 var errType, errMessage string
data := l.getUserData(r, authReq, "Password Change Done", errType, errMessage) data := l.getUserData(r, authReq, "Password Change Done", errType, errMessage)
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplChangePasswordDone], data, nil) 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/oidc"
"github.com/caos/oidc/pkg/rp" "github.com/caos/oidc/pkg/rp"
http_mw "github.com/caos/zitadel/internal/api/http/middleware" 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/crypto"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
caos_errors "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" iam_model "github.com/caos/zitadel/internal/iam/model"
org_model "github.com/caos/zitadel/internal/org/model" "github.com/caos/zitadel/internal/v2/domain"
usr_model "github.com/caos/zitadel/internal/user/model"
"net/http" "net/http"
"strings" "strings"
"time" "time"
@@ -41,7 +38,7 @@ type externalNotFoundOptionData struct {
baseData 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 { for _, idp := range authReq.AllowedExternalIDPs {
if idp.IDPConfigID == selectedIDPConfigID { if idp.IDPConfigID == selectedIDPConfigID {
l.handleIDP(w, r, authReq, 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) 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) idpConfig, err := l.getIDPConfigByID(r, selectedIDPConfigID)
if err != nil { if err != nil {
l.renderError(w, r, authReq, err) 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) 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) provider := l.getRPConfig(w, r, authReq, idpConfig, callbackEndpoint)
http.Redirect(w, r, rp.AuthURL(authReq.ID, provider, rp.WithPrompt(oidc.PromptSelectAccount)), http.StatusFound) 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) 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) oidcClientSecret, err := crypto.DecryptString(idpConfig.OIDCClientSecret, l.IDPConfigAesCrypto)
if err != nil { if err != nil {
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
@@ -130,9 +127,9 @@ func (l *Login) getRPConfig(w http.ResponseWriter, r *http.Request, authReq *mod
return provider 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) 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 err != nil {
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
err = nil err = nil
@@ -143,7 +140,7 @@ func (l *Login) handleExternalUserAuthenticated(w http.ResponseWriter, r *http.R
l.renderNextStep(w, r, authReq) 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 var errType, errMessage string
if err != nil { if err != nil {
errMessage = l.getErrorMessage(r, err) errMessage = l.getErrorMessage(r, err)
@@ -176,7 +173,7 @@ func (l *Login) handleExternalNotFoundOptionCheck(w http.ResponseWriter, r *http
l.handleAutoRegister(w, r, authReq) 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()) iam, err := l.authRepo.GetIAM(r.Context())
if err != nil { if err != nil {
l.renderExternalNotFoundOption(w, r, authReq, err) l.renderExternalNotFoundOption(w, r, authReq, err)
@@ -184,13 +181,10 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR
} }
resourceOwner := iam.GlobalOrgID resourceOwner := iam.GlobalOrgID
member := &org_model.OrgMember{ memberRoles := []string{domain.RoleOrgProjectCreator}
ObjectRoot: models.ObjectRoot{AggregateID: iam.GlobalOrgID},
Roles: []string{orgProjectCreatorRole},
}
if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != iam.GlobalOrgID { if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != iam.GlobalOrgID {
member = nil memberRoles = nil
resourceOwner = authReq.RequestedOrgID resourceOwner = authReq.RequestedOrgID
} }
@@ -208,7 +202,7 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context()) userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
user, externalIDP := l.mapExternalUserToLoginUser(orgIamPolicy, authReq.LinkingUsers[len(authReq.LinkingUsers)-1], idpConfig) 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 { if err != nil {
l.renderExternalNotFoundOption(w, r, authReq, err) l.renderExternalNotFoundOption(w, r, authReq, err)
return return
@@ -216,7 +210,7 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR
l.renderNextStep(w, r, authReq) 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() displayName := tokens.IDTokenClaims.GetPreferredUsername()
if displayName == "" && tokens.IDTokenClaims.GetEmail() != "" { if displayName == "" && tokens.IDTokenClaims.GetEmail() != "" {
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, IDPConfigID: idpConfig.IDPConfigID,
ExternalUserID: tokens.IDTokenClaims.GetSubject(), ExternalUserID: tokens.IDTokenClaims.GetSubject(),
PreferredUsername: tokens.IDTokenClaims.GetPreferredUsername(), PreferredUsername: tokens.IDTokenClaims.GetPreferredUsername(),
@@ -246,7 +240,7 @@ func (l *Login) mapTokenToLoginUser(tokens *oidc.Tokens, idpConfig *iam_model.ID
} }
return externalUser 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 username := linkingUser.PreferredUsername
switch idpConfig.OIDCUsernameMapping { switch idpConfig.OIDCUsernameMapping {
case iam_model.OIDCMappingFieldEmail: case iam_model.OIDCMappingFieldEmail:
@@ -262,23 +256,21 @@ func (l *Login) mapExternalUserToLoginUser(orgIamPolicy *iam_model.OrgIAMPolicyV
} }
} }
user := &usr_model.User{ human := &domain.Human{
UserName: username, Username: username,
Human: &usr_model.Human{ Profile: &domain.Profile{
Profile: &usr_model.Profile{ FirstName: linkingUser.FirstName,
FirstName: linkingUser.FirstName, LastName: linkingUser.LastName,
LastName: linkingUser.LastName, PreferredLanguage: linkingUser.PreferredLanguage,
PreferredLanguage: linkingUser.PreferredLanguage, NickName: linkingUser.NickName,
NickName: linkingUser.NickName, },
}, Email: &domain.Email{
Email: &usr_model.Email{ EmailAddress: linkingUser.Email,
EmailAddress: linkingUser.Email, IsEmailVerified: linkingUser.IsEmailVerified,
IsEmailVerified: linkingUser.IsEmailVerified,
},
}, },
} }
if linkingUser.Phone != "" { if linkingUser.Phone != "" {
user.Phone = &usr_model.Phone{ human.Phone = &domain.Phone{
PhoneNumber: linkingUser.Phone, PhoneNumber: linkingUser.Phone,
IsPhoneVerified: linkingUser.IsPhoneVerified, IsPhoneVerified: linkingUser.IsPhoneVerified,
} }
@@ -292,10 +284,10 @@ func (l *Login) mapExternalUserToLoginUser(orgIamPolicy *iam_model.OrgIAMPolicyV
} }
} }
externalIDP := &usr_model.ExternalIDP{ externalIDP := &domain.ExternalIDP{
IDPConfigID: idpConfig.IDPConfigID, IDPConfigID: idpConfig.IDPConfigID,
UserID: linkingUser.ExternalUserID, ExternalUserID: linkingUser.ExternalUserID,
DisplayName: displayName, DisplayName: displayName,
} }
return user, externalIDP return human, externalIDP
} }

View File

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

View File

@@ -1,10 +1,10 @@
package handler package handler
import ( import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http" "net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware" http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/errors" "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) 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 { if data.Password != data.PasswordConfirm {
err := errors.ThrowInvalidArgument(nil, "VIEW-KaGue", "Errors.User.Password.ConfirmationWrong") err := errors.ThrowInvalidArgument(nil, "VIEW-KaGue", "Errors.User.Password.ConfirmationWrong")
l.renderInitPassword(w, r, authReq, data.UserID, data.Code, err) l.renderInitPassword(w, r, authReq, data.UserID, data.Code, err)
return return
} }
userOrg := login userOrg := ""
if authReq != nil { if authReq != nil {
userOrg = authReq.UserOrgID userOrg = authReq.UserOrgID
} }
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context()) 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 { if err != nil {
l.renderInitPassword(w, r, authReq, data.UserID, "", err) l.renderInitPassword(w, r, authReq, data.UserID, "", err)
return return
@@ -77,16 +77,25 @@ func (l *Login) checkPWCode(w http.ResponseWriter, r *http.Request, authReq *mod
l.renderInitPasswordDone(w, r, authReq) 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 userOrg := login
if authReq != nil { if authReq != nil {
userOrg = authReq.UserOrgID 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) 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 var errType, errMessage string
if err != nil { if err != nil {
errMessage = l.getErrorMessage(r, err) 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) 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", "", "") data := l.getUserData(r, authReq, "Password Init Done", "", "")
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplInitPasswordDone], data, nil) l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplInitPasswordDone], data, nil)
} }

View File

@@ -1,10 +1,10 @@
package handler package handler
import ( import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http" "net/http"
"strconv" "strconv"
"github.com/caos/zitadel/internal/auth_request/model"
caos_errs "github.com/caos/zitadel/internal/errors" 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) 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 { if data.Password != data.PasswordConfirm {
err := caos_errs.ThrowInvalidArgument(nil, "VIEW-fsdfd", "Errors.User.Password.ConfirmationWrong") err := caos_errs.ThrowInvalidArgument(nil, "VIEW-fsdfd", "Errors.User.Password.ConfirmationWrong")
l.renderInitUser(w, r, authReq, data.UserID, data.Code, data.PasswordSet, err) l.renderInitUser(w, r, authReq, data.UserID, data.Code, data.PasswordSet, err)
return return
} }
userOrgID := login userOrgID := ""
if authReq != nil { if authReq != nil {
userOrgID = authReq.UserOrgID 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 { if err != nil {
l.renderInitUser(w, r, authReq, data.UserID, "", data.PasswordSet, err) l.renderInitUser(w, r, authReq, data.UserID, "", data.PasswordSet, err)
return return
@@ -80,16 +80,16 @@ func (l *Login) checkUserInitCode(w http.ResponseWriter, r *http.Request, authRe
l.renderInitUserDone(w, r, authReq) l.renderInitUserDone(w, r, authReq)
} }
func (l *Login) resendUserInit(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, userID string, showPassword bool) { func (l *Login) resendUserInit(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID string, showPassword bool) {
userOrgID := login userOrgID := ""
if authReq != nil { if authReq != nil {
userOrgID = authReq.UserOrgID 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) 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 var errType, errMessage string
if err != nil { if err != nil {
errMessage = l.getErrorMessage(r, err) 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) 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", "", "") data := l.getUserData(r, authReq, "User Init Done", "", "")
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplInitUserDone], data, nil) l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplInitUserDone], data, nil)
} }

View File

@@ -2,22 +2,21 @@ package handler
import ( import (
http_mw "github.com/caos/zitadel/internal/api/http/middleware" http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/v2/domain"
"net/http" "net/http"
"github.com/caos/zitadel/internal/auth_request/model"
) )
const ( const (
tmplLinkUsersDone = "linkusersdone" 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()) 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) 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 var errType, errMessage string
data := l.getUserData(r, authReq, "Linking Users Done", errType, errMessage) data := l.getUserData(r, authReq, "Linking Users Done", errType, errMessage)
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplLinkUsersDone], data, nil) l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplLinkUsersDone], data, nil)

View File

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

View File

@@ -1,9 +1,8 @@
package handler package handler
import ( import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http" "net/http"
"github.com/caos/zitadel/internal/auth_request/model"
) )
const ( const (
@@ -47,21 +46,21 @@ func (l *Login) handleMailVerificationCheck(w http.ResponseWriter, r *http.Reque
l.checkMailCode(w, r, authReq, data.UserID, data.Code) l.checkMailCode(w, r, authReq, data.UserID, data.Code)
return return
} }
userOrg := login userOrg := ""
if authReq != nil { if authReq != nil {
userOrg = authReq.UserOrgID 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) l.renderMailVerification(w, r, authReq, data.UserID, err)
} }
func (l *Login) checkMailCode(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, userID, code string) { func (l *Login) checkMailCode(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID, code string) {
userOrg := login userOrg := ""
if authReq != nil { if authReq != nil {
userID = authReq.UserID userID = authReq.UserID
userOrg = authReq.UserOrgID 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 { if err != nil {
l.renderMailVerification(w, r, authReq, userID, err) l.renderMailVerification(w, r, authReq, userID, err)
return return
@@ -69,7 +68,7 @@ func (l *Login) checkMailCode(w http.ResponseWriter, r *http.Request, authReq *m
l.renderMailVerified(w, r, authReq) 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 var errType, errMessage string
if err != nil { if err != nil {
errMessage = l.getErrorMessage(r, err) 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) 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{ data := mailVerificationData{
baseData: l.getBaseData(r, authReq, "Mail Verified", "", ""), baseData: l.getBaseData(r, authReq, "Mail Verified", "", ""),
profileData: l.getProfileData(authReq), profileData: l.getProfileData(authReq),

View File

@@ -1,9 +1,8 @@
package handler package handler
import ( import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http" "net/http"
"github.com/caos/zitadel/internal/auth_request/model"
) )
const ( const (
@@ -13,7 +12,7 @@ const (
type mfaInitDoneData struct { 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 var errType, errMessage string
data.baseData = l.getBaseData(r, authReq, "MFA Init Done", errType, errMessage) data.baseData = l.getBaseData(r, authReq, "MFA Init Done", errType, errMessage)
data.profileData = l.getProfileData(authReq) data.profileData = l.getProfileData(authReq)

View File

@@ -2,11 +2,11 @@ package handler
import ( import (
"encoding/base64" "encoding/base64"
"github.com/caos/zitadel/internal/v2/domain"
"net/http" "net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware" http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model" "github.com/caos/zitadel/internal/auth_request/model"
user_model "github.com/caos/zitadel/internal/user/model"
) )
const ( const (
@@ -18,11 +18,11 @@ type u2fInitData struct {
MFAType model.MFAType 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 errType, errMessage, credentialData string
var u2f *user_model.WebAuthNToken var u2f *domain.WebAuthNToken
if err == nil { 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 { if err != nil {
errMessage = l.getErrorMessage(r, err) 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()) 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) l.renderRegisterU2F(w, r, authReq, err)
return return
} }
done := &mfaDoneData{ done := &mfaDoneData{
MFAType: model.MFATypeU2F, MFAType: domain.MFATypeU2F,
} }
l.renderMFAInitDone(w, r, authReq, done) l.renderMFAInitDone(w, r, authReq, done)
} }

View File

@@ -2,13 +2,13 @@ package handler
import ( import (
"bytes" "bytes"
"github.com/caos/zitadel/internal/v2/domain"
"net/http" "net/http"
svg "github.com/ajstarks/svgo" svg "github.com/ajstarks/svgo"
"github.com/boombuler/barcode/qr" "github.com/boombuler/barcode/qr"
http_mw "github.com/caos/zitadel/internal/api/http/middleware" http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/qrcode" "github.com/caos/zitadel/internal/qrcode"
) )
@@ -17,10 +17,10 @@ const (
) )
type mfaInitVerifyData struct { type mfaInitVerifyData struct {
MFAType model.MFAType `schema:"mfaType"` MFAType domain.MFAType `schema:"mfaType"`
Code string `schema:"code"` Code string `schema:"code"`
URL string `schema:"url"` URL string `schema:"url"`
Secret string `schema:"secret"` Secret string `schema:"secret"`
} }
func (l *Login) handleMFAInitVerify(w http.ResponseWriter, r *http.Request) { 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 var verifyData *mfaVerifyData
switch data.MFAType { switch data.MFAType {
case model.MFATypeOTP: case domain.MFATypeOTP:
verifyData = l.handleOTPVerify(w, r, authReq, data) 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) 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()) 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 { if err == nil {
return nil return nil
} }
@@ -64,14 +64,14 @@ func (l *Login) handleOTPVerify(w http.ResponseWriter, r *http.Request, authReq
return mfadata 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 var errType, errMessage string
if err != nil { if err != nil {
errMessage = l.getErrorMessage(r, err) errMessage = l.getErrorMessage(r, err)
} }
data.baseData = l.getBaseData(r, authReq, "MFA Init Verify", errType, errMessage) data.baseData = l.getBaseData(r, authReq, "MFA Init Verify", errType, errMessage)
data.profileData = l.getProfileData(authReq) data.profileData = l.getProfileData(authReq)
if data.MFAType == model.MFATypeOTP { if data.MFAType == domain.MFATypeOTP {
code, err := generateQrCode(data.otpData.Url) code, err := generateQrCode(data.otpData.Url)
if err == nil { if err == nil {
data.otpData.QrCode = code data.otpData.QrCode = code

View File

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

View File

@@ -1,6 +1,7 @@
package handler package handler
import ( import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http" "net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware" 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 { if data.MFAType == model.MFATypeOTP {
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context()) 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 { if err != nil {
l.renderError(w, r, authReq, err) 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) 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 { if verificationStep == nil {
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return
@@ -43,7 +44,7 @@ func (l *Login) renderMFAVerify(w http.ResponseWriter, r *http.Request, authReq
l.renderMFAVerifySelected(w, r, authReq, verificationStep, provider, err) 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 var errType, errMessage string
if err != nil { if err != nil {
errMessage = l.getErrorMessage(r, err) errMessage = l.getErrorMessage(r, err)
@@ -54,12 +55,12 @@ func (l *Login) renderMFAVerifySelected(w http.ResponseWriter, r *http.Request,
return return
} }
switch selectedProvider { switch selectedProvider {
case model.MFATypeU2F: case domain.MFATypeU2F:
l.renderU2FVerification(w, r, authReq, removeSelectedProviderFromList(verificationStep.MFAProviders, model.MFATypeU2F), nil) l.renderU2FVerification(w, r, authReq, removeSelectedProviderFromList(verificationStep.MFAProviders, domain.MFATypeU2F), nil)
return return
case model.MFATypeOTP: case domain.MFATypeOTP:
data.MFAProviders = removeSelectedProviderFromList(verificationStep.MFAProviders, model.MFATypeOTP) data.MFAProviders = removeSelectedProviderFromList(verificationStep.MFAProviders, domain.MFATypeOTP)
data.SelectedMFAProvider = model.MFATypeOTP data.SelectedMFAProvider = domain.MFATypeOTP
default: default:
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return 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) 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-- { for i := len(providers) - 1; i >= 0; i-- {
if providers[i] == selected { if providers[i] == selected {
copy(providers[i:], providers[i+1:]) copy(providers[i:], providers[i+1:])

View File

@@ -2,11 +2,10 @@ package handler
import ( import (
"encoding/base64" "encoding/base64"
"github.com/caos/zitadel/internal/v2/domain"
"net/http" "net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware" 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 ( const (
@@ -15,21 +14,21 @@ const (
type mfaU2FData struct { type mfaU2FData struct {
webAuthNData webAuthNData
MFAProviders []model.MFAType MFAProviders []domain.MFAType
SelectedProvider model.MFAType SelectedProvider domain.MFAType
} }
type mfaU2FFormData struct { type mfaU2FFormData struct {
webAuthNFormData 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 errType, errMessage, credentialData string
var webAuthNLogin *user_model.WebAuthNLogin var webAuthNLogin *domain.WebAuthNLogin
if err == nil { if err == nil {
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context()) 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 { if err != nil {
errMessage = l.getErrorMessage(r, err) 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) l.renderError(w, r, authReq, err)
return return
} }
step, ok := authReq.PossibleSteps[0].(*model.MFAVerificationStep) step, ok := authReq.PossibleSteps[0].(*domain.MFAVerificationStep)
if !ok { if !ok {
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return
@@ -70,7 +69,7 @@ func (l *Login) handleU2FVerification(w http.ResponseWriter, r *http.Request) {
return return
} }
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context()) 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 { if err != nil {
l.renderU2FVerification(w, r, authReq, step.MFAProviders, err) l.renderU2FVerification(w, r, authReq, step.MFAProviders, err)
return return

View File

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

View File

@@ -1,9 +1,8 @@
package handler package handler
import ( import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http" "net/http"
"github.com/caos/zitadel/internal/auth_request/model"
) )
const ( const (
@@ -16,11 +15,16 @@ func (l *Login) handlePasswordReset(w http.ResponseWriter, r *http.Request) {
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return 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) 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 var errType, errMessage string
if err != nil { if err != nil {
errMessage = l.getErrorMessage(r, err) errMessage = l.getErrorMessage(r, err)

View File

@@ -2,11 +2,10 @@ package handler
import ( import (
"encoding/base64" "encoding/base64"
"github.com/caos/zitadel/internal/v2/domain"
"net/http" "net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware" 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 ( const (
@@ -23,12 +22,12 @@ type passwordlessFormData struct {
PasswordLogin bool `schema:"passwordlogin"` 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 errType, errMessage, credentialData string
var webAuthNLogin *user_model.WebAuthNLogin var webAuthNLogin *domain.WebAuthNLogin
if err == nil { if err == nil {
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context()) 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 { if err != nil {
errMessage = l.getErrorMessage(r, err) errMessage = l.getErrorMessage(r, err)
@@ -67,7 +66,7 @@ func (l *Login) handlePasswordlessVerification(w http.ResponseWriter, r *http.Re
return return
} }
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context()) 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 { if err != nil {
l.renderPasswordlessVerification(w, r, authReq, err) l.renderPasswordlessVerification(w, r, authReq, err)
return return

View File

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

View File

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

View File

@@ -1,10 +1,10 @@
package handler package handler
import ( import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http" "net/http"
auth_model "github.com/caos/zitadel/internal/auth/model" 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" caos_errs "github.com/caos/zitadel/internal/errors"
org_model "github.com/caos/zitadel/internal/org/model" org_model "github.com/caos/zitadel/internal/org/model"
usr_model "github.com/caos/zitadel/internal/user/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) 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 var errType, errMessage string
if err != nil { if err != nil {
errMessage = l.getErrorMessage(r, err) errMessage = l.getErrorMessage(r, err)

View File

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

View File

@@ -1,10 +1,10 @@
package handler package handler
import ( import (
"github.com/caos/zitadel/internal/v2/domain"
"net/http" "net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware" http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
) )
const ( const (
@@ -15,7 +15,7 @@ type userSelectionFormData struct {
UserID string `schema:"userID"` 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{ data := userSelectionData{
baseData: l.getBaseData(r, authReq, "Select User", "", ""), baseData: l.getBaseData(r, authReq, "Select User", "", ""),
Users: selectionData.Users, Users: selectionData.Users,

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
package model package model
import ( import (
"github.com/caos/zitadel/internal/v2/domain"
"time" "time"
"golang.org/x/text/language" "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}) 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 { func (u *UserView) MFATypesSetupPossible(level domain.MFALevel, policy *domain.LoginPolicy) []domain.MFAType {
types := make([]req_model.MFAType, 0) types := make([]domain.MFAType, 0)
switch level { switch level {
default: default:
fallthrough fallthrough
case req_model.MFALevelSecondFactor: case domain.MFALevelSecondFactor:
if policy.HasSecondFactors() { if policy.HasSecondFactors() {
for _, mfaType := range policy.SecondFactors { for _, mfaType := range policy.SecondFactors {
switch mfaType { switch mfaType {
case iam_model.SecondFactorTypeOTP: case domain.SecondFactorTypeOTP:
if u.OTPState != MFAStateReady { if u.OTPState != MFAStateReady {
types = append(types, req_model.MFATypeOTP) types = append(types, domain.MFATypeOTP)
} }
case iam_model.SecondFactorTypeU2F: case domain.SecondFactorTypeU2F:
types = append(types, req_model.MFATypeU2F) types = append(types, domain.MFATypeU2F)
} }
} }
} }
@@ -140,24 +141,24 @@ func (u *UserView) MFATypesSetupPossible(level req_model.MFALevel, policy *iam_m
return types return types
} }
func (u *UserView) MFATypesAllowed(level req_model.MFALevel, policy *iam_model.LoginPolicyView) ([]req_model.MFAType, bool) { func (u *UserView) MFATypesAllowed(level domain.MFALevel, policy *domain.LoginPolicy) ([]domain.MFAType, bool) {
types := make([]req_model.MFAType, 0) types := make([]domain.MFAType, 0)
required := true required := true
switch level { switch level {
default: default:
required = policy.ForceMFA required = policy.ForceMFA
fallthrough fallthrough
case req_model.MFALevelSecondFactor: case domain.MFALevelSecondFactor:
if policy.HasSecondFactors() { if policy.HasSecondFactors() {
for _, mfaType := range policy.SecondFactors { for _, mfaType := range policy.SecondFactors {
switch mfaType { switch mfaType {
case iam_model.SecondFactorTypeOTP: case domain.SecondFactorTypeOTP:
if u.OTPState == MFAStateReady { 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() { 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"` VerifiedPhone string `json:"-" gorm:"column:verified_phone"`
PasswordSet bool `json:"-" gorm:"column:password_set"` PasswordSet bool `json:"-" gorm:"column:password_set"`
Sequence uint64 `json:"-" gorm:"column:sequence"` Sequence uint64 `json:"-" gorm:"column:sequence"`
State int32 `json:"-"`
} }
func NotifyUserFromModel(user *model.NotifyUser) *NotifyUser { func NotifyUserFromModel(user *model.NotifyUser) *NotifyUser {
@@ -144,6 +145,8 @@ func (u *NotifyUser) AppendEvent(event *models.Event) (err error) {
case es_model.UserPasswordChanged, case es_model.UserPasswordChanged,
es_model.HumanPasswordChanged: es_model.HumanPasswordChanged:
err = u.setPasswordData(event) err = u.setPasswordData(event)
case es_model.UserRemoved:
u.State = int32(UserStateDeleted)
} }
return err return err
} }

View File

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

View File

@@ -108,7 +108,7 @@ func (wm *IAMIDPOIDCConfigWriteModel) NewChangedEvent(
if userNameMapping.Valid() && wm.UserNameMapping != userNameMapping { if userNameMapping.Valid() && wm.UserNameMapping != userNameMapping {
changes = append(changes, idpconfig.ChangeUserNameMapping(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)) changes = append(changes, idpconfig.ChangeScopes(scopes))
} }
if len(changes) == 0 { 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") 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 return nil
} }
@@ -72,7 +72,7 @@ func (r *CommandSide) changeDefaultLoginPolicy(ctx context.Context, iamAgg *iam_
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved { if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return caos_errs.ThrowNotFound(nil, "IAM-M0sif", "Errors.IAM.LoginPolicy.NotFound") 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 { if !hasChanged {
return caos_errs.ThrowPreconditionFailed(nil, "IAM-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged") 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 := 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) err = r.eventstore.PushAggregate(ctx, addedPolicy, orgAgg)
if err != nil { 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 { if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "Org-M0sif", "Errors.Org.LoginPolicy.NotFound") 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 { if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-5M9vdd", "Errors.Org.LoginPolicy.NotChanged") return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-5M9vdd", "Errors.Org.LoginPolicy.NotChanged")
} }

View File

@@ -2,6 +2,7 @@ package command
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/eventstore/v2"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/v2/domain" "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) 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 == "" { if projectID == "" || resourceOwner == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-66hM9", "Errors.Project.ProjectIDMissing") 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") return caos_errs.ThrowNotFound(nil, "COMMAND-3M9sd", "Errors.Project.NotFound")
} }
aggregates := make([]eventstore.Aggregater, 0)
projectAgg := ProjectAggregateFromWriteModel(&existingProject.WriteModel) projectAgg := ProjectAggregateFromWriteModel(&existingProject.WriteModel)
projectAgg.PushEvents(project.NewProjectRemovedEvent(ctx, existingProject.Name, existingProject.ResourceOwner)) 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) { 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{ &domain.LoginPolicy{
AllowUsernamePassword: step1.DefaultLoginPolicy.AllowUsernamePassword, AllowUsernamePassword: step1.DefaultLoginPolicy.AllowUsernamePassword,
AllowRegister: step1.DefaultLoginPolicy.AllowRegister, AllowRegister: step1.DefaultLoginPolicy.AllowRegister,
AllowExternalIdp: step1.DefaultLoginPolicy.AllowExternalIdp, AllowExternalIDP: step1.DefaultLoginPolicy.AllowExternalIdp,
}) })
if err != nil { if err != nil {
return err return err

View File

@@ -2,6 +2,10 @@ package command
import ( import (
"context" "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" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/telemetry/tracing" "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") return caos_errs.ThrowNotFound(nil, "COMMAND-5N9ds", "Errors.User.NotFound")
} }
if existingUser.UserName == userName { 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) 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") return caos_errs.ThrowNotFound(nil, "COMMAND-5M9fs", "Errors.User.NotFound")
} }
if existingUser.UserState != domain.UserStateActive && existingUser.UserState != domain.UserStateInitial { 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 := UserAggregateFromWriteModel(&existingUser.WriteModel)
userAgg.PushEvents(user.NewUserLockedEvent(ctx)) 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) 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 { func (r *CommandSide) checkUserExists(ctx context.Context, userID, resourceOwner string) error {
userWriteModel, err := r.userWriteModelByID(ctx, userID, resourceOwner) userWriteModel, err := r.userWriteModelByID(ctx, userID, resourceOwner)
if err != nil { if err != nil {

View File

@@ -2,6 +2,7 @@ package command
import ( import (
"github.com/caos/zitadel/internal/v2/domain" "github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
) )
func writeModelToHuman(wm *HumanWriteModel) *domain.Human { 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 { func readModelToU2FTokens(wm *HumanU2FTokensReadModel) []*domain.WebAuthNToken {
tokens := make([]*domain.WebAuthNToken, len(wm.WebAuthNTokens)) tokens := make([]*domain.WebAuthNToken, len(wm.WebAuthNTokens))
for i, token := range wm.WebAuthNTokens { for i, token := range wm.WebAuthNTokens {
@@ -107,3 +117,19 @@ func writeModelToWebAuthN(wm *HumanWebAuthNWriteModel) *domain.WebAuthNToken {
State: wm.State, 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) { 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") 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") return nil, nil, caos_errs.ThrowNotFound(nil, "COMMAND-1My0t", "Errors.UserGrant.NotFound")
} }
//TODO: Remove Uniqueness
removeUserGrant := NewUserGrantWriteModel(grantID, resourceOwner) removeUserGrant := NewUserGrantWriteModel(grantID, resourceOwner)
userGrantAgg := UserGrantAggregateFromWriteModel(&removeUserGrant.WriteModel) userGrantAgg := UserGrantAggregateFromWriteModel(&removeUserGrant.WriteModel)
if !cascade { if !cascade {

View File

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

View File

@@ -2,6 +2,7 @@ package command
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/eventstore/v2" "github.com/caos/zitadel/internal/eventstore/v2"
caos_errs "github.com/caos/zitadel/internal/errors" 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) err = r.eventstore.PushAggregate(ctx, addedHuman, userAgg)
if err != nil { if err != nil {
if caos_errs.IsErrorAlreadyExists(err) {
return nil, caos_errs.ThrowAlreadyExists(err, "COMMAND-4kSff", "Errors.User.AlreadyExists")
}
return nil, err 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) 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) userAgg, addedHuman, err := r.registerHuman(ctx, orgID, human, externalIDP)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = r.eventstore.PushAggregate(ctx, addedHuman, userAgg) aggregates[0] = userAgg
if err != nil {
if caos_errs.IsErrorAlreadyExists(err) { orgMemberWriteModel := NewOrgMemberWriteModel(orgID, addedHuman.AggregateID)
return nil, caos_errs.ThrowAlreadyExists(err, "COMMAND-4kSff", "Errors.User.AlreadyExists") 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 return writeModelToHuman(addedHuman), nil
} }
@@ -82,7 +97,7 @@ func (r *CommandSide) createHuman(ctx context.Context, orgID string, human *doma
} }
addedHuman := NewHumanWriteModel(human.AggregateID, orgID) 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 return nil, nil, err
} }
human.SetNamesAsDisplayname() human.SetNamesAsDisplayname()
@@ -100,11 +115,10 @@ func (r *CommandSide) createHuman(ctx context.Context, orgID string, human *doma
userAgg.PushEvents(createEvent) userAgg.PushEvents(createEvent)
if externalIDP != nil { if externalIDP != nil {
if !externalIDP.IsValid() { err = r.addHumanExternalIDP(ctx, userAgg, externalIDP)
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4Dj9s", "Errors.User.ExternalIDP.Invalid") if err != nil {
return nil, nil, err
} }
//TODO: check if idpconfig exists
userAgg.PushEvents(user.NewHumanExternalIDPAddedEvent(ctx, externalIDP.IDPConfigID, externalIDP.DisplayName))
} }
if human.IsInitialState() { if human.IsInitialState() {
initCode, err := domain.NewInitUserCode(r.initializeUserCode) initCode, err := domain.NewInitUserCode(r.initializeUserCode)
@@ -121,7 +135,7 @@ func (r *CommandSide) createHuman(ctx context.Context, orgID string, human *doma
if err != nil { if err != nil {
return nil, nil, err 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 { } else if human.Phone != nil && human.PhoneNumber != "" && human.IsPhoneVerified {
userAgg.PushEvents(user.NewHumanPhoneVerifiedEvent(ctx)) userAgg.PushEvents(user.NewHumanPhoneVerifiedEvent(ctx))
} }
@@ -129,32 +143,21 @@ func (r *CommandSide) createHuman(ctx context.Context, orgID string, human *doma
return userAgg, addedHuman, nil 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 == "" { 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 { if err != nil {
return err return err
} }
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted { if existingHuman.UserState == domain.UserStateUnspecified || existingHuman.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9df", "Errors.User.NotFound") return caos_errs.ThrowNotFound(nil, "COMMAND-m9cV8", "Errors.User.NotFound")
} }
if existingEmail.UserState != domain.UserStateInitial { userAgg := UserAggregateFromWriteModel(&existingHuman.WriteModel)
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sd", "Errors.User.AlreadyInitialised") userAgg.PushEvents(user.NewHumanMFAInitSkippedEvent(ctx))
} return r.eventstore.PushAggregate(ctx, existingHuman, userAgg)
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)
} }
func createAddHumanEvent(ctx context.Context, orgID string, human *domain.Human, userLoginMustBeDomain bool) *user.HumanAddedEvent { 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 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) { func (r *CommandSide) getHumanWriteModelByID(ctx context.Context, userID, resourceowner string) (*HumanWriteModel, error) {
humanWriteModel := NewHumanWriteModel(userID, resourceowner) humanWriteModel := NewHumanWriteModel(userID, resourceowner)
err := r.eventstore.FilterToQueryReducer(ctx, humanWriteModel) 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) changedEvent, hasChanged := existingEmail.NewChangedEvent(ctx, email.EmailAddress)
if !hasChanged { 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 := UserAggregateFromWriteModel(&existingEmail.WriteModel)
userAgg.PushEvents(changedEvent) userAgg.PushEvents(changedEvent)
@@ -60,7 +60,7 @@ func (r *CommandSide) VerifyHumanEmail(ctx context.Context, userID, code, resour
return err return err
} }
if existingCode.Code == nil || existingCode.UserState == domain.UserStateUnspecified || existingCode.UserState == domain.UserStateDeleted { 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) userAgg := UserAggregateFromWriteModel(&existingCode.WriteModel)
@@ -103,6 +103,19 @@ func (r *CommandSide) CreateHumanEmailVerificationCode(ctx context.Context, user
return r.eventstore.PushAggregate(ctx, existingEmail, userAgg) 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) { func (r *CommandSide) emailWriteModel(ctx context.Context, userID, resourceOwner string) (writeModel *HumanEmailWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()

View File

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

View File

@@ -3,11 +3,40 @@ package command
import ( import (
"context" "context"
caos_errs "github.com/caos/zitadel/internal/errors" 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/telemetry/tracing"
"github.com/caos/zitadel/internal/v2/domain" "github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user" "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 { func (r *CommandSide) RemoveHumanExternalIDP(ctx context.Context, externalIDP *domain.ExternalIDP) error {
return r.removeHumanExternalIDP(ctx, externalIDP, false) 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) 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) { func (r *CommandSide) externalIDPWriteModelByID(ctx context.Context, userID, idpConfigID, externalUserID, resourceOwner string) (writeModel *HumanExternalIDPWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() 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 ( import (
"context" "context"
"github.com/caos/logging"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/telemetry/tracing" "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) human, err := r.getHuman(ctx, userID, resourceowner)
if err != nil { if err != nil {
logging.Log("COMMAND-DAqe1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get human for loginname")
return nil, err return nil, err
} }
org, err := r.getOrg(ctx, human.ResourceOwner) org, err := r.getOrg(ctx, human.ResourceOwner)
if err != nil { if err != nil {
logging.Log("COMMAND-Cm0ds").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get org for loginname")
return nil, err return nil, err
} }
orgPolicy, err := r.getOrgIAMPolicy(ctx, org.AggregateID) orgPolicy, err := r.getOrgIAMPolicy(ctx, org.AggregateID)
if err != nil { 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 return nil, err
} }
otpWriteModel, err := r.otpWriteModelByID(ctx, userID, resourceowner) otpWriteModel, err := r.otpWriteModelByID(ctx, userID, resourceowner)
@@ -58,7 +62,7 @@ func (r *CommandSide) AddHumanOTP(ctx context.Context, userID, resourceowner str
}, nil }, 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 == "" { if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-8N9ds", "Errors.User.UserIDMissing") 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) 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 == "" { if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-5M0sd", "Errors.User.UserIDMissing") 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 { func (wm *HumanOTPWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType). query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID). AggregateIDs(wm.AggregateID)
ResourceOwner(wm.ResourceOwner) if wm.ResourceOwner != "" {
query.ResourceOwner(wm.ResourceOwner)
}
return query
} }

View File

@@ -2,6 +2,7 @@ package command
import ( import (
"context" "context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/telemetry/tracing" "github.com/caos/zitadel/internal/telemetry/tracing"
@@ -21,7 +22,34 @@ func (r *CommandSide) SetOneTimePassword(ctx context.Context, orgID, userID, pas
SecretString: passwordString, SecretString: passwordString,
ChangeRequired: true, 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) { 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 { if err != nil {
return err return err
} }
if existingPassword.Secret != nil { if existingPassword.Secret == nil {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Fds3s", "Errors.User.Password.Empty") return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Fds3s", "Errors.User.Password.Empty")
} }
ctx, spanPasswordComparison := tracing.NewNamedSpan(ctx, "crypto.CompareHash") ctx, spanPasswordComparison := tracing.NewNamedSpan(ctx, "crypto.CompareHash")
@@ -44,12 +72,14 @@ func (r *CommandSide) ChangePassword(ctx context.Context, orgID, userID, oldPass
} }
password := &domain.Password{ password := &domain.Password{
SecretString: newPassword, 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) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() 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 { if err := password.HashPasswordIfExisting(pwPolicy, r.userPasswordAlg); err != nil {
return err return err
} }
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
userAgg.PushEvents(user.NewHumanPasswordChangedEvent(ctx, password.SecretCrypto, password.ChangeRequired, userAgentID)) userAgg.PushEvents(user.NewHumanPasswordChangedEvent(ctx, password.SecretCrypto, password.ChangeRequired, userAgentID))
return r.eventstore.PushAggregate(ctx, existingPassword, userAgg) 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) 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) { func (r *CommandSide) passwordWriteModel(ctx context.Context, userID, resourceOwner string) (writeModel *HumanPasswordWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()

View File

@@ -5,6 +5,7 @@ import (
"github.com/caos/zitadel/internal/eventstore/v2" "github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain" "github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user" "github.com/caos/zitadel/internal/v2/repository/user"
"time"
) )
type HumanPasswordWriteModel struct { type HumanPasswordWriteModel struct {
@@ -13,6 +14,10 @@ type HumanPasswordWriteModel struct {
Secret *crypto.CryptoValue Secret *crypto.CryptoValue
SecretChangeRequired bool SecretChangeRequired bool
Code *crypto.CryptoValue
CodeCreationDate time.Time
CodeExpiry time.Duration
UserState domain.UserState UserState domain.UserState
} }
@@ -43,6 +48,11 @@ func (wm *HumanPasswordWriteModel) Reduce() error {
case *user.HumanPasswordChangedEvent: case *user.HumanPasswordChangedEvent:
wm.Secret = e.Secret wm.Secret = e.Secret
wm.SecretChangeRequired = e.ChangeRequired 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: case *user.HumanEmailVerifiedEvent:
if wm.UserState == domain.UserStateInitial { if wm.UserState == domain.UserStateInitial {
wm.UserState = domain.UserStateActive wm.UserState = domain.UserStateActive
@@ -55,7 +65,10 @@ func (wm *HumanPasswordWriteModel) Reduce() error {
} }
func (wm *HumanPasswordWriteModel) Query() *eventstore.SearchQueryBuilder { func (wm *HumanPasswordWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType). query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID). AggregateIDs(wm.AggregateID)
ResourceOwner(wm.ResourceOwner) 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 return err
} }
if existingPhone.State == domain.PhoneStateUnspecified || existingPhone.State == domain.PhoneStateRemoved { 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 { if existingPhone.IsPhoneVerified {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sf", "Errors.User.Phone.AlreadyVerified") 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) 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 { func (r *CommandSide) RemoveHumanPhone(ctx context.Context, userID, resourceOwner string) error {
if userID == "" { if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M0ds", "Errors.User.UserIDMissing") 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 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) u2fTokens, err := r.getHumanU2FTokens(ctx, userID, resourceowner)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -55,7 +83,7 @@ func (r *CommandSide) AddHumanU2F(ctx context.Context, userID, resourceowner str
return createdWebAuthN, nil 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) passwordlessTokens, err := r.getHumanPasswordlessTokens(ctx, userID, resourceowner)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -64,7 +92,7 @@ func (r *CommandSide) AddHumanPasswordless(ctx context.Context, userID, resource
if err != nil { if err != nil {
return nil, err 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) err = r.eventstore.PushAggregate(ctx, addWebAuthN, userAgg)
if err != nil { if err != nil {
@@ -114,7 +142,7 @@ func (r *CommandSide) addHumanWebAuthN(ctx context.Context, userID, resourceowne
return addWebAuthN, userAgg, webAuthN, nil 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) u2fTokens, err := r.getHumanU2FTokens(ctx, userID, resourceowner)
if err != nil { if err != nil {
return err return err
@@ -139,7 +167,7 @@ func (r *CommandSide) VerifyHumanU2F(ctx context.Context, userID, resourceowner,
return r.eventstore.PushAggregate(ctx, verifyWebAuthN, userAgg) 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) u2fTokens, err := r.getHumanPasswordlessTokens(ctx, userID, resourceowner)
if err != nil { if err != nil {
return err return err
@@ -149,7 +177,7 @@ func (r *CommandSide) VerifyHumanPasswordless(ctx context.Context, userID, resou
return err return err
} }
userAgg.PushEvents( userAgg.PushEvents(
usr_repo.NewHumanU2FVerifiedEvent( usr_repo.NewHumanPasswordlessVerifiedEvent(
ctx, ctx,
verifyWebAuthN.WebauthNTokenID, verifyWebAuthN.WebauthNTokenID,
webAuthN.WebAuthNTokenName, webAuthN.WebAuthNTokenName,
@@ -186,12 +214,156 @@ func (r *CommandSide) verifyHumanWebAuthN(ctx context.Context, userID, resourceo
return verifyWebAuthN, userAgg, webAuthN, nil 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) event := usr_repo.NewHumanU2FRemovedEvent(ctx, webAuthNID)
return r.removeHumanWebAuthN(ctx, userID, webAuthNID, resourceOwner, event) 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) event := usr_repo.NewHumanPasswordlessRemovedEvent(ctx, webAuthNID)
return r.removeHumanWebAuthN(ctx, userID, webAuthNID, resourceOwner, event) 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 { if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(e) 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: case *user.HumanWebAuthNRemovedEvent:
if wm.WebauthNTokenID == e.WebAuthNTokenID { if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(e) wm.WriteModel.AppendEvents(e)
@@ -56,6 +64,8 @@ func (wm *HumanWebAuthNWriteModel) Reduce() error {
wm.appendAddedEvent(e) wm.appendAddedEvent(e)
case *user.HumanWebAuthNVerifiedEvent: case *user.HumanWebAuthNVerifiedEvent:
wm.appendVerifiedEvent(e) wm.appendVerifiedEvent(e)
case *user.HumanWebAuthNSignCountChangedEvent:
wm.SignCount = e.SignCount
case *user.HumanWebAuthNRemovedEvent: case *user.HumanWebAuthNRemovedEvent:
wm.State = domain.MFAStateRemoved wm.State = domain.MFAStateRemoved
case *user.UserRemovedEvent: case *user.UserRemovedEvent:
@@ -104,34 +114,35 @@ func NewHumanU2FTokensReadModel(userID, resourceOwner string) *HumanU2FTokensRea
} }
func (wm *HumanU2FTokensReadModel) AppendEvents(events ...eventstore.EventReader) { func (wm *HumanU2FTokensReadModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events { wm.WriteModel.AppendEvents(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)
}
}
} }
func (wm *HumanU2FTokensReadModel) Reduce() error { func (wm *HumanU2FTokensReadModel) Reduce() error {
for _, event := range wm.Events { for _, event := range wm.Events {
switch e := event.(type) { switch e := event.(type) {
case *user.HumanWebAuthNAddedEvent: case *user.HumanU2FAddedEvent:
token := &HumanWebAuthNWriteModel{} token := &HumanWebAuthNWriteModel{}
token.appendAddedEvent(e) token.appendAddedEvent(&e.HumanWebAuthNAddedEvent)
wm.WebAuthNTokens = append(wm.WebAuthNTokens, token) token.WriteModel = eventstore.WriteModel{
case *user.HumanWebAuthNVerifiedEvent: 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) idx, token := wm.WebAuthNTokenByID(e.WebAuthNTokenID)
if idx < 0 { if idx < 0 {
continue continue
} }
token.appendVerifiedEvent(e) token.appendVerifiedEvent(&e.HumanWebAuthNVerifiedEvent)
case *user.HumanWebAuthNRemovedEvent: case *user.HumanU2FRemovedEvent:
idx, _ := wm.WebAuthNTokenByID(e.WebAuthNTokenID) idx, _ := wm.WebAuthNTokenByID(e.WebAuthNTokenID)
if idx < 0 { if idx < 0 {
continue continue
@@ -153,8 +164,7 @@ func (rm *HumanU2FTokensReadModel) Query() *eventstore.SearchQueryBuilder {
EventTypes( EventTypes(
user.HumanU2FTokenAddedType, user.HumanU2FTokenAddedType,
user.HumanU2FTokenVerifiedType, user.HumanU2FTokenVerifiedType,
user.HumanU2FTokenRemovedType, user.HumanU2FTokenRemovedType)
user.UserV1MFAOTPRemovedType)
} }
@@ -190,17 +200,29 @@ func (wm *HumanPasswordlessTokensReadModel) AppendEvents(events ...eventstore.Ev
func (wm *HumanPasswordlessTokensReadModel) Reduce() error { func (wm *HumanPasswordlessTokensReadModel) Reduce() error {
for _, event := range wm.Events { for _, event := range wm.Events {
switch e := event.(type) { switch e := event.(type) {
case *user.HumanWebAuthNAddedEvent: case *user.HumanPasswordlessAddedEvent:
token := &HumanWebAuthNWriteModel{} token := &HumanWebAuthNWriteModel{}
token.appendAddedEvent(e) token.appendAddedEvent(&e.HumanWebAuthNAddedEvent)
wm.WebAuthNTokens = append(wm.WebAuthNTokens, token) token.WriteModel = eventstore.WriteModel{
case *user.HumanWebAuthNVerifiedEvent: 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) idx, token := wm.WebAuthNTokenByID(e.WebAuthNTokenID)
if idx < 0 { if idx < 0 {
continue continue
} }
token.appendVerifiedEvent(e) token.appendVerifiedEvent(&e.HumanWebAuthNVerifiedEvent)
case *user.HumanWebAuthNRemovedEvent: case *user.HumanPasswordlessRemovedEvent:
idx, _ := wm.WebAuthNTokenByID(e.WebAuthNTokenID) idx, _ := wm.WebAuthNTokenByID(e.WebAuthNTokenID)
if idx < 0 { if idx < 0 {
continue continue
@@ -222,8 +244,7 @@ func (rm *HumanPasswordlessTokensReadModel) Query() *eventstore.SearchQueryBuild
EventTypes( EventTypes(
user.HumanPasswordlessTokenAddedType, user.HumanPasswordlessTokenAddedType,
user.HumanPasswordlessTokenVerifiedType, user.HumanPasswordlessTokenVerifiedType,
user.HumanPasswordlessTokenRemovedType, user.HumanPasswordlessTokenRemovedType)
user.UserV1MFAOTPRemovedType)
} }
@@ -235,3 +256,114 @@ func (wm *HumanPasswordlessTokensReadModel) WebAuthNTokenByID(id string) (idx in
} }
return -1, nil 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) changedEvent, hasChanged := existingUser.NewChangedEvent(ctx, machine.Name, machine.Description)
if !hasChanged { 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 := UserAggregateFromWriteModel(&existingUser.WriteModel)
userAgg.PushEvents(changedEvent) 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: case *user.HumanRegisteredEvent:
wm.UserName = e.UserName wm.UserName = e.UserName
wm.UserState = domain.UserStateInitial wm.UserState = domain.UserStateInitial
case *user.HumanInitializedCheckSucceededEvent:
wm.UserState = domain.UserStateActive
case *user.MachineAddedEvent: case *user.MachineAddedEvent:
wm.UserName = e.UserName wm.UserName = e.UserName
wm.UserState = domain.UserStateActive 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()) 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 { if policy == nil {
return caos_errors.ThrowPreconditionFailed(nil, "DOMAIN-zSH7j", "Errors.Users.OrgIamPolicyNil") 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") return caos_errors.ThrowPreconditionFailed(nil, "DOMAIN-se4sJ", "Errors.User.EmailAsUsernameNotAllowed")
} }
if !policy.UserLoginMustBeDomain && u.Profile != nil && userName == "" && u.Email != nil { if !policy.UserLoginMustBeDomain && u.Profile != nil && u.Username == "" && u.Email != nil {
userName = u.EmailAddress u.Username = u.EmailAddress
} }
return nil return nil
} }

View File

@@ -1,6 +1,9 @@
package domain 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 { type WebAuthNToken struct {
es_models.ObjectRoot es_models.ObjectRoot
@@ -55,3 +58,12 @@ func GetTokenToVerify(tokens []*WebAuthNToken) (int, *WebAuthNToken) {
} }
return -1, nil 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