mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-01 15:03:32 +00:00
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:
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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 ""
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
18
internal/auth_request/repository/cache/cache.go
vendored
18
internal/auth_request/repository/cache/cache.go
vendored
@@ -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")
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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("")
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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:])
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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) }()
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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) }()
|
||||||
|
|||||||
104
internal/v2/command/user_human_init.go
Normal file
104
internal/v2/command/user_human_init.go
Normal 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
|
||||||
|
}
|
||||||
89
internal/v2/command/user_human_init_model.go
Normal file
89
internal/v2/command/user_human_init_model.go
Normal 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
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) }()
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
83
internal/v2/command/user_machine_key.go
Normal file
83
internal/v2/command/user_machine_key.go
Normal 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
|
||||||
|
}
|
||||||
74
internal/v2/command/user_machine_key_model.go
Normal file
74
internal/v2/command/user_machine_key_model.go
Normal 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)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
150
internal/v2/domain/auth_request.go
Normal file
150
internal/v2/domain/auth_request.go
Normal 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 ""
|
||||||
|
}
|
||||||
22
internal/v2/domain/browser_info.go
Normal file
22
internal/v2/domain/browser_info.go
Normal 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
Reference in New Issue
Block a user