mirror of
https://github.com/zitadel/zitadel.git
synced 2025-07-30 17:03:43 +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:
parent
c65331df1a
commit
320679467b
@ -115,7 +115,7 @@ func startZitadel(configPaths []string) {
|
||||
logging.Log("MAIN-s9KOw").OnError(err).Fatal("error starting authz repo")
|
||||
var authRepo *auth_es.EsRepository
|
||||
if *authEnabled || *oidcEnabled || *loginEnabled {
|
||||
authRepo, err = auth_es.Start(conf.Auth, conf.InternalAuthZ, conf.SystemDefaults, authZRepo)
|
||||
authRepo, err = auth_es.Start(conf.Auth, conf.InternalAuthZ, conf.SystemDefaults, command, authZRepo)
|
||||
logging.Log("MAIN-9oRw6").OnError(err).Fatal("error starting auth repo")
|
||||
}
|
||||
|
||||
@ -123,7 +123,7 @@ func startZitadel(configPaths []string) {
|
||||
startUI(ctx, conf, authRepo, command, query)
|
||||
|
||||
if *notificationEnabled {
|
||||
notification.Start(ctx, conf.Notification, conf.SystemDefaults)
|
||||
notification.Start(ctx, conf.Notification, conf.SystemDefaults, command)
|
||||
}
|
||||
|
||||
<-ctx.Done()
|
||||
@ -166,7 +166,7 @@ func startAPI(ctx context.Context, conf *Config, authZRepo *authz_repo.EsReposit
|
||||
apis.RegisterServer(ctx, auth.CreateServer(command, query, authRepo))
|
||||
}
|
||||
if *oidcEnabled {
|
||||
op := oidc.NewProvider(ctx, conf.API.OIDC, authRepo, *localDevMode)
|
||||
op := oidc.NewProvider(ctx, conf.API.OIDC, command, query, authRepo, *localDevMode)
|
||||
apis.RegisterHandler("/oauth/v2", op.HttpHandler())
|
||||
}
|
||||
apis.Start(ctx)
|
||||
|
@ -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.IAMRepository
|
||||
eventstore.AdministratorRepo
|
||||
eventstore.UserRepo
|
||||
}
|
||||
|
||||
func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, roles []string) (*EsRepository, error) {
|
||||
@ -86,12 +85,6 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, r
|
||||
AdministratorRepo: eventstore.AdministratorRepo{
|
||||
View: view,
|
||||
},
|
||||
UserRepo: eventstore.UserRepo{
|
||||
UserEvents: user,
|
||||
OrgEvents: org,
|
||||
View: view,
|
||||
SystemDefaults: systemDefaults,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
func loginPolicyToDomain(policy *admin.DefaultLoginPolicyRequest) *domain.LoginPolicy {
|
||||
return &domain.LoginPolicy{
|
||||
AllowUsernamePassword: policy.AllowUsernamePassword,
|
||||
AllowExternalIdp: policy.AllowExternalIdp,
|
||||
AllowExternalIDP: policy.AllowExternalIdp,
|
||||
AllowRegister: policy.AllowRegister,
|
||||
ForceMFA: policy.ForceMfa,
|
||||
PasswordlessType: passwordlessTypeToDomain(policy.PasswordlessType),
|
||||
@ -22,7 +22,7 @@ func loginPolicyToDomain(policy *admin.DefaultLoginPolicyRequest) *domain.LoginP
|
||||
func loginPolicyFromDomain(policy *domain.LoginPolicy) *admin.DefaultLoginPolicy {
|
||||
return &admin.DefaultLoginPolicy{
|
||||
AllowUsernamePassword: policy.AllowUsernamePassword,
|
||||
AllowExternalIdp: policy.AllowExternalIdp,
|
||||
AllowExternalIdp: policy.AllowExternalIDP,
|
||||
AllowRegister: policy.AllowRegister,
|
||||
ForceMfa: policy.ForceMFA,
|
||||
PasswordlessType: passwordlessTypeFromDomain(policy.PasswordlessType),
|
||||
|
@ -162,19 +162,19 @@ func (s *Server) AddMfaOTP(ctx context.Context, _ *empty.Empty) (_ *auth.MfaOtpR
|
||||
|
||||
func (s *Server) VerifyMfaOTP(ctx context.Context, request *auth.VerifyMfaOtp) (*empty.Empty, error) {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
err := s.command.CheckMFAOTPSetup(ctx, ctxData.UserID, request.Code, "", ctxData.ResourceOwner)
|
||||
err := s.command.HumanCheckMFAOTPSetup(ctx, ctxData.UserID, request.Code, "", ctxData.ResourceOwner)
|
||||
return &empty.Empty{}, err
|
||||
}
|
||||
|
||||
func (s *Server) RemoveMfaOTP(ctx context.Context, _ *empty.Empty) (_ *empty.Empty, err error) {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
err = s.command.RemoveHumanOTP(ctx, ctxData.UserID, ctxData.OrgID)
|
||||
err = s.command.HumanRemoveOTP(ctx, ctxData.UserID, ctxData.OrgID)
|
||||
return &empty.Empty{}, err
|
||||
}
|
||||
|
||||
func (s *Server) AddMyMfaU2F(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNResponse, err error) {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
u2f, err := s.command.AddHumanU2F(ctx, ctxData.UserID, ctxData.ResourceOwner, false)
|
||||
u2f, err := s.command.HumanAddU2FSetup(ctx, ctxData.UserID, ctxData.ResourceOwner, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -183,13 +183,13 @@ func (s *Server) AddMyMfaU2F(ctx context.Context, _ *empty.Empty) (_ *auth.WebAu
|
||||
|
||||
func (s *Server) VerifyMyMfaU2F(ctx context.Context, request *auth.VerifyWebAuthN) (*empty.Empty, error) {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
err := s.command.VerifyHumanU2F(ctx, ctxData.UserID, ctxData.OrgID, request.TokenName, "", request.PublicKeyCredential)
|
||||
err := s.command.HumanVerifyU2FSetup(ctx, ctxData.UserID, ctxData.OrgID, request.TokenName, "", request.PublicKeyCredential)
|
||||
return &empty.Empty{}, err
|
||||
}
|
||||
|
||||
func (s *Server) RemoveMyMfaU2F(ctx context.Context, id *auth.WebAuthNTokenID) (*empty.Empty, error) {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
err := s.command.RemoveHumanU2F(ctx, ctxData.UserID, id.Id, ctxData.OrgID)
|
||||
err := s.command.HumanRemoveU2F(ctx, ctxData.UserID, id.Id, ctxData.OrgID)
|
||||
return &empty.Empty{}, err
|
||||
}
|
||||
|
||||
@ -203,7 +203,7 @@ func (s *Server) GetMyPasswordless(ctx context.Context, _ *empty.Empty) (_ *auth
|
||||
|
||||
func (s *Server) AddMyPasswordless(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNResponse, err error) {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
u2f, err := s.command.AddHumanPasswordless(ctx, ctxData.UserID, ctxData.ResourceOwner, false)
|
||||
u2f, err := s.command.HumanAddPasswordlessSetup(ctx, ctxData.UserID, ctxData.ResourceOwner, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -212,13 +212,13 @@ func (s *Server) AddMyPasswordless(ctx context.Context, _ *empty.Empty) (_ *auth
|
||||
|
||||
func (s *Server) VerifyMyPasswordless(ctx context.Context, request *auth.VerifyWebAuthN) (*empty.Empty, error) {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
err := s.command.VerifyHumanPasswordless(ctx, ctxData.UserID, ctxData.OrgID, request.TokenName, "", request.PublicKeyCredential)
|
||||
err := s.command.HumanHumanPasswordlessSetup(ctx, ctxData.UserID, ctxData.OrgID, request.TokenName, "", request.PublicKeyCredential)
|
||||
return &empty.Empty{}, err
|
||||
}
|
||||
|
||||
func (s *Server) RemoveMyPasswordless(ctx context.Context, id *auth.WebAuthNTokenID) (*empty.Empty, error) {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
err := s.command.RemoveHumanPasswordless(ctx, ctxData.UserID, id.Id, ctxData.ResourceOwner)
|
||||
err := s.command.HumanRemovePasswordless(ctx, ctxData.UserID, id.Id, ctxData.ResourceOwner)
|
||||
return &empty.Empty{}, err
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ func loginPolicyRequestToDomain(ctx context.Context, policy *management.LoginPol
|
||||
AggregateID: authz.GetCtxData(ctx).OrgID,
|
||||
},
|
||||
AllowUsernamePassword: policy.AllowUsernamePassword,
|
||||
AllowExternalIdp: policy.AllowExternalIdp,
|
||||
AllowExternalIDP: policy.AllowExternalIdp,
|
||||
AllowRegister: policy.AllowRegister,
|
||||
ForceMFA: policy.ForceMfa,
|
||||
PasswordlessType: passwordlessTypeToDomain(policy.PasswordlessType),
|
||||
@ -30,7 +30,7 @@ func loginPolicyRequestToDomain(ctx context.Context, policy *management.LoginPol
|
||||
func loginPolicyFromDomain(policy *domain.LoginPolicy) *management.LoginPolicy {
|
||||
return &management.LoginPolicy{
|
||||
AllowUsernamePassword: policy.AllowUsernamePassword,
|
||||
AllowExternalIdp: policy.AllowExternalIdp,
|
||||
AllowExternalIdp: policy.AllowExternalIDP,
|
||||
AllowRegister: policy.AllowRegister,
|
||||
ChangeDate: timestamppb.New(policy.ChangeDate),
|
||||
ForceMfa: policy.ForceMFA,
|
||||
|
@ -2,7 +2,6 @@ package management
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
@ -36,7 +35,11 @@ func (s *Server) ReactivateProject(ctx context.Context, in *management.ProjectID
|
||||
}
|
||||
|
||||
func (s *Server) RemoveProject(ctx context.Context, in *management.ProjectID) (*empty.Empty, error) {
|
||||
err := s.command.RemoveProject(ctx, in.Id, authz.GetCtxData(ctx).OrgID)
|
||||
grants, err := s.usergrant.UserGrantsByProjectID(ctx, in.Id)
|
||||
if err != nil {
|
||||
return &empty.Empty{}, err
|
||||
}
|
||||
err = s.command.RemoveProject(ctx, in.Id, authz.GetCtxData(ctx).OrgID, userGrantsToIDs(grants)...)
|
||||
return &empty.Empty{}, err
|
||||
}
|
||||
|
||||
|
@ -217,11 +217,11 @@ func (s *Server) GetUserMfas(ctx context.Context, userID *management.UserID) (*m
|
||||
}
|
||||
|
||||
func (s *Server) RemoveMfaOTP(ctx context.Context, userID *management.UserID) (*empty.Empty, error) {
|
||||
return &empty.Empty{}, s.command.RemoveHumanOTP(ctx, userID.Id, authz.GetCtxData(ctx).OrgID)
|
||||
return &empty.Empty{}, s.command.HumanRemoveOTP(ctx, userID.Id, authz.GetCtxData(ctx).OrgID)
|
||||
}
|
||||
|
||||
func (s *Server) RemoveMfaU2F(ctx context.Context, webAuthNTokenID *management.WebAuthNTokenID) (*empty.Empty, error) {
|
||||
return &empty.Empty{}, s.command.RemoveHumanU2F(ctx, webAuthNTokenID.UserId, webAuthNTokenID.Id, authz.GetCtxData(ctx).OrgID)
|
||||
return &empty.Empty{}, s.command.HumanRemoveU2F(ctx, webAuthNTokenID.UserId, webAuthNTokenID.Id, authz.GetCtxData(ctx).OrgID)
|
||||
}
|
||||
|
||||
func (s *Server) GetPasswordless(ctx context.Context, userID *management.UserID) (_ *management.WebAuthNTokens, err error) {
|
||||
@ -233,7 +233,7 @@ func (s *Server) GetPasswordless(ctx context.Context, userID *management.UserID)
|
||||
}
|
||||
|
||||
func (s *Server) RemovePasswordless(ctx context.Context, id *management.WebAuthNTokenID) (*empty.Empty, error) {
|
||||
return &empty.Empty{}, s.command.RemoveHumanPasswordless(ctx, id.UserId, id.Id, authz.GetCtxData(ctx).OrgID)
|
||||
return &empty.Empty{}, s.command.HumanRemovePasswordless(ctx, id.UserId, id.Id, authz.GetCtxData(ctx).OrgID)
|
||||
}
|
||||
|
||||
func (s *Server) SearchUserMemberships(ctx context.Context, in *management.UserMembershipSearchRequest) (*management.UserMembershipSearchResponse, error) {
|
||||
|
@ -177,3 +177,11 @@ func usergrantStateFromDomain(state domain.UserGrantState) management.UserGrantS
|
||||
return management.UserGrantState_USERGRANTSTATE_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
func userGrantsToIDs(userGrants []*grant_model.UserGrantView) []string {
|
||||
converted := make([]string, len(userGrants))
|
||||
for i, grant := range userGrants {
|
||||
converted[i] = grant.ID
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
@ -2,21 +2,22 @@ package management
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
|
||||
"github.com/caos/zitadel/pkg/grpc/management"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
)
|
||||
|
||||
func (s *Server) AddMachineKey(ctx context.Context, req *management.AddMachineKeyRequest) (*management.AddMachineKeyResponse, error) {
|
||||
key, err := s.user.AddMachineKey(ctx, addMachineKeyToModel(req))
|
||||
key, err := s.command.AddUserMachineKey(ctx, addMachineKeyToDomain(req), authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addMachineKeyFromModel(key), nil
|
||||
return addMachineKeyFromDomain(key), nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteMachineKey(ctx context.Context, req *management.MachineKeyIDRequest) (*empty.Empty, error) {
|
||||
err := s.user.RemoveMachineKey(ctx, req.UserId, req.KeyId)
|
||||
err := s.command.RemoveUserMachineKey(ctx, req.UserId, req.KeyId, authz.GetCtxData(ctx).OrgID)
|
||||
return &empty.Empty{}, err
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package management
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
@ -75,7 +76,7 @@ func machineKeyViewFromModel(key *usr_model.MachineKeyView) *management.MachineK
|
||||
}
|
||||
}
|
||||
|
||||
func addMachineKeyToModel(key *management.AddMachineKeyRequest) *usr_model.MachineKey {
|
||||
func addMachineKeyToDomain(key *management.AddMachineKeyRequest) *domain.MachineKey {
|
||||
expirationDate := time.Time{}
|
||||
if key.ExpirationDate != nil {
|
||||
var err error
|
||||
@ -83,20 +84,14 @@ func addMachineKeyToModel(key *management.AddMachineKeyRequest) *usr_model.Machi
|
||||
logging.Log("MANAG-iNshR").OnError(err).Debug("unable to parse expiration date")
|
||||
}
|
||||
|
||||
return &usr_model.MachineKey{
|
||||
return &domain.MachineKey{
|
||||
ExpirationDate: expirationDate,
|
||||
Type: machineKeyTypeToModel(key.Type),
|
||||
Type: machineKeyTypeToDomain(key.Type),
|
||||
ObjectRoot: models.ObjectRoot{AggregateID: key.UserId},
|
||||
}
|
||||
}
|
||||
|
||||
func addMachineKeyFromModel(key *usr_model.MachineKey) *management.AddMachineKeyResponse {
|
||||
creationDate, err := ptypes.TimestampProto(key.CreationDate)
|
||||
logging.Log("MANAG-dlb8m").OnError(err).Debug("unable to parse cretaion date")
|
||||
|
||||
expirationDate, err := ptypes.TimestampProto(key.ExpirationDate)
|
||||
logging.Log("MANAG-dlb8m").OnError(err).Debug("unable to parse cretaion date")
|
||||
|
||||
func addMachineKeyFromDomain(key *domain.MachineKey) *management.AddMachineKeyResponse {
|
||||
detail, err := json.Marshal(struct {
|
||||
Type string `json:"type"`
|
||||
KeyID string `json:"keyId"`
|
||||
@ -112,20 +107,29 @@ func addMachineKeyFromModel(key *usr_model.MachineKey) *management.AddMachineKey
|
||||
|
||||
return &management.AddMachineKeyResponse{
|
||||
Id: key.KeyID,
|
||||
CreationDate: creationDate,
|
||||
ExpirationDate: expirationDate,
|
||||
CreationDate: timestamppb.New(key.CreationDate),
|
||||
ExpirationDate: timestamppb.New(key.ExpirationDate),
|
||||
Sequence: key.Sequence,
|
||||
KeyDetails: detail,
|
||||
Type: machineKeyTypeFromModel(key.Type),
|
||||
Type: machineKeyTypeFromDomain(key.Type),
|
||||
}
|
||||
}
|
||||
|
||||
func machineKeyTypeToModel(typ management.MachineKeyType) usr_model.MachineKeyType {
|
||||
func machineKeyTypeToDomain(typ management.MachineKeyType) domain.MachineKeyType {
|
||||
switch typ {
|
||||
case management.MachineKeyType_MACHINEKEY_JSON:
|
||||
return usr_model.MachineKeyTypeJSON
|
||||
return domain.MachineKeyTypeJSON
|
||||
default:
|
||||
return usr_model.MachineKeyTypeNONE
|
||||
return domain.MachineKeyTypeNONE
|
||||
}
|
||||
}
|
||||
|
||||
func machineKeyTypeFromDomain(typ domain.MachineKeyType) management.MachineKeyType {
|
||||
switch typ {
|
||||
case domain.MachineKeyTypeJSON:
|
||||
return management.MachineKeyType_MACHINEKEY_JSON
|
||||
default:
|
||||
return management.MachineKeyType_MACHINEKEY_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ func (o *OPStorage) CreateToken(ctx context.Context, req op.TokenRequest) (_ str
|
||||
userAgentID = authReq.AgentID
|
||||
applicationID = authReq.ApplicationID
|
||||
}
|
||||
resp, err := o.repo.CreateToken(ctx, userAgentID, applicationID, req.GetSubject(), req.GetAudience(), req.GetScopes(), o.defaultAccessTokenLifetime) //PLANNED: lifetime from client
|
||||
resp, err := o.command.CreateUserToken(ctx, authReq.UserOrgID, userAgentID, applicationID, req.GetSubject(), req.GetAudience(), req.GetScopes(), o.defaultAccessTokenLifetime) //PLANNED: lifetime from client
|
||||
if err != nil {
|
||||
return "", time.Time{}, err
|
||||
}
|
||||
@ -113,7 +113,11 @@ func (o *OPStorage) TerminateSession(ctx context.Context, userID, clientID strin
|
||||
if !ok {
|
||||
return errors.ThrowPreconditionFailed(nil, "OIDC-fso7F", "no user agent id")
|
||||
}
|
||||
return o.repo.SignOut(ctx, userAgentID)
|
||||
userIDs, err := o.repo.UserSessionUserIDsByAgentID(ctx, userAgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.command.HumansSignOut(ctx, userAgentID, userIDs)
|
||||
}
|
||||
|
||||
func (o *OPStorage) GetSigningKey(ctx context.Context, keyCh chan<- jose.SigningKey, errCh chan<- error, timer <-chan time.Time) {
|
||||
|
@ -2,6 +2,7 @@ package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
@ -10,7 +11,6 @@ import (
|
||||
"golang.org/x/text/language"
|
||||
|
||||
http_utils "github.com/caos/zitadel/internal/api/http"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
)
|
||||
|
||||
@ -22,7 +22,7 @@ const (
|
||||
)
|
||||
|
||||
type AuthRequest struct {
|
||||
*model.AuthRequest
|
||||
*domain.AuthRequest
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetID() string {
|
||||
@ -92,26 +92,26 @@ func (a *AuthRequest) GetSubject() string {
|
||||
|
||||
func (a *AuthRequest) Done() bool {
|
||||
for _, step := range a.PossibleSteps {
|
||||
if step.Type() == model.NextStepRedirectToCallback {
|
||||
if step.Type() == domain.NextStepRedirectToCallback {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *AuthRequest) oidc() *model.AuthRequestOIDC {
|
||||
return a.Request.(*model.AuthRequestOIDC)
|
||||
func (a *AuthRequest) oidc() *domain.AuthRequestOIDC {
|
||||
return a.Request.(*domain.AuthRequestOIDC)
|
||||
}
|
||||
|
||||
func AuthRequestFromBusiness(authReq *model.AuthRequest) (_ op.AuthRequest, err error) {
|
||||
if _, ok := authReq.Request.(*model.AuthRequestOIDC); !ok {
|
||||
func AuthRequestFromBusiness(authReq *domain.AuthRequest) (_ op.AuthRequest, err error) {
|
||||
if _, ok := authReq.Request.(*domain.AuthRequestOIDC); !ok {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "OIDC-Haz7A", "auth request is not of type oidc")
|
||||
}
|
||||
return &AuthRequest{authReq}, nil
|
||||
}
|
||||
|
||||
func CreateAuthRequestToBusiness(ctx context.Context, authReq *oidc.AuthRequest, userAgentID, userID string) *model.AuthRequest {
|
||||
return &model.AuthRequest{
|
||||
func CreateAuthRequestToBusiness(ctx context.Context, authReq *oidc.AuthRequest, userAgentID, userID string) *domain.AuthRequest {
|
||||
return &domain.AuthRequest{
|
||||
AgentID: userAgentID,
|
||||
BrowserInfo: ParseBrowserInfoFromContext(ctx),
|
||||
ApplicationID: authReq.ClientID,
|
||||
@ -123,7 +123,7 @@ func CreateAuthRequestToBusiness(ctx context.Context, authReq *oidc.AuthRequest,
|
||||
LoginHint: authReq.LoginHint,
|
||||
MaxAuthAge: authReq.MaxAge,
|
||||
UserID: userID,
|
||||
Request: &model.AuthRequestOIDC{
|
||||
Request: &domain.AuthRequestOIDC{
|
||||
Scopes: authReq.Scopes,
|
||||
ResponseType: ResponseTypeToBusiness(authReq.ResponseType),
|
||||
Nonce: authReq.Nonce,
|
||||
@ -132,10 +132,10 @@ func CreateAuthRequestToBusiness(ctx context.Context, authReq *oidc.AuthRequest,
|
||||
}
|
||||
}
|
||||
|
||||
func ParseBrowserInfoFromContext(ctx context.Context) *model.BrowserInfo {
|
||||
func ParseBrowserInfoFromContext(ctx context.Context) *domain.BrowserInfo {
|
||||
userAgent, acceptLang := HttpHeadersFromContext(ctx)
|
||||
ip := IpFromContext(ctx)
|
||||
return &model.BrowserInfo{RemoteIP: ip, UserAgent: userAgent, AcceptLanguage: acceptLang}
|
||||
return &domain.BrowserInfo{RemoteIP: ip, UserAgent: userAgent, AcceptLanguage: acceptLang}
|
||||
}
|
||||
|
||||
func HttpHeadersFromContext(ctx context.Context) (userAgent, acceptLang string) {
|
||||
@ -160,22 +160,22 @@ func IpFromContext(ctx context.Context) net.IP {
|
||||
return net.ParseIP(ipString)
|
||||
}
|
||||
|
||||
func PromptToBusiness(prompt oidc.Prompt) model.Prompt {
|
||||
func PromptToBusiness(prompt oidc.Prompt) domain.Prompt {
|
||||
switch prompt {
|
||||
case oidc.PromptNone:
|
||||
return model.PromptNone
|
||||
return domain.PromptNone
|
||||
case oidc.PromptLogin:
|
||||
return model.PromptLogin
|
||||
return domain.PromptLogin
|
||||
case oidc.PromptConsent:
|
||||
return model.PromptConsent
|
||||
return domain.PromptConsent
|
||||
case oidc.PromptSelectAccount:
|
||||
return model.PromptSelectAccount
|
||||
return domain.PromptSelectAccount
|
||||
default:
|
||||
return model.PromptUnspecified
|
||||
return domain.PromptUnspecified
|
||||
}
|
||||
}
|
||||
|
||||
func ACRValuesToBusiness(values []string) []model.LevelOfAssurance {
|
||||
func ACRValuesToBusiness(values []string) []domain.LevelOfAssurance {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -190,52 +190,52 @@ func UILocalesToBusiness(tags []language.Tag) []string {
|
||||
return locales
|
||||
}
|
||||
|
||||
func ResponseTypeToBusiness(responseType oidc.ResponseType) model.OIDCResponseType {
|
||||
func ResponseTypeToBusiness(responseType oidc.ResponseType) domain.OIDCResponseType {
|
||||
switch responseType {
|
||||
case oidc.ResponseTypeCode:
|
||||
return model.OIDCResponseTypeCode
|
||||
return domain.OIDCResponseTypeCode
|
||||
case oidc.ResponseTypeIDTokenOnly:
|
||||
return model.OIDCResponseTypeIdToken
|
||||
return domain.OIDCResponseTypeIDToken
|
||||
case oidc.ResponseTypeIDToken:
|
||||
return model.OIDCResponseTypeIdTokenToken
|
||||
return domain.OIDCResponseTypeIDTokenToken
|
||||
default:
|
||||
return model.OIDCResponseTypeCode
|
||||
return domain.OIDCResponseTypeCode
|
||||
}
|
||||
}
|
||||
|
||||
func ResponseTypeToOIDC(responseType model.OIDCResponseType) oidc.ResponseType {
|
||||
func ResponseTypeToOIDC(responseType domain.OIDCResponseType) oidc.ResponseType {
|
||||
switch responseType {
|
||||
case model.OIDCResponseTypeCode:
|
||||
case domain.OIDCResponseTypeCode:
|
||||
return oidc.ResponseTypeCode
|
||||
case model.OIDCResponseTypeIdTokenToken:
|
||||
case domain.OIDCResponseTypeIDTokenToken:
|
||||
return oidc.ResponseTypeIDToken
|
||||
case model.OIDCResponseTypeIdToken:
|
||||
case domain.OIDCResponseTypeIDToken:
|
||||
return oidc.ResponseTypeIDTokenOnly
|
||||
default:
|
||||
return oidc.ResponseTypeCode
|
||||
}
|
||||
}
|
||||
|
||||
func CodeChallengeToBusiness(challenge string, method oidc.CodeChallengeMethod) *model.OIDCCodeChallenge {
|
||||
func CodeChallengeToBusiness(challenge string, method oidc.CodeChallengeMethod) *domain.OIDCCodeChallenge {
|
||||
if challenge == "" {
|
||||
return nil
|
||||
}
|
||||
challengeMethod := model.CodeChallengeMethodPlain
|
||||
challengeMethod := domain.CodeChallengeMethodPlain
|
||||
if method == oidc.CodeChallengeMethodS256 {
|
||||
challengeMethod = model.CodeChallengeMethodS256
|
||||
challengeMethod = domain.CodeChallengeMethodS256
|
||||
}
|
||||
return &model.OIDCCodeChallenge{
|
||||
return &domain.OIDCCodeChallenge{
|
||||
Challenge: challenge,
|
||||
Method: challengeMethod,
|
||||
}
|
||||
}
|
||||
|
||||
func CodeChallengeToOIDC(challenge *model.OIDCCodeChallenge) *oidc.CodeChallenge {
|
||||
func CodeChallengeToOIDC(challenge *domain.OIDCCodeChallenge) *oidc.CodeChallenge {
|
||||
if challenge == nil {
|
||||
return nil
|
||||
}
|
||||
challengeMethod := oidc.CodeChallengeMethodPlain
|
||||
if challenge.Method == model.CodeChallengeMethodS256 {
|
||||
if challenge.Method == domain.CodeChallengeMethodS256 {
|
||||
challengeMethod = oidc.CodeChallengeMethodS256
|
||||
}
|
||||
return &oidc.CodeChallenge{
|
||||
@ -244,12 +244,12 @@ func CodeChallengeToOIDC(challenge *model.OIDCCodeChallenge) *oidc.CodeChallenge
|
||||
}
|
||||
}
|
||||
|
||||
func AMRFromMFAType(mfaType model.MFAType) string {
|
||||
func AMRFromMFAType(mfaType domain.MFAType) string {
|
||||
switch mfaType {
|
||||
case model.MFATypeOTP:
|
||||
case domain.MFATypeOTP:
|
||||
return amrOTP
|
||||
case model.MFATypeU2F,
|
||||
model.MFATypeU2FUserVerification:
|
||||
case domain.MFATypeU2F,
|
||||
domain.MFATypeU2FUserVerification:
|
||||
return amrUserPresence
|
||||
default:
|
||||
return ""
|
||||
|
@ -3,6 +3,8 @@ package oidc
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/telemetry/metrics"
|
||||
"github.com/caos/zitadel/internal/v2/command"
|
||||
"github.com/caos/zitadel/internal/v2/query"
|
||||
"time"
|
||||
|
||||
"github.com/caos/logging"
|
||||
@ -46,13 +48,15 @@ type Endpoint struct {
|
||||
|
||||
type OPStorage struct {
|
||||
repo repository.Repository
|
||||
command *command.CommandSide
|
||||
query *query.QuerySide
|
||||
defaultLoginURL string
|
||||
defaultAccessTokenLifetime time.Duration
|
||||
defaultIdTokenLifetime time.Duration
|
||||
signingKeyAlgorithm string
|
||||
}
|
||||
|
||||
func NewProvider(ctx context.Context, config OPHandlerConfig, repo repository.Repository, localDevMode bool) op.OpenIDProvider {
|
||||
func NewProvider(ctx context.Context, config OPHandlerConfig, command *command.CommandSide, query *query.QuerySide, repo repository.Repository, localDevMode bool) op.OpenIDProvider {
|
||||
cookieHandler, err := middleware.NewUserAgentHandler(config.UserAgentCookieConfig, id.SonyFlakeGenerator, localDevMode)
|
||||
logging.Log("OIDC-sd4fd").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Panic("cannot user agent handler")
|
||||
config.OPConfig.CodeMethodS256 = true
|
||||
@ -60,7 +64,7 @@ func NewProvider(ctx context.Context, config OPHandlerConfig, repo repository.Re
|
||||
provider, err := op.NewOpenIDProvider(
|
||||
ctx,
|
||||
config.OPConfig,
|
||||
newStorage(config.StorageConfig, repo),
|
||||
newStorage(config.StorageConfig, command, query, repo),
|
||||
op.WithHttpInterceptors(
|
||||
middleware.MetricsHandler(metricTypes),
|
||||
middleware.TelemetryHandler(),
|
||||
@ -79,9 +83,11 @@ func NewProvider(ctx context.Context, config OPHandlerConfig, repo repository.Re
|
||||
return provider
|
||||
}
|
||||
|
||||
func newStorage(config StorageConfig, repo repository.Repository) *OPStorage {
|
||||
func newStorage(config StorageConfig, command *command.CommandSide, query *query.QuerySide, repo repository.Repository) *OPStorage {
|
||||
return &OPStorage{
|
||||
repo: repo,
|
||||
command: command,
|
||||
query: query,
|
||||
defaultLoginURL: config.DefaultLoginURL,
|
||||
signingKeyAlgorithm: config.SigningKeyAlgorithm,
|
||||
defaultAccessTokenLifetime: config.DefaultAccessTokenLifetime.Duration,
|
||||
|
@ -2,32 +2,30 @@ package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
user_model "github.com/caos/zitadel/internal/user/model"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
)
|
||||
|
||||
type AuthRequestRepository interface {
|
||||
CreateAuthRequest(ctx context.Context, request *model.AuthRequest) (*model.AuthRequest, error)
|
||||
AuthRequestByID(ctx context.Context, id, userAgentID string) (*model.AuthRequest, error)
|
||||
AuthRequestByIDCheckLoggedIn(ctx context.Context, id, userAgentID string) (*model.AuthRequest, error)
|
||||
AuthRequestByCode(ctx context.Context, code string) (*model.AuthRequest, error)
|
||||
CreateAuthRequest(ctx context.Context, request *domain.AuthRequest) (*domain.AuthRequest, error)
|
||||
AuthRequestByID(ctx context.Context, id, userAgentID string) (*domain.AuthRequest, error)
|
||||
AuthRequestByIDCheckLoggedIn(ctx context.Context, id, userAgentID string) (*domain.AuthRequest, error)
|
||||
AuthRequestByCode(ctx context.Context, code string) (*domain.AuthRequest, error)
|
||||
SaveAuthCode(ctx context.Context, id, code, userAgentID string) error
|
||||
DeleteAuthRequest(ctx context.Context, id string) error
|
||||
|
||||
CheckLoginName(ctx context.Context, id, loginName, userAgentID string) error
|
||||
CheckExternalUserLogin(ctx context.Context, authReqID, userAgentID string, user *model.ExternalUser, info *model.BrowserInfo) error
|
||||
CheckExternalUserLogin(ctx context.Context, authReqID, userAgentID string, user *domain.ExternalUser, info *domain.BrowserInfo) error
|
||||
SelectUser(ctx context.Context, id, userID, userAgentID string) error
|
||||
SelectExternalIDP(ctx context.Context, authReqID, idpConfigID, userAgentID string) error
|
||||
VerifyPassword(ctx context.Context, id, userID, password, userAgentID string, info *model.BrowserInfo) error
|
||||
VerifyPassword(ctx context.Context, id, userID, resourceOwner, password, userAgentID string, info *domain.BrowserInfo) error
|
||||
|
||||
VerifyMFAOTP(ctx context.Context, agentID, authRequestID, code, userAgentID string, info *model.BrowserInfo) error
|
||||
BeginMFAU2FLogin(ctx context.Context, userID, authRequestID, userAgentID string) (*user_model.WebAuthNLogin, error)
|
||||
VerifyMFAU2F(ctx context.Context, userID, authRequestID, userAgentID string, credentialData []byte, info *model.BrowserInfo) error
|
||||
BeginPasswordlessLogin(ctx context.Context, userID, authRequestID, userAgentID string) (*user_model.WebAuthNLogin, error)
|
||||
VerifyPasswordless(ctx context.Context, userID, authRequestID, userAgentID string, credentialData []byte, info *model.BrowserInfo) error
|
||||
VerifyMFAOTP(ctx context.Context, authRequestID, userID, resourceOwner, code, userAgentID string, info *domain.BrowserInfo) error
|
||||
BeginMFAU2FLogin(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string) (*domain.WebAuthNLogin, error)
|
||||
VerifyMFAU2F(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string, credentialData []byte, info *domain.BrowserInfo) error
|
||||
BeginPasswordlessLogin(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string) (*domain.WebAuthNLogin, error)
|
||||
VerifyPasswordless(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string, credentialData []byte, info *domain.BrowserInfo) error
|
||||
|
||||
LinkExternalUsers(ctx context.Context, authReqID, userAgentID string, info *model.BrowserInfo) error
|
||||
AutoRegisterExternalUser(ctx context.Context, user *user_model.User, externalIDP *user_model.ExternalIDP, member *org_model.OrgMember, authReqID, userAgentID, resourceOwner string, info *model.BrowserInfo) error
|
||||
LinkExternalUsers(ctx context.Context, authReqID, userAgentID string, info *domain.BrowserInfo) error
|
||||
AutoRegisterExternalUser(ctx context.Context, user *domain.Human, externalIDP *domain.ExternalIDP, orgMemberRoles []string, authReqID, userAgentID, resourceOwner string, info *domain.BrowserInfo) error
|
||||
ResetLinkingUsers(ctx context.Context, authReqID, userAgentID string) error
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package eventstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/v2/command"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"time"
|
||||
|
||||
"github.com/caos/logging"
|
||||
@ -9,10 +11,10 @@ import (
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
auth_req_model "github.com/caos/zitadel/internal/auth_request/model"
|
||||
cache "github.com/caos/zitadel/internal/auth_request/repository"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/eventstore/sdk"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model"
|
||||
iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model"
|
||||
@ -30,6 +32,7 @@ import (
|
||||
)
|
||||
|
||||
type AuthRequestRepo struct {
|
||||
Command *command.CommandSide
|
||||
UserEvents *user_event.UserEventstore
|
||||
OrgEvents *org_event.OrgEventstore
|
||||
AuthRequests cache.AuthRequestCache
|
||||
@ -37,6 +40,7 @@ type AuthRequestRepo struct {
|
||||
|
||||
UserSessionViewProvider userSessionViewProvider
|
||||
UserViewProvider userViewProvider
|
||||
UserCommandProvider userCommandProvider
|
||||
UserEventProvider userEventProvider
|
||||
OrgViewProvider orgViewProvider
|
||||
LoginPolicyViewProvider loginPolicyViewProvider
|
||||
@ -72,7 +76,10 @@ type idpProviderViewProvider interface {
|
||||
|
||||
type userEventProvider interface {
|
||||
UserEventsByID(ctx context.Context, id string, sequence uint64) ([]*es_models.Event, error)
|
||||
BulkAddExternalIDPs(ctx context.Context, userID string, externalIDPs []*user_model.ExternalIDP) error
|
||||
}
|
||||
|
||||
type userCommandProvider interface {
|
||||
BulkAddedHumanExternalIDP(ctx context.Context, userID, resourceOwner string, externalIDPs []*domain.ExternalIDP) error
|
||||
}
|
||||
|
||||
type orgViewProvider interface {
|
||||
@ -92,7 +99,7 @@ func (repo *AuthRequestRepo) Health(ctx context.Context) error {
|
||||
return repo.AuthRequests.Health(ctx)
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) CreateAuthRequest(ctx context.Context, request *model.AuthRequest) (_ *model.AuthRequest, err error) {
|
||||
func (repo *AuthRequestRepo) CreateAuthRequest(ctx context.Context, request *domain.AuthRequest) (_ *domain.AuthRequest, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
reqID, err := repo.IdGenerator.Next()
|
||||
@ -124,13 +131,13 @@ func (repo *AuthRequestRepo) CreateAuthRequest(ctx context.Context, request *mod
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) AuthRequestByID(ctx context.Context, id, userAgentID string) (_ *model.AuthRequest, err error) {
|
||||
func (repo *AuthRequestRepo) AuthRequestByID(ctx context.Context, id, userAgentID string) (_ *domain.AuthRequest, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
return repo.getAuthRequestNextSteps(ctx, id, userAgentID, false)
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) AuthRequestByIDCheckLoggedIn(ctx context.Context, id, userAgentID string) (_ *model.AuthRequest, err error) {
|
||||
func (repo *AuthRequestRepo) AuthRequestByIDCheckLoggedIn(ctx context.Context, id, userAgentID string) (_ *domain.AuthRequest, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
return repo.getAuthRequestNextSteps(ctx, id, userAgentID, true)
|
||||
@ -147,7 +154,7 @@ func (repo *AuthRequestRepo) SaveAuthCode(ctx context.Context, id, code, userAge
|
||||
return repo.AuthRequests.UpdateAuthRequest(ctx, request)
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) AuthRequestByCode(ctx context.Context, code string) (_ *model.AuthRequest, err error) {
|
||||
func (repo *AuthRequestRepo) AuthRequestByCode(ctx context.Context, code string) (_ *domain.AuthRequest, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
request, err := repo.AuthRequests.GetAuthRequestByCode(ctx, code)
|
||||
@ -200,7 +207,7 @@ func (repo *AuthRequestRepo) SelectExternalIDP(ctx context.Context, authReqID, i
|
||||
return repo.AuthRequests.UpdateAuthRequest(ctx, request)
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) CheckExternalUserLogin(ctx context.Context, authReqID, userAgentID string, externalUser *model.ExternalUser, info *model.BrowserInfo) (err error) {
|
||||
func (repo *AuthRequestRepo) CheckExternalUserLogin(ctx context.Context, authReqID, userAgentID string, externalUser *domain.ExternalUser, info *domain.BrowserInfo) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
request, err := repo.getAuthRequest(ctx, authReqID, userAgentID)
|
||||
@ -218,14 +225,14 @@ func (repo *AuthRequestRepo) CheckExternalUserLogin(ctx context.Context, authReq
|
||||
return err
|
||||
}
|
||||
|
||||
err = repo.UserEvents.ExternalLoginChecked(ctx, request.UserID, request.WithCurrentInfo(info))
|
||||
err = repo.Command.HumanExternalLoginChecked(ctx, request.UserOrgID, request.UserID, request.WithCurrentInfo(info))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return repo.AuthRequests.UpdateAuthRequest(ctx, request)
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) setLinkingUser(ctx context.Context, request *model.AuthRequest, externalUser *model.ExternalUser) error {
|
||||
func (repo *AuthRequestRepo) setLinkingUser(ctx context.Context, request *domain.AuthRequest, externalUser *domain.ExternalUser) error {
|
||||
request.LinkingUsers = append(request.LinkingUsers, externalUser)
|
||||
return repo.AuthRequests.UpdateAuthRequest(ctx, request)
|
||||
}
|
||||
@ -248,27 +255,27 @@ func (repo *AuthRequestRepo) SelectUser(ctx context.Context, id, userID, userAge
|
||||
return repo.AuthRequests.UpdateAuthRequest(ctx, request)
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) VerifyPassword(ctx context.Context, id, userID, password, userAgentID string, info *model.BrowserInfo) (err error) {
|
||||
func (repo *AuthRequestRepo) VerifyPassword(ctx context.Context, id, userID, resourceOwner, password, userAgentID string, info *domain.BrowserInfo) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
request, err := repo.getAuthRequestEnsureUser(ctx, id, userAgentID, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return repo.UserEvents.CheckPassword(ctx, userID, password, request.WithCurrentInfo(info))
|
||||
return repo.Command.HumanCheckPassword(ctx, resourceOwner, userID, password, request.WithCurrentInfo(info))
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) VerifyMFAOTP(ctx context.Context, authRequestID, userID, code, userAgentID string, info *model.BrowserInfo) (err error) {
|
||||
func (repo *AuthRequestRepo) VerifyMFAOTP(ctx context.Context, authRequestID, userID, resourceOwner, code, userAgentID string, info *domain.BrowserInfo) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
request, err := repo.getAuthRequestEnsureUser(ctx, authRequestID, userAgentID, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return repo.UserEvents.CheckMFAOTP(ctx, userID, code, request.WithCurrentInfo(info))
|
||||
return repo.Command.HumanCheckMFAOTP(ctx, userID, code, resourceOwner, request.WithCurrentInfo(info))
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) BeginMFAU2FLogin(ctx context.Context, userID, authRequestID, userAgentID string) (login *user_model.WebAuthNLogin, err error) {
|
||||
func (repo *AuthRequestRepo) BeginMFAU2FLogin(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string) (login *domain.WebAuthNLogin, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -276,51 +283,51 @@ func (repo *AuthRequestRepo) BeginMFAU2FLogin(ctx context.Context, userID, authR
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return repo.UserEvents.BeginU2FLogin(ctx, userID, request, true)
|
||||
return repo.Command.HumanBeginU2FLogin(ctx, userID, resourceOwner, request, true)
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) VerifyMFAU2F(ctx context.Context, userID, authRequestID, userAgentID string, credentialData []byte, info *model.BrowserInfo) (err error) {
|
||||
func (repo *AuthRequestRepo) VerifyMFAU2F(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string, credentialData []byte, info *domain.BrowserInfo) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
request, err := repo.getAuthRequestEnsureUser(ctx, authRequestID, userAgentID, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return repo.UserEvents.VerifyMFAU2F(ctx, userID, credentialData, request, true)
|
||||
return repo.Command.HumanFinishU2FLogin(ctx, userID, resourceOwner, credentialData, request, true)
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) BeginPasswordlessLogin(ctx context.Context, userID, authRequestID, userAgentID string) (login *user_model.WebAuthNLogin, err error) {
|
||||
func (repo *AuthRequestRepo) BeginPasswordlessLogin(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string) (login *domain.WebAuthNLogin, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
request, err := repo.getAuthRequestEnsureUser(ctx, authRequestID, userAgentID, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return repo.UserEvents.BeginPasswordlessLogin(ctx, userID, request, true)
|
||||
return repo.Command.HumanBeginPasswordlessLogin(ctx, userID, resourceOwner, request, true)
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) VerifyPasswordless(ctx context.Context, userID, authRequestID, userAgentID string, credentialData []byte, info *model.BrowserInfo) (err error) {
|
||||
func (repo *AuthRequestRepo) VerifyPasswordless(ctx context.Context, userID, resourceOwner, authRequestID, userAgentID string, credentialData []byte, info *domain.BrowserInfo) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
request, err := repo.getAuthRequestEnsureUser(ctx, authRequestID, userAgentID, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return repo.UserEvents.VerifyPasswordless(ctx, userID, credentialData, request, true)
|
||||
return repo.Command.HumanFinishPasswordlessLogin(ctx, userID, resourceOwner, credentialData, request, true)
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) LinkExternalUsers(ctx context.Context, authReqID, userAgentID string, info *model.BrowserInfo) (err error) {
|
||||
func (repo *AuthRequestRepo) LinkExternalUsers(ctx context.Context, authReqID, userAgentID string, info *domain.BrowserInfo) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
request, err := repo.getAuthRequest(ctx, authReqID, userAgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = linkExternalIDPs(ctx, repo.UserEventProvider, request)
|
||||
err = linkExternalIDPs(ctx, repo.UserCommandProvider, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = repo.UserEvents.ExternalLoginChecked(ctx, request.UserID, request.WithCurrentInfo(info))
|
||||
err = repo.Command.HumanExternalLoginChecked(ctx, request.UserOrgID, request.UserID, request.WithCurrentInfo(info))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -338,62 +345,29 @@ func (repo *AuthRequestRepo) ResetLinkingUsers(ctx context.Context, authReqID, u
|
||||
return repo.AuthRequests.UpdateAuthRequest(ctx, request)
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) AutoRegisterExternalUser(ctx context.Context, registerUser *user_model.User, externalIDP *user_model.ExternalIDP, orgMember *org_model.OrgMember, authReqID, userAgentID, resourceOwner string, info *model.BrowserInfo) (err error) {
|
||||
func (repo *AuthRequestRepo) AutoRegisterExternalUser(ctx context.Context, registerUser *domain.Human, externalIDP *domain.ExternalIDP, orgMemberRoles []string, authReqID, userAgentID, resourceOwner string, info *domain.BrowserInfo) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
request, err := repo.getAuthRequest(ctx, authReqID, userAgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
policyResourceOwner := authz.GetCtxData(ctx).OrgID
|
||||
if resourceOwner != "" {
|
||||
policyResourceOwner = resourceOwner
|
||||
}
|
||||
pwPolicy, err := repo.View.PasswordComplexityPolicyByAggregateID(policyResourceOwner)
|
||||
if errors.IsNotFound(err) {
|
||||
pwPolicy, err = repo.View.PasswordComplexityPolicyByAggregateID(repo.IAMID)
|
||||
}
|
||||
human, err := repo.Command.RegisterHuman(ctx, resourceOwner, registerUser, externalIDP, orgMemberRoles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pwPolicyView := iam_es_model.PasswordComplexityViewToModel(pwPolicy)
|
||||
orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(policyResourceOwner)
|
||||
if errors.IsNotFound(err) {
|
||||
orgPolicy, err = repo.View.OrgIAMPolicyByAggregateID(repo.IAMID)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
orgPolicyView := iam_es_model.OrgIAMViewToModel(orgPolicy)
|
||||
user, aggregates, err := repo.UserEvents.PrepareRegisterUser(ctx, registerUser, externalIDP, pwPolicyView, orgPolicyView, resourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if orgMember != nil {
|
||||
orgMember.UserID = user.AggregateID
|
||||
_, memberAggregate, err := repo.OrgEvents.PrepareAddOrgMember(ctx, orgMember, policyResourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
aggregates = append(aggregates, memberAggregate)
|
||||
}
|
||||
|
||||
err = sdk.PushAggregates(ctx, repo.UserEvents.PushAggregates, user.AppendEvents, aggregates...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request.UserID = user.AggregateID
|
||||
request.UserOrgID = user.ResourceOwner
|
||||
request.UserID = human.AggregateID
|
||||
request.UserOrgID = human.ResourceOwner
|
||||
request.SelectedIDPConfigID = externalIDP.IDPConfigID
|
||||
request.LinkingUsers = nil
|
||||
err = repo.UserEvents.ExternalLoginChecked(ctx, request.UserID, request.WithCurrentInfo(info))
|
||||
err = repo.Command.HumanExternalLoginChecked(ctx, request.UserOrgID, request.UserID, request.WithCurrentInfo(info))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return repo.AuthRequests.UpdateAuthRequest(ctx, request)
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) getAuthRequestNextSteps(ctx context.Context, id, userAgentID string, checkLoggedIn bool) (*model.AuthRequest, error) {
|
||||
func (repo *AuthRequestRepo) getAuthRequestNextSteps(ctx context.Context, id, userAgentID string, checkLoggedIn bool) (*domain.AuthRequest, error) {
|
||||
request, err := repo.getAuthRequest(ctx, id, userAgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -406,7 +380,7 @@ func (repo *AuthRequestRepo) getAuthRequestNextSteps(ctx context.Context, id, us
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) getAuthRequestEnsureUser(ctx context.Context, authRequestID, userAgentID, userID string) (*model.AuthRequest, error) {
|
||||
func (repo *AuthRequestRepo) getAuthRequestEnsureUser(ctx context.Context, authRequestID, userAgentID, userID string) (*domain.AuthRequest, error) {
|
||||
request, err := repo.getAuthRequest(ctx, authRequestID, userAgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -417,7 +391,7 @@ func (repo *AuthRequestRepo) getAuthRequestEnsureUser(ctx context.Context, authR
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) getAuthRequest(ctx context.Context, id, userAgentID string) (*model.AuthRequest, error) {
|
||||
func (repo *AuthRequestRepo) getAuthRequest(ctx context.Context, id, userAgentID string) (*domain.AuthRequest, error) {
|
||||
request, err := repo.AuthRequests.GetAuthRequestByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -432,22 +406,24 @@ func (repo *AuthRequestRepo) getAuthRequest(ctx context.Context, id, userAgentID
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) getLoginPolicyAndIDPProviders(ctx context.Context, orgID string) (*iam_model.LoginPolicyView, []*iam_model.IDPProviderView, error) {
|
||||
func (repo *AuthRequestRepo) getLoginPolicyAndIDPProviders(ctx context.Context, orgID string) (*domain.LoginPolicy, []*domain.IDPProvider, error) {
|
||||
policy, err := repo.getLoginPolicy(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if !policy.AllowExternalIDP {
|
||||
return policy, nil, nil
|
||||
return policy.ToLoginPolicyDomain(), nil, nil
|
||||
}
|
||||
idpProviders, err := getLoginPolicyIDPProviders(repo.IDPProviderViewProvider, repo.IAMID, orgID, policy.Default)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return policy, idpProviders, nil
|
||||
|
||||
providers := iam_model.IdpProviderViewsToDomain(idpProviders)
|
||||
return policy.ToLoginPolicyDomain(), providers, nil
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) fillLoginPolicy(ctx context.Context, request *model.AuthRequest) error {
|
||||
func (repo *AuthRequestRepo) fillLoginPolicy(ctx context.Context, request *domain.AuthRequest) error {
|
||||
orgID := request.RequestedOrgID
|
||||
if orgID == "" {
|
||||
orgID = request.UserOrgID
|
||||
@ -467,7 +443,7 @@ func (repo *AuthRequestRepo) fillLoginPolicy(ctx context.Context, request *model
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *model.AuthRequest, loginName string) (err error) {
|
||||
func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *domain.AuthRequest, loginName string) (err error) {
|
||||
user := new(user_view_model.UserView)
|
||||
if request.RequestedOrgID != "" {
|
||||
user, err = repo.View.UserByLoginNameAndResourceOwner(loginName, request.RequestedOrgID)
|
||||
@ -488,7 +464,7 @@ func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *model.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo AuthRequestRepo) checkLoginPolicyWithResourceOwner(ctx context.Context, request *model.AuthRequest, user *user_view_model.UserView) error {
|
||||
func (repo AuthRequestRepo) checkLoginPolicyWithResourceOwner(ctx context.Context, request *domain.AuthRequest, user *user_view_model.UserView) error {
|
||||
loginPolicy, idpProviders, err := repo.getLoginPolicyAndIDPProviders(ctx, user.ResourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -507,7 +483,7 @@ func (repo AuthRequestRepo) checkLoginPolicyWithResourceOwner(ctx context.Contex
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) checkSelectedExternalIDP(request *model.AuthRequest, idpConfigID string) error {
|
||||
func (repo *AuthRequestRepo) checkSelectedExternalIDP(request *domain.AuthRequest, idpConfigID string) error {
|
||||
for _, externalIDP := range request.AllowedExternalIDPs {
|
||||
if externalIDP.IDPConfigID == idpConfigID {
|
||||
request.SelectedIDPConfigID = idpConfigID
|
||||
@ -517,7 +493,7 @@ func (repo *AuthRequestRepo) checkSelectedExternalIDP(request *model.AuthRequest
|
||||
return errors.ThrowNotFound(nil, "LOGIN-Nsm8r", "Errors.User.ExternalIDP.NotAllowed")
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) checkExternalUserLogin(request *model.AuthRequest, idpConfigID, externalUserID string) (err error) {
|
||||
func (repo *AuthRequestRepo) checkExternalUserLogin(request *domain.AuthRequest, idpConfigID, externalUserID string) (err error) {
|
||||
externalIDP := new(user_view_model.ExternalIDPView)
|
||||
if request.RequestedOrgID != "" {
|
||||
externalIDP, err = repo.View.ExternalIDPByExternalUserIDAndIDPConfigIDAndResourceOwner(externalUserID, idpConfigID, request.RequestedOrgID)
|
||||
@ -531,27 +507,27 @@ func (repo *AuthRequestRepo) checkExternalUserLogin(request *model.AuthRequest,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *model.AuthRequest, checkLoggedIn bool) ([]model.NextStep, error) {
|
||||
func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.AuthRequest, checkLoggedIn bool) ([]domain.NextStep, error) {
|
||||
if request == nil {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "EVENT-ds27a", "Errors.Internal")
|
||||
}
|
||||
steps := make([]model.NextStep, 0)
|
||||
if !checkLoggedIn && request.Prompt == model.PromptNone {
|
||||
return append(steps, &model.RedirectToCallbackStep{}), nil
|
||||
steps := make([]domain.NextStep, 0)
|
||||
if !checkLoggedIn && request.Prompt == domain.PromptNone {
|
||||
return append(steps, &domain.RedirectToCallbackStep{}), nil
|
||||
}
|
||||
if request.UserID == "" {
|
||||
if request.LinkingUsers != nil && len(request.LinkingUsers) > 0 {
|
||||
steps = append(steps, new(model.ExternalNotFoundOptionStep))
|
||||
steps = append(steps, new(domain.ExternalNotFoundOptionStep))
|
||||
return steps, nil
|
||||
}
|
||||
steps = append(steps, new(model.LoginStep))
|
||||
if request.Prompt == model.PromptSelectAccount || request.Prompt == model.PromptUnspecified {
|
||||
steps = append(steps, new(domain.LoginStep))
|
||||
if request.Prompt == domain.PromptSelectAccount || request.Prompt == domain.PromptUnspecified {
|
||||
users, err := repo.usersForUserSelection(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(users) > 0 || request.Prompt == model.PromptSelectAccount {
|
||||
steps = append(steps, &model.SelectUserStep{Users: users})
|
||||
if len(users) > 0 || request.Prompt == domain.PromptSelectAccount {
|
||||
steps = append(steps, &domain.SelectUserStep{Users: users})
|
||||
}
|
||||
}
|
||||
return steps, nil
|
||||
@ -572,7 +548,7 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *model.AuthR
|
||||
if selectedIDPConfigID == "" {
|
||||
selectedIDPConfigID = userSession.SelectedIDPConfigID
|
||||
}
|
||||
return append(steps, &model.ExternalLoginStep{SelectedIDPConfigID: selectedIDPConfigID}), nil
|
||||
return append(steps, &domain.ExternalLoginStep{SelectedIDPConfigID: selectedIDPConfigID}), nil
|
||||
}
|
||||
if isInternalLogin || (!isInternalLogin && len(request.LinkingUsers) > 0) {
|
||||
step := repo.firstFactorChecked(request, user, userSession)
|
||||
@ -590,13 +566,13 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *model.AuthR
|
||||
}
|
||||
|
||||
if user.PasswordChangeRequired {
|
||||
steps = append(steps, &model.ChangePasswordStep{})
|
||||
steps = append(steps, &domain.ChangePasswordStep{})
|
||||
}
|
||||
if !user.IsEmailVerified {
|
||||
steps = append(steps, &model.VerifyEMailStep{})
|
||||
steps = append(steps, &domain.VerifyEMailStep{})
|
||||
}
|
||||
if user.UsernameChangeRequired {
|
||||
steps = append(steps, &model.ChangeUsernameStep{})
|
||||
steps = append(steps, &domain.ChangeUsernameStep{})
|
||||
}
|
||||
|
||||
if user.PasswordChangeRequired || !user.IsEmailVerified || user.UsernameChangeRequired {
|
||||
@ -604,7 +580,7 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *model.AuthR
|
||||
}
|
||||
|
||||
if request.LinkingUsers != nil && len(request.LinkingUsers) != 0 {
|
||||
return append(steps, &model.LinkUsersStep{}), nil
|
||||
return append(steps, &domain.LinkUsersStep{}), nil
|
||||
|
||||
}
|
||||
//PLANNED: consent step
|
||||
@ -614,46 +590,46 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *model.AuthR
|
||||
return nil, err
|
||||
}
|
||||
if missing {
|
||||
return append(steps, &model.GrantRequiredStep{}), nil
|
||||
return append(steps, &domain.GrantRequiredStep{}), nil
|
||||
}
|
||||
|
||||
return append(steps, &model.RedirectToCallbackStep{}), nil
|
||||
return append(steps, &domain.RedirectToCallbackStep{}), nil
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) usersForUserSelection(request *model.AuthRequest) ([]model.UserSelection, error) {
|
||||
func (repo *AuthRequestRepo) usersForUserSelection(request *domain.AuthRequest) ([]domain.UserSelection, error) {
|
||||
userSessions, err := userSessionsByUserAgentID(repo.UserSessionViewProvider, request.AgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
users := make([]model.UserSelection, len(userSessions))
|
||||
users := make([]domain.UserSelection, len(userSessions))
|
||||
for i, session := range userSessions {
|
||||
users[i] = model.UserSelection{
|
||||
users[i] = domain.UserSelection{
|
||||
UserID: session.UserID,
|
||||
DisplayName: session.DisplayName,
|
||||
LoginName: session.LoginName,
|
||||
UserSessionState: session.State,
|
||||
UserSessionState: auth_req_model.UserSessionStateToDomain(session.State),
|
||||
SelectionPossible: request.RequestedOrgID == "" || request.RequestedOrgID == session.ResourceOwner,
|
||||
}
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) firstFactorChecked(request *model.AuthRequest, user *user_model.UserView, userSession *user_model.UserSessionView) model.NextStep {
|
||||
func (repo *AuthRequestRepo) firstFactorChecked(request *domain.AuthRequest, user *user_model.UserView, userSession *user_model.UserSessionView) domain.NextStep {
|
||||
if user.InitRequired {
|
||||
return &model.InitUserStep{PasswordSet: user.PasswordSet}
|
||||
return &domain.InitUserStep{PasswordSet: user.PasswordSet}
|
||||
}
|
||||
|
||||
var step model.NextStep
|
||||
if request.LoginPolicy.PasswordlessType != iam_model.PasswordlessTypeNotAllowed && user.IsPasswordlessReady() {
|
||||
var step domain.NextStep
|
||||
if request.LoginPolicy.PasswordlessType != domain.PasswordlessTypeNotAllowed && user.IsPasswordlessReady() {
|
||||
if checkVerificationTime(userSession.PasswordlessVerification, repo.MultiFactorCheckLifeTime) {
|
||||
request.AuthTime = userSession.PasswordlessVerification
|
||||
return nil
|
||||
}
|
||||
step = &model.PasswordlessStep{}
|
||||
step = &domain.PasswordlessStep{}
|
||||
}
|
||||
|
||||
if !user.PasswordSet {
|
||||
return &model.InitPasswordStep{}
|
||||
return &domain.InitPasswordStep{}
|
||||
}
|
||||
|
||||
if checkVerificationTime(userSession.PasswordVerification, repo.PasswordCheckLifeTime) {
|
||||
@ -664,13 +640,13 @@ func (repo *AuthRequestRepo) firstFactorChecked(request *model.AuthRequest, user
|
||||
if step != nil {
|
||||
return step
|
||||
}
|
||||
return &model.PasswordStep{}
|
||||
return &domain.PasswordStep{}
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView, request *model.AuthRequest, user *user_model.UserView) (model.NextStep, bool, error) {
|
||||
func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView, request *domain.AuthRequest, user *user_model.UserView) (domain.NextStep, bool, error) {
|
||||
mfaLevel := request.MFALevel()
|
||||
allowedProviders, required := user.MFATypesAllowed(mfaLevel, request.LoginPolicy)
|
||||
promptRequired := (user.MFAMaxSetUp < mfaLevel) || (len(allowedProviders) == 0 && required)
|
||||
promptRequired := (auth_req_model.MFALevelToDomain(user.MFAMaxSetUp) < mfaLevel) || (len(allowedProviders) == 0 && required)
|
||||
if promptRequired || !repo.mfaSkippedOrSetUp(user) {
|
||||
types := user.MFATypesSetupPossible(mfaLevel, request.LoginPolicy)
|
||||
if promptRequired && len(types) == 0 {
|
||||
@ -679,7 +655,7 @@ func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView,
|
||||
if len(types) == 0 {
|
||||
return nil, true, nil
|
||||
}
|
||||
return &model.MFAPromptStep{
|
||||
return &domain.MFAPromptStep{
|
||||
Required: promptRequired,
|
||||
MFAProviders: types,
|
||||
}, false, nil
|
||||
@ -687,26 +663,26 @@ func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView,
|
||||
switch mfaLevel {
|
||||
default:
|
||||
fallthrough
|
||||
case model.MFALevelNotSetUp:
|
||||
case domain.MFALevelNotSetUp:
|
||||
if len(allowedProviders) == 0 {
|
||||
return nil, true, nil
|
||||
}
|
||||
fallthrough
|
||||
case model.MFALevelSecondFactor:
|
||||
case domain.MFALevelSecondFactor:
|
||||
if checkVerificationTime(userSession.SecondFactorVerification, repo.SecondFactorCheckLifeTime) {
|
||||
request.MFAsVerified = append(request.MFAsVerified, userSession.SecondFactorVerificationType)
|
||||
request.MFAsVerified = append(request.MFAsVerified, auth_req_model.MFATypeToDomain(userSession.SecondFactorVerificationType))
|
||||
request.AuthTime = userSession.SecondFactorVerification
|
||||
return nil, true, nil
|
||||
}
|
||||
fallthrough
|
||||
case model.MFALevelMultiFactor:
|
||||
case domain.MFALevelMultiFactor:
|
||||
if checkVerificationTime(userSession.MultiFactorVerification, repo.MultiFactorCheckLifeTime) {
|
||||
request.MFAsVerified = append(request.MFAsVerified, userSession.MultiFactorVerificationType)
|
||||
request.MFAsVerified = append(request.MFAsVerified, auth_req_model.MFATypeToDomain(userSession.MultiFactorVerificationType))
|
||||
request.AuthTime = userSession.MultiFactorVerification
|
||||
return nil, true, nil
|
||||
}
|
||||
}
|
||||
return &model.MFAVerificationStep{
|
||||
return &domain.MFAVerificationStep{
|
||||
MFAProviders: allowedProviders,
|
||||
}, false, nil
|
||||
}
|
||||
@ -733,7 +709,7 @@ func (repo *AuthRequestRepo) getLoginPolicy(ctx context.Context, orgID string) (
|
||||
return iam_es_model.LoginPolicyViewToModel(policy), err
|
||||
}
|
||||
|
||||
func setOrgID(orgViewProvider orgViewProvider, request *model.AuthRequest) error {
|
||||
func setOrgID(orgViewProvider orgViewProvider, request *domain.AuthRequest) error {
|
||||
primaryDomain := request.GetScopeOrgPrimaryDomain()
|
||||
if primaryDomain == "" {
|
||||
return nil
|
||||
@ -881,14 +857,14 @@ func userByID(ctx context.Context, viewProvider userViewProvider, eventProvider
|
||||
return user_view_model.UserToModel(&userCopy), nil
|
||||
}
|
||||
|
||||
func linkExternalIDPs(ctx context.Context, userEventProvider userEventProvider, request *model.AuthRequest) error {
|
||||
externalIDPs := make([]*user_model.ExternalIDP, len(request.LinkingUsers))
|
||||
func linkExternalIDPs(ctx context.Context, userCommandProvider userCommandProvider, request *domain.AuthRequest) error {
|
||||
externalIDPs := make([]*domain.ExternalIDP, len(request.LinkingUsers))
|
||||
for i, linkingUser := range request.LinkingUsers {
|
||||
externalIDP := &user_model.ExternalIDP{
|
||||
ObjectRoot: es_models.ObjectRoot{AggregateID: request.UserID},
|
||||
IDPConfigID: linkingUser.IDPConfigID,
|
||||
UserID: linkingUser.ExternalUserID,
|
||||
DisplayName: linkingUser.DisplayName,
|
||||
externalIDP := &domain.ExternalIDP{
|
||||
ObjectRoot: es_models.ObjectRoot{AggregateID: request.UserID},
|
||||
IDPConfigID: linkingUser.IDPConfigID,
|
||||
ExternalUserID: linkingUser.ExternalUserID,
|
||||
DisplayName: linkingUser.DisplayName,
|
||||
}
|
||||
externalIDPs[i] = externalIDP
|
||||
}
|
||||
@ -896,10 +872,10 @@ func linkExternalIDPs(ctx context.Context, userEventProvider userEventProvider,
|
||||
UserID: "LOGIN",
|
||||
OrgID: request.UserOrgID,
|
||||
}
|
||||
return userEventProvider.BulkAddExternalIDPs(authz.SetCtxData(ctx, data), request.UserID, externalIDPs)
|
||||
return userCommandProvider.BulkAddedHumanExternalIDP(authz.SetCtxData(ctx, data), request.UserID, request.UserOrgID, externalIDPs)
|
||||
}
|
||||
|
||||
func linkingIDPConfigExistingInAllowedIDPs(linkingUsers []*model.ExternalUser, idpProviders []*iam_model.IDPProviderView) bool {
|
||||
func linkingIDPConfigExistingInAllowedIDPs(linkingUsers []*domain.ExternalUser, idpProviders []*domain.IDPProvider) bool {
|
||||
for _, linkingUser := range linkingUsers {
|
||||
exists := false
|
||||
for _, idp := range idpProviders {
|
||||
@ -914,10 +890,10 @@ func linkingIDPConfigExistingInAllowedIDPs(linkingUsers []*model.ExternalUser, i
|
||||
}
|
||||
return true
|
||||
}
|
||||
func userGrantRequired(ctx context.Context, request *model.AuthRequest, user *user_model.UserView, userGrantProvider userGrantProvider) (_ bool, err error) {
|
||||
func userGrantRequired(ctx context.Context, request *domain.AuthRequest, user *user_model.UserView, userGrantProvider userGrantProvider) (_ bool, err error) {
|
||||
var app *project_view_model.ApplicationView
|
||||
switch request.Request.Type() {
|
||||
case model.AuthRequestTypeOIDC:
|
||||
case domain.AuthRequestTypeOIDC:
|
||||
app, err = userGrantProvider.ApplicationByClientID(ctx, request.ApplicationID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -2,12 +2,8 @@ package eventstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/logging"
|
||||
auth_req_model "github.com/caos/zitadel/internal/auth_request/model"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||
user_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
||||
@ -22,34 +18,6 @@ type TokenRepo struct {
|
||||
View *view.View
|
||||
}
|
||||
|
||||
func (repo *TokenRepo) CreateToken(ctx context.Context, agentID, clientID, userID string, audience, scopes []string, lifetime time.Duration) (*usr_model.Token, error) {
|
||||
preferredLanguage := ""
|
||||
user, _ := repo.View.UserByID(userID)
|
||||
if user != nil {
|
||||
preferredLanguage = user.PreferredLanguage
|
||||
}
|
||||
|
||||
for _, scope := range scopes {
|
||||
if strings.HasPrefix(scope, auth_req_model.ProjectIDScope) && strings.HasSuffix(scope, auth_req_model.AudSuffix) {
|
||||
audience = append(audience, strings.TrimSuffix(strings.TrimPrefix(scope, auth_req_model.ProjectIDScope), auth_req_model.AudSuffix))
|
||||
}
|
||||
}
|
||||
|
||||
now := time.Now().UTC()
|
||||
token := &usr_model.Token{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: userID,
|
||||
},
|
||||
UserAgentID: agentID,
|
||||
ApplicationID: clientID,
|
||||
Audience: audience,
|
||||
Scopes: scopes,
|
||||
Expiration: now.Add(lifetime),
|
||||
PreferredLanguage: preferredLanguage,
|
||||
}
|
||||
return repo.UserEvents.TokenAdded(ctx, token)
|
||||
}
|
||||
|
||||
func (repo *TokenRepo) IsTokenValid(ctx context.Context, userID, tokenID string) (bool, error) {
|
||||
token, err := repo.TokenByID(ctx, userID, tokenID)
|
||||
if err == nil {
|
||||
|
@ -36,14 +36,6 @@ func (repo *UserRepo) Health(ctx context.Context) error {
|
||||
return repo.UserEvents.Health(ctx)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) Register(ctx context.Context, user *model.User, orgMember *org_model.OrgMember, resourceOwner string) (*model.User, error) {
|
||||
return repo.registerUser(ctx, user, nil, orgMember, resourceOwner)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) RegisterExternalUser(ctx context.Context, user *model.User, externalIDP *model.ExternalIDP, orgMember *org_model.OrgMember, resourceOwner string) (*model.User, error) {
|
||||
return repo.registerUser(ctx, user, externalIDP, orgMember, resourceOwner)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) registerUser(ctx context.Context, registerUser *model.User, externalIDP *model.ExternalIDP, orgMember *org_model.OrgMember, resourceOwner string) (*model.User, error) {
|
||||
policyResourceOwner := authz.GetCtxData(ctx).OrgID
|
||||
if resourceOwner != "" {
|
||||
@ -133,18 +125,6 @@ func (repo *UserRepo) MyEmail(ctx context.Context) (*model.Email, error) {
|
||||
return user.GetEmail()
|
||||
}
|
||||
|
||||
func (repo *UserRepo) VerifyEmail(ctx context.Context, userID, code string) error {
|
||||
return repo.UserEvents.VerifyEmail(ctx, userID, code)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) VerifyMyEmail(ctx context.Context, code string) error {
|
||||
return repo.UserEvents.VerifyEmail(ctx, authz.GetCtxData(ctx).UserID, code)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) ResendEmailVerificationMail(ctx context.Context, userID string) error {
|
||||
return repo.UserEvents.CreateEmailVerificationCode(ctx, userID)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) MyPhone(ctx context.Context) (*model.Phone, error) {
|
||||
user, err := repo.UserByID(ctx, authz.GetCtxData(ctx).UserID)
|
||||
if err != nil {
|
||||
@ -156,21 +136,6 @@ func (repo *UserRepo) MyPhone(ctx context.Context) (*model.Phone, error) {
|
||||
return user.GetPhone()
|
||||
}
|
||||
|
||||
func (repo *UserRepo) ChangeMyPhone(ctx context.Context, phone *model.Phone) (*model.Phone, error) {
|
||||
if err := checkIDs(ctx, phone.ObjectRoot); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return repo.UserEvents.ChangePhone(ctx, phone)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) RemoveMyPhone(ctx context.Context) error {
|
||||
return repo.UserEvents.RemovePhone(ctx, authz.GetCtxData(ctx).UserID)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) VerifyMyPhone(ctx context.Context, code string) error {
|
||||
return repo.UserEvents.VerifyPhone(ctx, authz.GetCtxData(ctx).UserID, code)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) MyAddress(ctx context.Context) (*model.Address, error) {
|
||||
user, err := repo.UserByID(ctx, authz.GetCtxData(ctx).UserID)
|
||||
if err != nil {
|
||||
@ -182,34 +147,6 @@ func (repo *UserRepo) MyAddress(ctx context.Context) (*model.Address, error) {
|
||||
return user.GetAddress()
|
||||
}
|
||||
|
||||
func (repo *UserRepo) ChangeMyPassword(ctx context.Context, old, new string) error {
|
||||
policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
|
||||
if errors.IsNotFound(err) {
|
||||
policy, err = repo.View.PasswordComplexityPolicyByAggregateID(repo.SystemDefaults.IamID)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pwPolicyView := iam_es_model.PasswordComplexityViewToModel(policy)
|
||||
_, err = repo.UserEvents.ChangePassword(ctx, pwPolicyView, authz.GetCtxData(ctx).UserID, old, new, "")
|
||||
return err
|
||||
}
|
||||
|
||||
func (repo *UserRepo) ChangePassword(ctx context.Context, userID, old, new, userAgentID string) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
|
||||
if errors.IsNotFound(err) {
|
||||
policy, err = repo.View.PasswordComplexityPolicyByAggregateID(repo.SystemDefaults.IamID)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pwPolicyView := iam_es_model.PasswordComplexityViewToModel(policy)
|
||||
_, err = repo.UserEvents.ChangePassword(ctx, pwPolicyView, userID, old, new, userAgentID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (repo *UserRepo) MyUserMFAs(ctx context.Context) ([]*model.MultiFactor, error) {
|
||||
user, err := repo.UserByID(ctx, authz.GetCtxData(ctx).UserID)
|
||||
if err != nil {
|
||||
@ -225,142 +162,24 @@ func (repo *UserRepo) MyUserMFAs(ctx context.Context) ([]*model.MultiFactor, err
|
||||
return mfas, nil
|
||||
}
|
||||
|
||||
func (repo *UserRepo) AddMFAOTP(ctx context.Context, userID string) (*model.OTP, error) {
|
||||
accountName := ""
|
||||
user, err := repo.UserByID(ctx, userID)
|
||||
if err != nil {
|
||||
logging.Log("EVENT-Fk93s").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname")
|
||||
} else {
|
||||
accountName = user.PreferredLoginName
|
||||
}
|
||||
return repo.UserEvents.AddOTP(ctx, userID, accountName)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) VerifyMFAOTPSetup(ctx context.Context, userID, code, userAgentID string) error {
|
||||
return repo.UserEvents.CheckMFAOTPSetup(ctx, userID, code, userAgentID)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) RemoveMyMFAOTP(ctx context.Context) error {
|
||||
return repo.UserEvents.RemoveOTP(ctx, authz.GetCtxData(ctx).UserID)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) AddMFAU2F(ctx context.Context, userID string) (*model.WebAuthNToken, error) {
|
||||
accountName := ""
|
||||
user, err := repo.UserByID(ctx, userID)
|
||||
if err != nil {
|
||||
logging.Log("EVENT-DAqe1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname")
|
||||
} else {
|
||||
accountName = user.PreferredLoginName
|
||||
}
|
||||
return repo.UserEvents.AddU2F(ctx, userID, accountName, true)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) AddMyMFAU2F(ctx context.Context) (*model.WebAuthNToken, error) {
|
||||
userID := authz.GetCtxData(ctx).UserID
|
||||
accountName := ""
|
||||
user, err := repo.UserByID(ctx, userID)
|
||||
if err != nil {
|
||||
logging.Log("EVENT-Ghwl1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname")
|
||||
} else {
|
||||
accountName = user.PreferredLoginName
|
||||
}
|
||||
return repo.UserEvents.AddU2F(ctx, userID, accountName, false)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) VerifyMFAU2FSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error {
|
||||
return repo.UserEvents.VerifyU2FSetup(ctx, userID, tokenName, userAgentID, credentialData)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) GetPasswordless(ctx context.Context, userID string) ([]*model.WebAuthNToken, error) {
|
||||
return repo.UserEvents.GetPasswordless(ctx, userID)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) AddPasswordless(ctx context.Context, userID string) (*model.WebAuthNToken, error) {
|
||||
accountName := ""
|
||||
user, err := repo.UserByID(ctx, userID)
|
||||
if err != nil {
|
||||
logging.Log("EVENT-Vj2k1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname")
|
||||
} else {
|
||||
accountName = user.PreferredLoginName
|
||||
}
|
||||
return repo.UserEvents.AddPasswordless(ctx, userID, accountName, true)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) GetMyPasswordless(ctx context.Context) ([]*model.WebAuthNToken, error) {
|
||||
return repo.UserEvents.GetPasswordless(ctx, authz.GetCtxData(ctx).UserID)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) VerifyPasswordlessSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error {
|
||||
return repo.UserEvents.VerifyPasswordlessSetup(ctx, userID, tokenName, userAgentID, credentialData)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) RemoveMyPasswordless(ctx context.Context, webAuthNTokenID string) error {
|
||||
return repo.UserEvents.RemovePasswordlessToken(ctx, authz.GetCtxData(ctx).UserID, webAuthNTokenID)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) ChangeMyUsername(ctx context.Context, username string) error {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(ctxData.OrgID)
|
||||
if errors.IsNotFound(err) {
|
||||
orgPolicy, err = repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
orgPolicyView := iam_es_model.OrgIAMViewToModel(orgPolicy)
|
||||
return repo.UserEvents.ChangeUsername(ctx, ctxData.UserID, username, orgPolicyView)
|
||||
}
|
||||
func (repo *UserRepo) ResendInitVerificationMail(ctx context.Context, userID string) error {
|
||||
_, err := repo.UserEvents.CreateInitializeUserCodeByID(ctx, userID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (repo *UserRepo) VerifyInitCode(ctx context.Context, userID, code, password string) error {
|
||||
policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
|
||||
if errors.IsNotFound(err) {
|
||||
policy, err = repo.View.PasswordComplexityPolicyByAggregateID(repo.SystemDefaults.IamID)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pwPolicyView := iam_es_model.PasswordComplexityViewToModel(policy)
|
||||
return repo.UserEvents.VerifyInitCode(ctx, pwPolicyView, userID, code, password)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) SkipMFAInit(ctx context.Context, userID string) error {
|
||||
return repo.UserEvents.SkipMFAInit(ctx, userID)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) RequestPasswordReset(ctx context.Context, loginname string) error {
|
||||
user, err := repo.View.UserByLoginName(loginname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return repo.UserEvents.RequestSetPassword(ctx, user.ID, model.NotificationTypeEmail)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) SetPassword(ctx context.Context, userID, code, password, userAgentID string) error {
|
||||
policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
|
||||
if errors.IsNotFound(err) {
|
||||
policy, err = repo.View.PasswordComplexityPolicyByAggregateID(repo.SystemDefaults.IamID)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pwPolicyView := iam_es_model.PasswordComplexityViewToModel(policy)
|
||||
return repo.UserEvents.SetPassword(ctx, pwPolicyView, userID, code, password, userAgentID)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) SignOut(ctx context.Context, agentID string) error {
|
||||
func (repo *UserRepo) UserSessionUserIDsByAgentID(ctx context.Context, agentID string) ([]string, error) {
|
||||
userSessions, err := repo.View.UserSessionsByAgentID(agentID)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
userIDs := make([]string, len(userSessions))
|
||||
for i, session := range userSessions {
|
||||
userIDs[i] = session.UserID
|
||||
}
|
||||
return repo.UserEvents.SignOut(ctx, agentID, userIDs)
|
||||
return userIDs, nil
|
||||
}
|
||||
|
||||
func (repo *UserRepo) UserByID(ctx context.Context, id string) (*model.UserView, error) {
|
||||
@ -385,6 +204,27 @@ func (repo *UserRepo) UserByID(ctx context.Context, id string) (*model.UserView,
|
||||
return usr_view_model.UserToModel(&userCopy), nil
|
||||
}
|
||||
|
||||
func (repo *UserRepo) UserByLoginName(ctx context.Context, loginname string) (*model.UserView, error) {
|
||||
user, err := repo.View.UserByLoginName(loginname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
events, err := repo.UserEvents.UserEventsByID(ctx, user.ID, user.Sequence)
|
||||
if err != nil {
|
||||
logging.Log("EVENT-PSoc3").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error retrieving new events")
|
||||
return usr_view_model.UserToModel(user), nil
|
||||
}
|
||||
userCopy := *user
|
||||
for _, event := range events {
|
||||
if err := userCopy.AppendEvent(event); err != nil {
|
||||
return usr_view_model.UserToModel(user), nil
|
||||
}
|
||||
}
|
||||
if userCopy.State == int32(model.UserStateDeleted) {
|
||||
return nil, errors.ThrowNotFound(nil, "EVENT-vZ8us", "Errors.User.NotFound")
|
||||
}
|
||||
return usr_view_model.UserToModel(&userCopy), nil
|
||||
}
|
||||
func (repo *UserRepo) MyUserChanges(ctx context.Context, lastSequence uint64, limit uint64, sortAscending bool) (*model.UserChanges, error) {
|
||||
changes, err := repo.UserEvents.UserChanges(ctx, authz.GetCtxData(ctx).UserID, lastSequence, limit, sortAscending)
|
||||
if err != nil {
|
||||
@ -405,19 +245,6 @@ func (repo *UserRepo) MyUserChanges(ctx context.Context, lastSequence uint64, li
|
||||
return changes, nil
|
||||
}
|
||||
|
||||
func (repo *UserRepo) ChangeUsername(ctx context.Context, userID, username string) error {
|
||||
policyResourceOwner := authz.GetCtxData(ctx).OrgID
|
||||
orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(policyResourceOwner)
|
||||
if errors.IsNotFound(err) {
|
||||
orgPolicy, err = repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
orgPolicyView := iam_es_model.OrgIAMViewToModel(orgPolicy)
|
||||
return repo.UserEvents.ChangeUsername(ctx, userID, username, orgPolicyView)
|
||||
}
|
||||
|
||||
func checkIDs(ctx context.Context, obj es_models.ObjectRoot) error {
|
||||
if obj.AggregateID != authz.GetCtxData(ctx).UserID {
|
||||
return errors.ThrowPermissionDenied(nil, "EVENT-kFi9w", "object does not belong to user")
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
es_org "github.com/caos/zitadel/internal/org/repository/eventsourcing"
|
||||
es_proj "github.com/caos/zitadel/internal/project/repository/eventsourcing"
|
||||
es_user "github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
||||
"github.com/caos/zitadel/internal/v2/command"
|
||||
"github.com/caos/zitadel/internal/v2/query"
|
||||
)
|
||||
|
||||
@ -46,7 +47,7 @@ type EsRepository struct {
|
||||
eventstore.IAMRepository
|
||||
}
|
||||
|
||||
func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, authZRepo *authz_repo.EsRepository) (*EsRepository, error) {
|
||||
func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, command *command.CommandSide, authZRepo *authz_repo.EsRepository) (*EsRepository, error) {
|
||||
es, err := es_int.Start(conf.Eventstore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -131,12 +132,14 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, au
|
||||
SystemDefaults: systemDefaults,
|
||||
},
|
||||
eventstore.AuthRequestRepo{
|
||||
Command: command,
|
||||
UserEvents: user,
|
||||
OrgEvents: org,
|
||||
AuthRequests: authReq,
|
||||
View: view,
|
||||
UserSessionViewProvider: view,
|
||||
UserViewProvider: view,
|
||||
UserCommandProvider: command,
|
||||
UserEventProvider: user,
|
||||
OrgViewProvider: view,
|
||||
IDPProviderViewProvider: view,
|
||||
|
@ -3,11 +3,9 @@ package repository
|
||||
import (
|
||||
"context"
|
||||
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TokenRepository interface {
|
||||
CreateToken(ctx context.Context, agentID, clientID, userID string, audience, scopes []string, lifetime time.Duration) (*usr_model.Token, error)
|
||||
IsTokenValid(ctx context.Context, userID, tokenID string) (bool, error)
|
||||
TokenByID(ctx context.Context, userID, tokenID string) (*usr_model.TokenView, error)
|
||||
}
|
||||
|
@ -3,43 +3,18 @@ package repository
|
||||
import (
|
||||
"context"
|
||||
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
|
||||
"github.com/caos/zitadel/internal/user/model"
|
||||
)
|
||||
|
||||
type UserRepository interface {
|
||||
Register(ctx context.Context, user *model.User, member *org_model.OrgMember, resourceOwner string) (*model.User, error)
|
||||
RegisterExternalUser(ctx context.Context, user *model.User, externalIDP *model.ExternalIDP, member *org_model.OrgMember, resourceOwner string) (*model.User, error)
|
||||
|
||||
myUserRepo
|
||||
SkipMFAInit(ctx context.Context, userID string) error
|
||||
|
||||
RequestPasswordReset(ctx context.Context, username string) error
|
||||
SetPassword(ctx context.Context, userID, code, password, userAgentID string) error
|
||||
ChangePassword(ctx context.Context, userID, old, new, userAgentID string) error
|
||||
|
||||
VerifyEmail(ctx context.Context, userID, code string) error
|
||||
ResendEmailVerificationMail(ctx context.Context, userID string) error
|
||||
|
||||
VerifyInitCode(ctx context.Context, userID, code, password string) error
|
||||
ResendInitVerificationMail(ctx context.Context, userID string) error
|
||||
|
||||
AddMFAOTP(ctx context.Context, userID string) (*model.OTP, error)
|
||||
VerifyMFAOTPSetup(ctx context.Context, userID, code, userAgentID string) error
|
||||
|
||||
AddMFAU2F(ctx context.Context, id string) (*model.WebAuthNToken, error)
|
||||
VerifyMFAU2FSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error
|
||||
|
||||
GetPasswordless(ctx context.Context, id string) ([]*model.WebAuthNToken, error)
|
||||
AddPasswordless(ctx context.Context, id string) (*model.WebAuthNToken, error)
|
||||
VerifyPasswordlessSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error
|
||||
|
||||
ChangeUsername(ctx context.Context, userID, username string) error
|
||||
|
||||
SignOut(ctx context.Context, agentID string) error
|
||||
UserSessionUserIDsByAgentID(ctx context.Context, agentID string) ([]string, error)
|
||||
|
||||
UserByID(ctx context.Context, userID string) (*model.UserView, error)
|
||||
UserByLoginName(ctx context.Context, loginName string) (*model.UserView, error)
|
||||
|
||||
MachineKeyByID(ctx context.Context, keyID string) (*model.MachineKeyView, error)
|
||||
}
|
||||
@ -59,8 +34,6 @@ type myUserRepo interface {
|
||||
|
||||
MyUserMFAs(ctx context.Context) ([]*model.MultiFactor, error)
|
||||
|
||||
AddMyMFAU2F(ctx context.Context) (*model.WebAuthNToken, error)
|
||||
|
||||
GetMyPasswordless(ctx context.Context) ([]*model.WebAuthNToken, error)
|
||||
|
||||
MyUserChanges(ctx context.Context, lastSequence uint64, limit uint64, sortAscending bool) (*model.UserChanges, error)
|
||||
|
@ -1,5 +1,9 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
)
|
||||
|
||||
type NextStep interface {
|
||||
Type() NextStepType
|
||||
}
|
||||
@ -164,3 +168,45 @@ const (
|
||||
MFALevelMultiFactor
|
||||
MFALevelMultiFactorCertified
|
||||
)
|
||||
|
||||
func MFATypeToDomain(mfaType MFAType) domain.MFAType {
|
||||
switch mfaType {
|
||||
case MFATypeOTP:
|
||||
return domain.MFATypeOTP
|
||||
case MFATypeU2F:
|
||||
return domain.MFATypeU2F
|
||||
case MFATypeU2FUserVerification:
|
||||
return domain.MFATypeU2FUserVerification
|
||||
default:
|
||||
return domain.MFATypeOTP
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func MFALevelToDomain(mfaLevel MFALevel) domain.MFALevel {
|
||||
switch mfaLevel {
|
||||
case MFALevelNotSetUp:
|
||||
return domain.MFALevelNotSetUp
|
||||
case MFALevelSecondFactor:
|
||||
return domain.MFALevelSecondFactor
|
||||
case MFALevelMultiFactor:
|
||||
return domain.MFALevelMultiFactor
|
||||
case MFALevelMultiFactorCertified:
|
||||
return domain.MFALevelMultiFactorCertified
|
||||
default:
|
||||
return domain.MFALevelNotSetUp
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func UserSessionStateToDomain(state UserSessionState) domain.UserSessionState {
|
||||
switch state {
|
||||
case UserSessionStateActive:
|
||||
return domain.UserSessionStateActive
|
||||
case UserSessionStateTerminated:
|
||||
return domain.UserSessionStateTerminated
|
||||
default:
|
||||
return domain.UserSessionStateActive
|
||||
}
|
||||
|
||||
}
|
||||
|
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"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
"github.com/caos/zitadel/internal/config/types"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
)
|
||||
@ -34,19 +34,19 @@ func (c *AuthRequestCache) Health(ctx context.Context) error {
|
||||
return c.client.PingContext(ctx)
|
||||
}
|
||||
|
||||
func (c *AuthRequestCache) GetAuthRequestByID(_ context.Context, id string) (*model.AuthRequest, error) {
|
||||
func (c *AuthRequestCache) GetAuthRequestByID(_ context.Context, id string) (*domain.AuthRequest, error) {
|
||||
return c.getAuthRequest("id", id)
|
||||
}
|
||||
|
||||
func (c *AuthRequestCache) GetAuthRequestByCode(_ context.Context, code string) (*model.AuthRequest, error) {
|
||||
func (c *AuthRequestCache) GetAuthRequestByCode(_ context.Context, code string) (*domain.AuthRequest, error) {
|
||||
return c.getAuthRequest("code", code)
|
||||
}
|
||||
|
||||
func (c *AuthRequestCache) SaveAuthRequest(_ context.Context, request *model.AuthRequest) error {
|
||||
func (c *AuthRequestCache) SaveAuthRequest(_ context.Context, request *domain.AuthRequest) error {
|
||||
return c.saveAuthRequest(request, "INSERT INTO auth.auth_requests (id, request, request_type) VALUES($1, $2, $3)", request.Request.Type())
|
||||
}
|
||||
|
||||
func (c *AuthRequestCache) UpdateAuthRequest(_ context.Context, request *model.AuthRequest) error {
|
||||
func (c *AuthRequestCache) UpdateAuthRequest(_ context.Context, request *domain.AuthRequest) error {
|
||||
return c.saveAuthRequest(request, "UPDATE auth.auth_requests SET request = $2, code = $3 WHERE id = $1", request.Code)
|
||||
}
|
||||
|
||||
@ -58,9 +58,9 @@ func (c *AuthRequestCache) DeleteAuthRequest(_ context.Context, id string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AuthRequestCache) getAuthRequest(key, value string) (*model.AuthRequest, error) {
|
||||
func (c *AuthRequestCache) getAuthRequest(key, value string) (*domain.AuthRequest, error) {
|
||||
var b []byte
|
||||
var requestType model.AuthRequestType
|
||||
var requestType domain.AuthRequestType
|
||||
query := fmt.Sprintf("SELECT request, request_type FROM auth.auth_requests WHERE %s = $1", key)
|
||||
err := c.client.QueryRow(query, value).Scan(&b, &requestType)
|
||||
if err != nil {
|
||||
@ -69,7 +69,7 @@ func (c *AuthRequestCache) getAuthRequest(key, value string) (*model.AuthRequest
|
||||
}
|
||||
return nil, caos_errs.ThrowInternal(err, "CACHE-as3kj", "Errors.Internal")
|
||||
}
|
||||
request, err := model.NewAuthRequestFromType(requestType)
|
||||
request, err := domain.NewAuthRequestFromType(requestType)
|
||||
if err == nil {
|
||||
err = json.Unmarshal(b, request)
|
||||
}
|
||||
@ -79,7 +79,7 @@ func (c *AuthRequestCache) getAuthRequest(key, value string) (*model.AuthRequest
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func (c *AuthRequestCache) saveAuthRequest(request *model.AuthRequest, query string, param interface{}) error {
|
||||
func (c *AuthRequestCache) saveAuthRequest(request *domain.AuthRequest, query string, param interface{}) error {
|
||||
b, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return caos_errs.ThrowInternal(err, "CACHE-os0GH", "Errors.Internal")
|
||||
|
@ -107,7 +107,7 @@ func (mr *MockAuthRequestCacheMockRecorder) SaveAuthRequest(arg0, arg1 interface
|
||||
}
|
||||
|
||||
// UpdateAuthRequest mocks base method
|
||||
func (m *MockAuthRequestCache) UpdateAuthRequest(arg0 context.Context, arg1 *model.AuthRequest) error {
|
||||
func (m *MockAuthRequestCache) UpdateAuthRequest(arg0 context.Context, arg1 *domain.AuthRequest) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateAuthRequest", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
|
@ -2,16 +2,15 @@ package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
)
|
||||
|
||||
type AuthRequestCache interface {
|
||||
Health(ctx context.Context) error
|
||||
|
||||
GetAuthRequestByID(ctx context.Context, id string) (*model.AuthRequest, error)
|
||||
GetAuthRequestByCode(ctx context.Context, code string) (*model.AuthRequest, error)
|
||||
SaveAuthRequest(ctx context.Context, request *model.AuthRequest) error
|
||||
UpdateAuthRequest(ctx context.Context, request *model.AuthRequest) error
|
||||
GetAuthRequestByID(ctx context.Context, id string) (*domain.AuthRequest, error)
|
||||
GetAuthRequestByCode(ctx context.Context, code string) (*domain.AuthRequest, error)
|
||||
SaveAuthRequest(ctx context.Context, request *domain.AuthRequest) error
|
||||
UpdateAuthRequest(ctx context.Context, request *domain.AuthRequest) error
|
||||
DeleteAuthRequest(ctx context.Context, id string) error
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ func (e *testEvent) Data() interface{} {
|
||||
return e.data()
|
||||
}
|
||||
|
||||
func (e *testEvent) UniqueConstraint() []EventUniqueConstraint {
|
||||
func (e *testEvent) UniqueConstraint() []*EventUniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -188,7 +188,7 @@ func (db *CRDB) handleUniqueConstraints(ctx context.Context, tx *sql.Tx, uniqueC
|
||||
logging.LogWithFields("SQL-M0vsf",
|
||||
"unique_type", uniqueConstraint.UniqueType,
|
||||
"unique_field", uniqueConstraint.UniqueField).WithError(err).Info("delete unique constraint failed")
|
||||
return caos_errs.ThrowInternal(err, "SQL-2M9fs", "unable to remove unique constraint ")
|
||||
return caos_errs.ThrowInternal(err, "SQL-6n88i", "unable to remove unique constraint ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,14 +139,22 @@ func (factory *SearchQueryBuilder) eventTypeFilter() *repository.Filter {
|
||||
if len(factory.eventTypes) == 1 {
|
||||
return repository.NewFilter(repository.FieldEventType, factory.eventTypes[0], repository.OperationEquals)
|
||||
}
|
||||
return repository.NewFilter(repository.FieldEventType, factory.eventTypes, repository.OperationIn)
|
||||
eventTypes := make([]repository.EventType, len(factory.eventTypes))
|
||||
for i, eventType := range factory.eventTypes {
|
||||
eventTypes[i] = repository.EventType(eventType)
|
||||
}
|
||||
return repository.NewFilter(repository.FieldEventType, eventTypes, repository.OperationIn)
|
||||
}
|
||||
|
||||
func (factory *SearchQueryBuilder) aggregateTypeFilter() *repository.Filter {
|
||||
if len(factory.aggregateTypes) == 1 {
|
||||
return repository.NewFilter(repository.FieldAggregateType, factory.aggregateTypes[0], repository.OperationEquals)
|
||||
}
|
||||
return repository.NewFilter(repository.FieldAggregateType, factory.aggregateTypes, repository.OperationIn)
|
||||
aggregateTypes := make([]repository.AggregateType, len(factory.aggregateTypes))
|
||||
for i, aggregateType := range factory.aggregateTypes {
|
||||
aggregateTypes[i] = repository.AggregateType(aggregateType)
|
||||
}
|
||||
return repository.NewFilter(repository.FieldAggregateType, aggregateTypes, repository.OperationIn)
|
||||
}
|
||||
|
||||
func (factory *SearchQueryBuilder) eventSequenceFilter() *repository.Filter {
|
||||
|
@ -2,6 +2,7 @@ package model
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/model"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -60,3 +61,63 @@ func (r *IDPProviderSearchRequest) EnsureLimit(limit uint64) {
|
||||
func (r *IDPProviderSearchRequest) AppendAggregateIDQuery(aggregateID string) {
|
||||
r.Queries = append(r.Queries, &IDPProviderSearchQuery{Key: IDPProviderSearchKeyAggregateID, Method: model.SearchMethodEquals, Value: aggregateID})
|
||||
}
|
||||
|
||||
func IdpProviderViewsToDomain(idpProviders []*IDPProviderView) []*domain.IDPProvider {
|
||||
providers := make([]*domain.IDPProvider, len(idpProviders))
|
||||
for i, provider := range idpProviders {
|
||||
p := &domain.IDPProvider{
|
||||
IDPConfigID: provider.IDPConfigID,
|
||||
Type: idpProviderTypeToDomain(provider.IDPProviderType),
|
||||
Name: provider.Name,
|
||||
IDPConfigType: idpConfigTypeToDomain(provider.IDPConfigType),
|
||||
StylingType: idpStylingTypeToDomain(provider.StylingType),
|
||||
IDPState: idpStateToDomain(provider.IDPState),
|
||||
}
|
||||
providers[i] = p
|
||||
}
|
||||
return providers
|
||||
}
|
||||
|
||||
func idpProviderTypeToDomain(idpType IDPProviderType) domain.IdentityProviderType {
|
||||
switch idpType {
|
||||
case IDPProviderTypeSystem:
|
||||
return domain.IdentityProviderTypeSystem
|
||||
case IDPProviderTypeOrg:
|
||||
return domain.IdentityProviderTypeOrg
|
||||
default:
|
||||
return domain.IdentityProviderTypeSystem
|
||||
}
|
||||
}
|
||||
|
||||
func idpConfigTypeToDomain(idpType IdpConfigType) domain.IDPConfigType {
|
||||
switch idpType {
|
||||
case IDPConfigTypeOIDC:
|
||||
return domain.IDPConfigTypeOIDC
|
||||
case IDPConfigTypeSAML:
|
||||
return domain.IDPConfigTypeSAML
|
||||
default:
|
||||
return domain.IDPConfigTypeOIDC
|
||||
}
|
||||
}
|
||||
|
||||
func idpStylingTypeToDomain(stylingType IDPStylingType) domain.IDPConfigStylingType {
|
||||
switch stylingType {
|
||||
case IDPStylingTypeGoogle:
|
||||
return domain.IDPConfigStylingTypeGoogle
|
||||
default:
|
||||
return domain.IDPConfigStylingTypeUnspecified
|
||||
}
|
||||
}
|
||||
|
||||
func idpStateToDomain(state IDPConfigState) domain.IDPConfigState {
|
||||
switch state {
|
||||
case IDPConfigStateActive:
|
||||
return domain.IDPConfigStateActive
|
||||
case IDPConfigStateInactive:
|
||||
return domain.IDPConfigStateInactive
|
||||
case IDPConfigStateRemoved:
|
||||
return domain.IDPConfigStateRemoved
|
||||
default:
|
||||
return domain.IDPConfigStateActive
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/model"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -64,3 +66,57 @@ func (p *LoginPolicyView) HasMultiFactors() bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *LoginPolicyView) ToLoginPolicyDomain() *domain.LoginPolicy {
|
||||
return &domain.LoginPolicy{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: p.AggregateID,
|
||||
CreationDate: p.CreationDate,
|
||||
ChangeDate: p.ChangeDate,
|
||||
Sequence: p.Sequence,
|
||||
},
|
||||
Default: p.Default,
|
||||
AllowUsernamePassword: p.AllowUsernamePassword,
|
||||
AllowRegister: p.AllowRegister,
|
||||
AllowExternalIDP: p.AllowExternalIDP,
|
||||
ForceMFA: p.ForceMFA,
|
||||
PasswordlessType: passwordLessTypeToDomain(p.PasswordlessType),
|
||||
SecondFactors: secondFactorsToDomain(p.SecondFactors),
|
||||
MultiFactors: multiFactorsToDomain(p.MultiFactors),
|
||||
}
|
||||
}
|
||||
|
||||
func passwordLessTypeToDomain(passwordless PasswordlessType) domain.PasswordlessType {
|
||||
switch passwordless {
|
||||
case PasswordlessTypeNotAllowed:
|
||||
return domain.PasswordlessTypeNotAllowed
|
||||
case PasswordlessTypeAllowed:
|
||||
return domain.PasswordlessTypeAllowed
|
||||
default:
|
||||
return domain.PasswordlessTypeNotAllowed
|
||||
}
|
||||
}
|
||||
|
||||
func secondFactorsToDomain(types []SecondFactorType) []domain.SecondFactorType {
|
||||
secondfactors := make([]domain.SecondFactorType, len(types))
|
||||
for i, secondfactorType := range types {
|
||||
switch secondfactorType {
|
||||
case SecondFactorTypeU2F:
|
||||
secondfactors[i] = domain.SecondFactorTypeU2F
|
||||
case SecondFactorTypeOTP:
|
||||
secondfactors[i] = domain.SecondFactorTypeOTP
|
||||
}
|
||||
}
|
||||
return secondfactors
|
||||
}
|
||||
|
||||
func multiFactorsToDomain(types []MultiFactorType) []domain.MultiFactorType {
|
||||
multifactors := make([]domain.MultiFactorType, len(types))
|
||||
for i, multifactorType := range types {
|
||||
switch multifactorType {
|
||||
case MultiFactorTypeU2FWithPIN:
|
||||
multifactors[i] = domain.MultiFactorTypeU2FWithPIN
|
||||
}
|
||||
}
|
||||
return multifactors
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
es_int "github.com/caos/zitadel/internal/eventstore"
|
||||
iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model"
|
||||
"github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
|
||||
global_model "github.com/caos/zitadel/internal/model"
|
||||
org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing"
|
||||
@ -194,26 +193,6 @@ func (repo *UserRepo) SearchMachineKeys(ctx context.Context, request *usr_model.
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (repo *UserRepo) AddMachineKey(ctx context.Context, key *usr_model.MachineKey) (*usr_model.MachineKey, error) {
|
||||
return repo.UserEvents.AddMachineKey(ctx, key)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) RemoveMachineKey(ctx context.Context, userID, keyID string) error {
|
||||
return repo.UserEvents.RemoveMachineKey(ctx, userID, keyID)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) ChangeUsername(ctx context.Context, userID, userName string) error {
|
||||
orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
orgPolicy, err = repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
orgPolicyView := iam_es_model.OrgIAMViewToModel(orgPolicy)
|
||||
return repo.UserEvents.ChangeUsername(ctx, userID, userName, orgPolicyView)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) EmailByID(ctx context.Context, userID string) (*usr_model.Email, error) {
|
||||
user, err := repo.UserByID(ctx, userID)
|
||||
if err != nil {
|
||||
|
@ -27,6 +27,22 @@ func (repo *UserGrantRepo) UserGrantByID(ctx context.Context, grantID string) (*
|
||||
return model.UserGrantToModel(grant), nil
|
||||
}
|
||||
|
||||
func (repo *UserGrantRepo) UserGrantsByProjectID(ctx context.Context, projectID string) ([]*grant_model.UserGrantView, error) {
|
||||
grants, err := repo.View.UserGrantsByProjectID(projectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return model.UserGrantsToModel(grants), nil
|
||||
}
|
||||
|
||||
func (repo *UserGrantRepo) UserGrantsByUserID(ctx context.Context, userID string) ([]*grant_model.UserGrantView, error) {
|
||||
grants, err := repo.View.UserGrantsByUserID(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return model.UserGrantsToModel(grants), nil
|
||||
}
|
||||
|
||||
func (repo *UserGrantRepo) SearchUserGrants(ctx context.Context, request *grant_model.UserGrantSearchRequest) (*grant_model.UserGrantSearchResponse, error) {
|
||||
request.EnsureLimit(repo.SearchLimit)
|
||||
sequence, sequenceErr := repo.View.GetLatestUserGrantSequence("")
|
||||
|
@ -25,8 +25,6 @@ type UserRepository interface {
|
||||
|
||||
SearchMachineKeys(ctx context.Context, request *model.MachineKeySearchRequest) (*model.MachineKeySearchResponse, error)
|
||||
GetMachineKey(ctx context.Context, userID, keyID string) (*model.MachineKeyView, error)
|
||||
AddMachineKey(ctx context.Context, key *model.MachineKey) (*model.MachineKey, error)
|
||||
RemoveMachineKey(ctx context.Context, userID, keyID string) error
|
||||
|
||||
EmailByID(ctx context.Context, userID string) (*model.Email, error)
|
||||
|
||||
|
@ -8,4 +8,6 @@ import (
|
||||
type UserGrantRepository interface {
|
||||
UserGrantByID(ctx context.Context, grantID string) (*model.UserGrantView, error)
|
||||
SearchUserGrants(ctx context.Context, request *model.UserGrantSearchRequest) (*model.UserGrantSearchResponse, error)
|
||||
UserGrantsByProjectID(ctx context.Context, projectID string) ([]*model.UserGrantView, error)
|
||||
UserGrantsByUserID(ctx context.Context, userID string) ([]*model.UserGrantView, error)
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"github.com/caos/logging"
|
||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/notification/repository/eventsourcing"
|
||||
"github.com/caos/zitadel/internal/v2/command"
|
||||
"github.com/rakyll/statik/fs"
|
||||
|
||||
_ "github.com/caos/zitadel/internal/notification/statik"
|
||||
@ -14,10 +15,10 @@ type Config struct {
|
||||
Repository eventsourcing.Config
|
||||
}
|
||||
|
||||
func Start(ctx context.Context, config Config, systemDefaults sd.SystemDefaults) {
|
||||
func Start(ctx context.Context, config Config, systemDefaults sd.SystemDefaults, command *command.CommandSide) {
|
||||
statikFS, err := fs.NewWithNamespace("notification")
|
||||
logging.Log("CONFI-7usEW").OnError(err).Panic("unable to start listener")
|
||||
|
||||
_, err = eventsourcing.Start(config.Repository, statikFS, systemDefaults)
|
||||
_, err = eventsourcing.Start(config.Repository, statikFS, systemDefaults, command)
|
||||
logging.Log("MAIN-9uBxp").OnError(err).Panic("unable to start app")
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/command"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@ -42,7 +43,7 @@ type EventstoreRepos struct {
|
||||
IAMEvents *iam_es.IAMEventstore
|
||||
}
|
||||
|
||||
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults, i18n *i18n.Translator, dir http.FileSystem) []query.Handler {
|
||||
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es eventstore.Eventstore, command *command.CommandSide, repos EventstoreRepos, systemDefaults sd.SystemDefaults, i18n *i18n.Translator, dir http.FileSystem) []query.Handler {
|
||||
aesCrypto, err := crypto.NewAESCrypto(systemDefaults.UserVerificationKey)
|
||||
if err != nil {
|
||||
logging.Log("HANDL-s90ew").WithError(err).Debug("error create new aes crypto")
|
||||
@ -56,6 +57,7 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
|
||||
),
|
||||
newNotification(
|
||||
handler{view, bulkLimit, configs.cycleDuration("Notification"), errorCount, es},
|
||||
command,
|
||||
repos.UserEvents,
|
||||
systemDefaults,
|
||||
aesCrypto,
|
||||
|
@ -3,6 +3,8 @@ package handler
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/caos/zitadel/internal/user/repository/view/model"
|
||||
"github.com/caos/zitadel/internal/v2/command"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@ -32,6 +34,7 @@ const (
|
||||
|
||||
type Notification struct {
|
||||
handler
|
||||
command *command.CommandSide
|
||||
userEvents *usr_event.UserEventstore
|
||||
systemDefaults sd.SystemDefaults
|
||||
AesCrypto crypto.EncryptionAlgorithm
|
||||
@ -42,6 +45,7 @@ type Notification struct {
|
||||
|
||||
func newNotification(
|
||||
handler handler,
|
||||
command *command.CommandSide,
|
||||
userEvents *usr_event.UserEventstore,
|
||||
defaults sd.SystemDefaults,
|
||||
aesCrypto crypto.EncryptionAlgorithm,
|
||||
@ -50,6 +54,7 @@ func newNotification(
|
||||
) *Notification {
|
||||
h := &Notification{
|
||||
handler: handler,
|
||||
command: command,
|
||||
userEvents: userEvents,
|
||||
systemDefaults: defaults,
|
||||
i18n: translator,
|
||||
@ -135,7 +140,7 @@ func (n *Notification) handleInitUserCode(event *models.Event) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
user, err := n.view.NotifyUserByID(event.AggregateID)
|
||||
user, err := n.getUserByID(event.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -143,7 +148,7 @@ func (n *Notification) handleInitUserCode(event *models.Event) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return n.userEvents.InitCodeSent(getSetNotifyContextData(event.ResourceOwner), event.AggregateID)
|
||||
return n.command.HumanInitCodeSent(getSetNotifyContextData(event.ResourceOwner), event.ResourceOwner, event.AggregateID)
|
||||
}
|
||||
|
||||
func (n *Notification) handlePasswordCode(event *models.Event) (err error) {
|
||||
@ -163,7 +168,7 @@ func (n *Notification) handlePasswordCode(event *models.Event) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
user, err := n.view.NotifyUserByID(event.AggregateID)
|
||||
user, err := n.getUserByID(event.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -171,7 +176,7 @@ func (n *Notification) handlePasswordCode(event *models.Event) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return n.userEvents.PasswordCodeSent(getSetNotifyContextData(event.ResourceOwner), event.AggregateID)
|
||||
return n.command.PasswordCodeSent(getSetNotifyContextData(event.ResourceOwner), event.ResourceOwner, event.AggregateID)
|
||||
}
|
||||
|
||||
func (n *Notification) handleEmailVerificationCode(event *models.Event) (err error) {
|
||||
@ -191,7 +196,7 @@ func (n *Notification) handleEmailVerificationCode(event *models.Event) (err err
|
||||
return err
|
||||
}
|
||||
|
||||
user, err := n.view.NotifyUserByID(event.AggregateID)
|
||||
user, err := n.getUserByID(event.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -199,7 +204,7 @@ func (n *Notification) handleEmailVerificationCode(event *models.Event) (err err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return n.userEvents.EmailVerificationCodeSent(getSetNotifyContextData(event.ResourceOwner), event.AggregateID)
|
||||
return n.command.HumanEmailVerificationCodeSent(getSetNotifyContextData(event.ResourceOwner), event.ResourceOwner, event.AggregateID)
|
||||
}
|
||||
|
||||
func (n *Notification) handlePhoneVerificationCode(event *models.Event) (err error) {
|
||||
@ -213,7 +218,7 @@ func (n *Notification) handlePhoneVerificationCode(event *models.Event) (err err
|
||||
if err != nil || alreadyHandled {
|
||||
return nil
|
||||
}
|
||||
user, err := n.view.NotifyUserByID(event.AggregateID)
|
||||
user, err := n.getUserByID(event.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -221,7 +226,7 @@ func (n *Notification) handlePhoneVerificationCode(event *models.Event) (err err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return n.userEvents.PhoneVerificationCodeSent(getSetNotifyContextData(event.ResourceOwner), event.AggregateID)
|
||||
return n.command.HumanPhoneVerificationCodeSent(getSetNotifyContextData(event.ResourceOwner), event.ResourceOwner, event.AggregateID)
|
||||
}
|
||||
|
||||
func (n *Notification) handleDomainClaimed(event *models.Event) (err error) {
|
||||
@ -234,7 +239,7 @@ func (n *Notification) handleDomainClaimed(event *models.Event) (err error) {
|
||||
logging.Log("HANDLE-Gghq2").WithError(err).Error("could not unmarshal event data")
|
||||
return caos_errs.ThrowInternal(err, "HANDLE-7hgj3", "could not unmarshal event")
|
||||
}
|
||||
user, err := n.view.NotifyUserByID(event.AggregateID)
|
||||
user, err := n.getUserByID(event.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -242,7 +247,7 @@ func (n *Notification) handleDomainClaimed(event *models.Event) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return n.userEvents.DomainClaimedSent(getSetNotifyContextData(event.ResourceOwner), event.AggregateID)
|
||||
return n.command.UserDomainClaimedSent(getSetNotifyContextData(event.ResourceOwner), event.ResourceOwner, event.AggregateID)
|
||||
}
|
||||
|
||||
func (n *Notification) checkIfCodeAlreadyHandledOrExpired(event *models.Event, expiry time.Duration, eventTypes ...models.EventType) (bool, error) {
|
||||
@ -306,3 +311,27 @@ func (n *Notification) getLabelPolicy(ctx context.Context) (*iam_model.LabelPoli
|
||||
}
|
||||
return iam_es_model.LabelPolicyViewToModel(policy), err
|
||||
}
|
||||
|
||||
func (n *Notification) getUserByID(userID string) (*model.NotifyUser, error) {
|
||||
user, usrErr := n.view.NotifyUserByID(userID)
|
||||
if usrErr != nil && !caos_errs.IsNotFound(usrErr) {
|
||||
return nil, usrErr
|
||||
}
|
||||
if user == nil {
|
||||
user = &model.NotifyUser{}
|
||||
}
|
||||
events, err := n.getUserEvents(userID, user.Sequence)
|
||||
if err != nil {
|
||||
return user, usrErr
|
||||
}
|
||||
userCopy := *user
|
||||
for _, event := range events {
|
||||
if err := userCopy.AppendEvent(event); err != nil {
|
||||
return user, nil
|
||||
}
|
||||
}
|
||||
if userCopy.State == int32(model.UserStateDeleted) {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "EVENT-3n8fs", "Errors.User.NotFound")
|
||||
}
|
||||
return &userCopy, nil
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package eventsourcing
|
||||
|
||||
import (
|
||||
es_iam "github.com/caos/zitadel/internal/iam/repository/eventsourcing"
|
||||
"github.com/caos/zitadel/internal/v2/command"
|
||||
"net/http"
|
||||
|
||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
@ -29,7 +30,7 @@ type EsRepository struct {
|
||||
spooler *es_spol.Spooler
|
||||
}
|
||||
|
||||
func Start(conf Config, dir http.FileSystem, systemDefaults sd.SystemDefaults) (*EsRepository, error) {
|
||||
func Start(conf Config, dir http.FileSystem, systemDefaults sd.SystemDefaults, command *command.CommandSide) (*EsRepository, error) {
|
||||
es, err := es_int.Start(conf.Eventstore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -65,7 +66,7 @@ func Start(conf Config, dir http.FileSystem, systemDefaults sd.SystemDefaults) (
|
||||
return nil, err
|
||||
}
|
||||
eventstoreRepos := handler.EventstoreRepos{UserEvents: user, OrgEvents: org, IAMEvents: iam}
|
||||
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, eventstoreRepos, systemDefaults, translator, dir)
|
||||
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, command, eventstoreRepos, systemDefaults, translator, dir)
|
||||
|
||||
return &EsRepository{
|
||||
spool,
|
||||
|
@ -2,6 +2,7 @@ package spooler
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/caos/zitadel/internal/v2/command"
|
||||
"net/http"
|
||||
|
||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
@ -19,12 +20,12 @@ type SpoolerConfig struct {
|
||||
Handlers handler.Configs
|
||||
}
|
||||
|
||||
func StartSpooler(c SpoolerConfig, es eventstore.Eventstore, view *view.View, sql *sql.DB, eventstoreRepos handler.EventstoreRepos, systemDefaults sd.SystemDefaults, i18n *i18n.Translator, dir http.FileSystem) *spooler.Spooler {
|
||||
func StartSpooler(c SpoolerConfig, es eventstore.Eventstore, view *view.View, sql *sql.DB, command *command.CommandSide, eventstoreRepos handler.EventstoreRepos, systemDefaults sd.SystemDefaults, i18n *i18n.Translator, dir http.FileSystem) *spooler.Spooler {
|
||||
spoolerConfig := spooler.Config{
|
||||
Eventstore: es,
|
||||
Locker: &locker{dbClient: sql},
|
||||
ConcurrentWorkers: c.ConcurrentWorkers,
|
||||
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, eventstoreRepos, systemDefaults, i18n, dir),
|
||||
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, command, eventstoreRepos, systemDefaults, i18n, dir),
|
||||
}
|
||||
spool := spoolerConfig.New()
|
||||
spool.Start()
|
||||
|
@ -38,6 +38,9 @@ Errors:
|
||||
Address:
|
||||
NotFound: Addresse nicht gefunden
|
||||
NotChanged: Addresse wurde nicht geändert
|
||||
Machine:
|
||||
Key:
|
||||
NotFound: Maschinen Key nicht gefunden
|
||||
NotHuman: Der Benutzer muss eine Person sein
|
||||
NotMachine: Der Benutzer muss technisch sein
|
||||
NotAllowedToLink: Der Benutzer darf nicht mit einem externen Login Provider verlinkt werden
|
||||
@ -53,6 +56,7 @@ Errors:
|
||||
NotFound: Password nicht gefunden
|
||||
Empty: Passwort ist leer
|
||||
Invalid: Passwort ungültig
|
||||
NotSet: Benutzer hat kein Passwort gesetzt
|
||||
PasswordComplexityPolicy:
|
||||
NotFound: Passwort Policy konnte nicht gefunden werden
|
||||
MinLength: Passwort ist zu kurz
|
||||
|
@ -38,6 +38,9 @@ Errors:
|
||||
Address:
|
||||
NotFound: Address not found
|
||||
NotChanged: Address not changed
|
||||
Machine:
|
||||
Key:
|
||||
NotFound: Machine key not found
|
||||
NotHuman: The User must be personal
|
||||
NotMachine: The User must be technical
|
||||
NotAllowedToLink: User is not allowed to link with external login provider
|
||||
@ -53,6 +56,7 @@ Errors:
|
||||
NotFound: Passoword not found
|
||||
Empty: Password is empty
|
||||
Invalid: Passwort is invalid
|
||||
NotSet: User has not set a password
|
||||
PasswordComplexityPolicy:
|
||||
NotFound: Password policy not found
|
||||
MinLength: Password is to short
|
||||
|
@ -1,17 +1,17 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
)
|
||||
|
||||
const (
|
||||
queryAuthRequestID = "authRequestID"
|
||||
)
|
||||
|
||||
func (l *Login) getAuthRequest(r *http.Request) (*model.AuthRequest, error) {
|
||||
func (l *Login) getAuthRequest(r *http.Request) (*domain.AuthRequest, error) {
|
||||
authRequestID := r.FormValue(queryAuthRequestID)
|
||||
if authRequestID == "" {
|
||||
return nil, nil
|
||||
@ -20,7 +20,7 @@ func (l *Login) getAuthRequest(r *http.Request) (*model.AuthRequest, error) {
|
||||
return l.authRepo.AuthRequestByID(r.Context(), authRequestID, userAgentID)
|
||||
}
|
||||
|
||||
func (l *Login) getAuthRequestAndParseData(r *http.Request, data interface{}) (*model.AuthRequest, error) {
|
||||
func (l *Login) getAuthRequestAndParseData(r *http.Request, data interface{}) (*domain.AuthRequest, error) {
|
||||
authReq, err := l.getAuthRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1,12 +1,11 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
)
|
||||
|
||||
func (l *Login) redirectToCallback(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
|
||||
func (l *Login) redirectToCallback(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
|
||||
callback := l.oidcAuthCallbackURL + authReq.ID
|
||||
http.Redirect(w, r, callback, http.StatusFound)
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -26,7 +26,7 @@ func (l *Login) handleChangePassword(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||
err = l.authRepo.ChangePassword(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, data.OldPassword, data.NewPassword, userAgentID)
|
||||
err = l.command.ChangePassword(setContext(r.Context(), authReq.UserOrgID), authReq.UserOrgID, authReq.UserID, data.OldPassword, data.NewPassword, userAgentID)
|
||||
if err != nil {
|
||||
l.renderChangePassword(w, r, authReq, err)
|
||||
return
|
||||
@ -34,7 +34,7 @@ func (l *Login) handleChangePassword(w http.ResponseWriter, r *http.Request) {
|
||||
l.renderChangePasswordDone(w, r, authReq)
|
||||
}
|
||||
|
||||
func (l *Login) renderChangePassword(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
|
||||
func (l *Login) renderChangePassword(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
|
||||
var errType, errMessage string
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
@ -63,7 +63,7 @@ func (l *Login) renderChangePassword(w http.ResponseWriter, r *http.Request, aut
|
||||
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplChangePassword], data, nil)
|
||||
}
|
||||
|
||||
func (l *Login) renderChangePasswordDone(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
|
||||
func (l *Login) renderChangePasswordDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
|
||||
var errType, errMessage string
|
||||
data := l.getUserData(r, authReq, "Password Change Done", errType, errMessage)
|
||||
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplChangePasswordDone], data, nil)
|
||||
|
@ -4,14 +4,11 @@ import (
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
"github.com/caos/oidc/pkg/rp"
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
caos_errors "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@ -41,7 +38,7 @@ type externalNotFoundOptionData struct {
|
||||
baseData
|
||||
}
|
||||
|
||||
func (l *Login) handleExternalLoginStep(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, selectedIDPConfigID string) {
|
||||
func (l *Login) handleExternalLoginStep(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, selectedIDPConfigID string) {
|
||||
for _, idp := range authReq.AllowedExternalIDPs {
|
||||
if idp.IDPConfigID == selectedIDPConfigID {
|
||||
l.handleIDP(w, r, authReq, selectedIDPConfigID)
|
||||
@ -65,7 +62,7 @@ func (l *Login) handleExternalLogin(w http.ResponseWriter, r *http.Request) {
|
||||
l.handleIDP(w, r, authReq, data.IDPConfigID)
|
||||
}
|
||||
|
||||
func (l *Login) handleIDP(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, selectedIDPConfigID string) {
|
||||
func (l *Login) handleIDP(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, selectedIDPConfigID string) {
|
||||
idpConfig, err := l.getIDPConfigByID(r, selectedIDPConfigID)
|
||||
if err != nil {
|
||||
l.renderError(w, r, authReq, err)
|
||||
@ -84,7 +81,7 @@ func (l *Login) handleIDP(w http.ResponseWriter, r *http.Request, authReq *model
|
||||
l.handleOIDCAuthorize(w, r, authReq, idpConfig, EndpointExternalLoginCallback)
|
||||
}
|
||||
|
||||
func (l *Login) handleOIDCAuthorize(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, idpConfig *iam_model.IDPConfigView, callbackEndpoint string) {
|
||||
func (l *Login) handleOIDCAuthorize(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, idpConfig *iam_model.IDPConfigView, callbackEndpoint string) {
|
||||
provider := l.getRPConfig(w, r, authReq, idpConfig, callbackEndpoint)
|
||||
http.Redirect(w, r, rp.AuthURL(authReq.ID, provider, rp.WithPrompt(oidc.PromptSelectAccount)), http.StatusFound)
|
||||
}
|
||||
@ -116,7 +113,7 @@ func (l *Login) handleExternalLoginCallback(w http.ResponseWriter, r *http.Reque
|
||||
l.handleExternalUserAuthenticated(w, r, authReq, idpConfig, userAgentID, tokens)
|
||||
}
|
||||
|
||||
func (l *Login) getRPConfig(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, idpConfig *iam_model.IDPConfigView, callbackEndpoint string) rp.RelayingParty {
|
||||
func (l *Login) getRPConfig(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, idpConfig *iam_model.IDPConfigView, callbackEndpoint string) rp.RelayingParty {
|
||||
oidcClientSecret, err := crypto.DecryptString(idpConfig.OIDCClientSecret, l.IDPConfigAesCrypto)
|
||||
if err != nil {
|
||||
l.renderError(w, r, authReq, err)
|
||||
@ -130,9 +127,9 @@ func (l *Login) getRPConfig(w http.ResponseWriter, r *http.Request, authReq *mod
|
||||
return provider
|
||||
}
|
||||
|
||||
func (l *Login) handleExternalUserAuthenticated(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, idpConfig *iam_model.IDPConfigView, userAgentID string, tokens *oidc.Tokens) {
|
||||
func (l *Login) handleExternalUserAuthenticated(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, idpConfig *iam_model.IDPConfigView, userAgentID string, tokens *oidc.Tokens) {
|
||||
externalUser := l.mapTokenToLoginUser(tokens, idpConfig)
|
||||
err := l.authRepo.CheckExternalUserLogin(r.Context(), authReq.ID, userAgentID, externalUser, model.BrowserInfoFromRequest(r))
|
||||
err := l.authRepo.CheckExternalUserLogin(r.Context(), authReq.ID, userAgentID, externalUser, domain.BrowserInfoFromRequest(r))
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
err = nil
|
||||
@ -143,7 +140,7 @@ func (l *Login) handleExternalUserAuthenticated(w http.ResponseWriter, r *http.R
|
||||
l.renderNextStep(w, r, authReq)
|
||||
}
|
||||
|
||||
func (l *Login) renderExternalNotFoundOption(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
|
||||
func (l *Login) renderExternalNotFoundOption(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
|
||||
var errType, errMessage string
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
@ -176,7 +173,7 @@ func (l *Login) handleExternalNotFoundOptionCheck(w http.ResponseWriter, r *http
|
||||
l.handleAutoRegister(w, r, authReq)
|
||||
}
|
||||
|
||||
func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
|
||||
func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
|
||||
iam, err := l.authRepo.GetIAM(r.Context())
|
||||
if err != nil {
|
||||
l.renderExternalNotFoundOption(w, r, authReq, err)
|
||||
@ -184,13 +181,10 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR
|
||||
}
|
||||
|
||||
resourceOwner := iam.GlobalOrgID
|
||||
member := &org_model.OrgMember{
|
||||
ObjectRoot: models.ObjectRoot{AggregateID: iam.GlobalOrgID},
|
||||
Roles: []string{orgProjectCreatorRole},
|
||||
}
|
||||
memberRoles := []string{domain.RoleOrgProjectCreator}
|
||||
|
||||
if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != iam.GlobalOrgID {
|
||||
member = nil
|
||||
memberRoles = nil
|
||||
resourceOwner = authReq.RequestedOrgID
|
||||
}
|
||||
|
||||
@ -208,7 +202,7 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR
|
||||
|
||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||
user, externalIDP := l.mapExternalUserToLoginUser(orgIamPolicy, authReq.LinkingUsers[len(authReq.LinkingUsers)-1], idpConfig)
|
||||
err = l.authRepo.AutoRegisterExternalUser(setContext(r.Context(), resourceOwner), user, externalIDP, member, authReq.ID, userAgentID, resourceOwner, model.BrowserInfoFromRequest(r))
|
||||
err = l.authRepo.AutoRegisterExternalUser(setContext(r.Context(), resourceOwner), user, externalIDP, memberRoles, authReq.ID, userAgentID, resourceOwner, domain.BrowserInfoFromRequest(r))
|
||||
if err != nil {
|
||||
l.renderExternalNotFoundOption(w, r, authReq, err)
|
||||
return
|
||||
@ -216,7 +210,7 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR
|
||||
l.renderNextStep(w, r, authReq)
|
||||
}
|
||||
|
||||
func (l *Login) mapTokenToLoginUser(tokens *oidc.Tokens, idpConfig *iam_model.IDPConfigView) *model.ExternalUser {
|
||||
func (l *Login) mapTokenToLoginUser(tokens *oidc.Tokens, idpConfig *iam_model.IDPConfigView) *domain.ExternalUser {
|
||||
displayName := tokens.IDTokenClaims.GetPreferredUsername()
|
||||
if displayName == "" && tokens.IDTokenClaims.GetEmail() != "" {
|
||||
displayName = tokens.IDTokenClaims.GetEmail()
|
||||
@ -228,7 +222,7 @@ func (l *Login) mapTokenToLoginUser(tokens *oidc.Tokens, idpConfig *iam_model.ID
|
||||
}
|
||||
}
|
||||
|
||||
externalUser := &model.ExternalUser{
|
||||
externalUser := &domain.ExternalUser{
|
||||
IDPConfigID: idpConfig.IDPConfigID,
|
||||
ExternalUserID: tokens.IDTokenClaims.GetSubject(),
|
||||
PreferredUsername: tokens.IDTokenClaims.GetPreferredUsername(),
|
||||
@ -246,7 +240,7 @@ func (l *Login) mapTokenToLoginUser(tokens *oidc.Tokens, idpConfig *iam_model.ID
|
||||
}
|
||||
return externalUser
|
||||
}
|
||||
func (l *Login) mapExternalUserToLoginUser(orgIamPolicy *iam_model.OrgIAMPolicyView, linkingUser *model.ExternalUser, idpConfig *iam_model.IDPConfigView) (*usr_model.User, *usr_model.ExternalIDP) {
|
||||
func (l *Login) mapExternalUserToLoginUser(orgIamPolicy *iam_model.OrgIAMPolicyView, linkingUser *domain.ExternalUser, idpConfig *iam_model.IDPConfigView) (*domain.Human, *domain.ExternalIDP) {
|
||||
username := linkingUser.PreferredUsername
|
||||
switch idpConfig.OIDCUsernameMapping {
|
||||
case iam_model.OIDCMappingFieldEmail:
|
||||
@ -262,23 +256,21 @@ func (l *Login) mapExternalUserToLoginUser(orgIamPolicy *iam_model.OrgIAMPolicyV
|
||||
}
|
||||
}
|
||||
|
||||
user := &usr_model.User{
|
||||
UserName: username,
|
||||
Human: &usr_model.Human{
|
||||
Profile: &usr_model.Profile{
|
||||
FirstName: linkingUser.FirstName,
|
||||
LastName: linkingUser.LastName,
|
||||
PreferredLanguage: linkingUser.PreferredLanguage,
|
||||
NickName: linkingUser.NickName,
|
||||
},
|
||||
Email: &usr_model.Email{
|
||||
EmailAddress: linkingUser.Email,
|
||||
IsEmailVerified: linkingUser.IsEmailVerified,
|
||||
},
|
||||
human := &domain.Human{
|
||||
Username: username,
|
||||
Profile: &domain.Profile{
|
||||
FirstName: linkingUser.FirstName,
|
||||
LastName: linkingUser.LastName,
|
||||
PreferredLanguage: linkingUser.PreferredLanguage,
|
||||
NickName: linkingUser.NickName,
|
||||
},
|
||||
Email: &domain.Email{
|
||||
EmailAddress: linkingUser.Email,
|
||||
IsEmailVerified: linkingUser.IsEmailVerified,
|
||||
},
|
||||
}
|
||||
if linkingUser.Phone != "" {
|
||||
user.Phone = &usr_model.Phone{
|
||||
human.Phone = &domain.Phone{
|
||||
PhoneNumber: linkingUser.Phone,
|
||||
IsPhoneVerified: linkingUser.IsPhoneVerified,
|
||||
}
|
||||
@ -292,10 +284,10 @@ func (l *Login) mapExternalUserToLoginUser(orgIamPolicy *iam_model.OrgIAMPolicyV
|
||||
}
|
||||
}
|
||||
|
||||
externalIDP := &usr_model.ExternalIDP{
|
||||
IDPConfigID: idpConfig.IDPConfigID,
|
||||
UserID: linkingUser.ExternalUserID,
|
||||
DisplayName: displayName,
|
||||
externalIDP := &domain.ExternalIDP{
|
||||
IDPConfigID: idpConfig.IDPConfigID,
|
||||
ExternalUserID: linkingUser.ExternalUserID,
|
||||
DisplayName: displayName,
|
||||
}
|
||||
return user, externalIDP
|
||||
return human, externalIDP
|
||||
}
|
||||
|
@ -1,20 +1,15 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
"github.com/caos/oidc/pkg/rp"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
caos_errors "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||
)
|
||||
|
||||
func (l *Login) handleExternalRegister(w http.ResponseWriter, r *http.Request) {
|
||||
@ -73,20 +68,17 @@ func (l *Login) handleExternalRegisterCallback(w http.ResponseWriter, r *http.Re
|
||||
l.handleExternalUserRegister(w, r, authReq, idpConfig, userAgentID, tokens)
|
||||
}
|
||||
|
||||
func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, idpConfig *iam_model.IDPConfigView, userAgentID string, tokens *oidc.Tokens) {
|
||||
func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, idpConfig *iam_model.IDPConfigView, userAgentID string, tokens *oidc.Tokens) {
|
||||
iam, err := l.authRepo.GetIAM(r.Context())
|
||||
if err != nil {
|
||||
l.renderRegisterOption(w, r, authReq, err)
|
||||
return
|
||||
}
|
||||
resourceOwner := iam.GlobalOrgID
|
||||
member := &org_model.OrgMember{
|
||||
ObjectRoot: models.ObjectRoot{AggregateID: iam.GlobalOrgID},
|
||||
Roles: []string{orgProjectCreatorRole},
|
||||
}
|
||||
memberRoles := []string{domain.RoleOrgProjectCreator}
|
||||
|
||||
if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != iam.GlobalOrgID {
|
||||
member = nil
|
||||
memberRoles = nil
|
||||
resourceOwner = authReq.RequestedOrgID
|
||||
}
|
||||
orgIamPolicy, err := l.getOrgIamPolicy(r, resourceOwner)
|
||||
@ -94,8 +86,8 @@ func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Reques
|
||||
l.renderRegisterOption(w, r, authReq, err)
|
||||
return
|
||||
}
|
||||
user, externalIDP := l.mapTokenToLoginUserAndExternalIDP(orgIamPolicy, tokens, idpConfig)
|
||||
_, err = l.authRepo.RegisterExternalUser(setContext(r.Context(), resourceOwner), user, externalIDP, member, resourceOwner)
|
||||
user, externalIDP := l.mapTokenToLoginHumanAndExternalIDP(orgIamPolicy, tokens, idpConfig)
|
||||
_, err = l.command.RegisterHuman(setContext(r.Context(), resourceOwner), resourceOwner, user, externalIDP, memberRoles)
|
||||
if err != nil {
|
||||
l.renderRegisterOption(w, r, authReq, err)
|
||||
return
|
||||
@ -103,7 +95,7 @@ func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Reques
|
||||
l.renderNextStep(w, r, authReq)
|
||||
}
|
||||
|
||||
func (l *Login) mapTokenToLoginUserAndExternalIDP(orgIamPolicy *iam_model.OrgIAMPolicyView, tokens *oidc.Tokens, idpConfig *iam_model.IDPConfigView) (*usr_model.User, *usr_model.ExternalIDP) {
|
||||
func (l *Login) mapTokenToLoginHumanAndExternalIDP(orgIamPolicy *iam_model.OrgIAMPolicyView, tokens *oidc.Tokens, idpConfig *iam_model.IDPConfigView) (*domain.Human, *domain.ExternalIDP) {
|
||||
username := tokens.IDTokenClaims.GetPreferredUsername()
|
||||
switch idpConfig.OIDCUsernameMapping {
|
||||
case iam_model.OIDCMappingFieldEmail:
|
||||
@ -119,23 +111,22 @@ func (l *Login) mapTokenToLoginUserAndExternalIDP(orgIamPolicy *iam_model.OrgIAM
|
||||
}
|
||||
}
|
||||
|
||||
user := &usr_model.User{
|
||||
UserName: username,
|
||||
Human: &usr_model.Human{
|
||||
Profile: &usr_model.Profile{
|
||||
FirstName: tokens.IDTokenClaims.GetGivenName(),
|
||||
LastName: tokens.IDTokenClaims.GetFamilyName(),
|
||||
PreferredLanguage: language.Tag(tokens.IDTokenClaims.GetLocale()),
|
||||
NickName: tokens.IDTokenClaims.GetNickname(),
|
||||
},
|
||||
Email: &usr_model.Email{
|
||||
EmailAddress: tokens.IDTokenClaims.GetEmail(),
|
||||
IsEmailVerified: tokens.IDTokenClaims.IsEmailVerified(),
|
||||
},
|
||||
human := &domain.Human{
|
||||
Username: username,
|
||||
Profile: &domain.Profile{
|
||||
FirstName: tokens.IDTokenClaims.GetGivenName(),
|
||||
LastName: tokens.IDTokenClaims.GetFamilyName(),
|
||||
PreferredLanguage: tokens.IDTokenClaims.GetLocale(),
|
||||
NickName: tokens.IDTokenClaims.GetNickname(),
|
||||
},
|
||||
Email: &domain.Email{
|
||||
EmailAddress: tokens.IDTokenClaims.GetEmail(),
|
||||
IsEmailVerified: tokens.IDTokenClaims.IsEmailVerified(),
|
||||
},
|
||||
}
|
||||
|
||||
if tokens.IDTokenClaims.GetPhoneNumber() != "" {
|
||||
user.Phone = &usr_model.Phone{
|
||||
human.Phone = &domain.Phone{
|
||||
PhoneNumber: tokens.IDTokenClaims.GetPhoneNumber(),
|
||||
IsPhoneVerified: tokens.IDTokenClaims.IsPhoneNumberVerified(),
|
||||
}
|
||||
@ -149,10 +140,10 @@ func (l *Login) mapTokenToLoginUserAndExternalIDP(orgIamPolicy *iam_model.OrgIAM
|
||||
}
|
||||
}
|
||||
|
||||
externalIDP := &usr_model.ExternalIDP{
|
||||
IDPConfigID: idpConfig.IDPConfigID,
|
||||
UserID: tokens.IDTokenClaims.GetSubject(),
|
||||
DisplayName: displayName,
|
||||
externalIDP := &domain.ExternalIDP{
|
||||
IDPConfigID: idpConfig.IDPConfigID,
|
||||
ExternalUserID: tokens.IDTokenClaims.GetSubject(),
|
||||
DisplayName: displayName,
|
||||
}
|
||||
return user, externalIDP
|
||||
return human, externalIDP
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
)
|
||||
|
||||
@ -58,18 +58,18 @@ func (l *Login) handleInitPasswordCheck(w http.ResponseWriter, r *http.Request)
|
||||
l.checkPWCode(w, r, authReq, data, nil)
|
||||
}
|
||||
|
||||
func (l *Login) checkPWCode(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *initPasswordFormData, err error) {
|
||||
func (l *Login) checkPWCode(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, data *initPasswordFormData, err error) {
|
||||
if data.Password != data.PasswordConfirm {
|
||||
err := errors.ThrowInvalidArgument(nil, "VIEW-KaGue", "Errors.User.Password.ConfirmationWrong")
|
||||
l.renderInitPassword(w, r, authReq, data.UserID, data.Code, err)
|
||||
return
|
||||
}
|
||||
userOrg := login
|
||||
userOrg := ""
|
||||
if authReq != nil {
|
||||
userOrg = authReq.UserOrgID
|
||||
}
|
||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||
err = l.authRepo.SetPassword(setContext(r.Context(), userOrg), data.UserID, data.Code, data.Password, userAgentID)
|
||||
err = l.command.SetPassword(setContext(r.Context(), userOrg), userOrg, data.UserID, data.Code, data.Password, userAgentID)
|
||||
if err != nil {
|
||||
l.renderInitPassword(w, r, authReq, data.UserID, "", err)
|
||||
return
|
||||
@ -77,16 +77,25 @@ func (l *Login) checkPWCode(w http.ResponseWriter, r *http.Request, authReq *mod
|
||||
l.renderInitPasswordDone(w, r, authReq)
|
||||
}
|
||||
|
||||
func (l *Login) resendPasswordSet(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
|
||||
func (l *Login) resendPasswordSet(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
|
||||
if authReq == nil {
|
||||
l.renderError(w, r, nil, errors.ThrowInternal(nil, "LOGIN-8sn7s", "Errors.AuthRequest.NotFound"))
|
||||
return
|
||||
}
|
||||
userOrg := login
|
||||
if authReq != nil {
|
||||
userOrg = authReq.UserOrgID
|
||||
}
|
||||
err := l.authRepo.RequestPasswordReset(setContext(r.Context(), userOrg), authReq.LoginName)
|
||||
user, err := l.authRepo.UserByLoginName(setContext(r.Context(), userOrg), authReq.LoginName)
|
||||
if err != nil {
|
||||
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
||||
return
|
||||
}
|
||||
err = l.command.RequestSetPassword(setContext(r.Context(), userOrg), user.ID, user.ResourceOwner, domain.NotificationTypeEmail)
|
||||
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
||||
}
|
||||
|
||||
func (l *Login) renderInitPassword(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, userID, code string, err error) {
|
||||
func (l *Login) renderInitPassword(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID, code string, err error) {
|
||||
var errType, errMessage string
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
@ -121,7 +130,7 @@ func (l *Login) renderInitPassword(w http.ResponseWriter, r *http.Request, authR
|
||||
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplInitPassword], data, nil)
|
||||
}
|
||||
|
||||
func (l *Login) renderInitPasswordDone(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
|
||||
func (l *Login) renderInitPasswordDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
|
||||
data := l.getUserData(r, authReq, "Password Init Done", "", "")
|
||||
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplInitPasswordDone], data, nil)
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
)
|
||||
|
||||
@ -62,17 +62,17 @@ func (l *Login) handleInitUserCheck(w http.ResponseWriter, r *http.Request) {
|
||||
l.checkUserInitCode(w, r, authReq, data, nil)
|
||||
}
|
||||
|
||||
func (l *Login) checkUserInitCode(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *initUserFormData, err error) {
|
||||
func (l *Login) checkUserInitCode(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, data *initUserFormData, err error) {
|
||||
if data.Password != data.PasswordConfirm {
|
||||
err := caos_errs.ThrowInvalidArgument(nil, "VIEW-fsdfd", "Errors.User.Password.ConfirmationWrong")
|
||||
l.renderInitUser(w, r, authReq, data.UserID, data.Code, data.PasswordSet, err)
|
||||
return
|
||||
}
|
||||
userOrgID := login
|
||||
userOrgID := ""
|
||||
if authReq != nil {
|
||||
userOrgID = authReq.UserOrgID
|
||||
}
|
||||
err = l.authRepo.VerifyInitCode(setContext(r.Context(), userOrgID), data.UserID, data.Code, data.Password)
|
||||
err = l.command.HumanVerifyInitCode(setContext(r.Context(), userOrgID), data.UserID, userOrgID, data.Code, data.Password)
|
||||
if err != nil {
|
||||
l.renderInitUser(w, r, authReq, data.UserID, "", data.PasswordSet, err)
|
||||
return
|
||||
@ -80,16 +80,16 @@ func (l *Login) checkUserInitCode(w http.ResponseWriter, r *http.Request, authRe
|
||||
l.renderInitUserDone(w, r, authReq)
|
||||
}
|
||||
|
||||
func (l *Login) resendUserInit(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, userID string, showPassword bool) {
|
||||
userOrgID := login
|
||||
func (l *Login) resendUserInit(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID string, showPassword bool) {
|
||||
userOrgID := ""
|
||||
if authReq != nil {
|
||||
userOrgID = authReq.UserOrgID
|
||||
}
|
||||
err := l.authRepo.ResendInitVerificationMail(setContext(r.Context(), userOrgID), userID)
|
||||
err := l.command.ResendInitialMail(setContext(r.Context(), userOrgID), userID, "", userOrgID)
|
||||
l.renderInitUser(w, r, authReq, userID, "", showPassword, err)
|
||||
}
|
||||
|
||||
func (l *Login) renderInitUser(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, userID, code string, passwordSet bool, err error) {
|
||||
func (l *Login) renderInitUser(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID, code string, passwordSet bool, err error) {
|
||||
var errType, errMessage string
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
@ -124,7 +124,7 @@ func (l *Login) renderInitUser(w http.ResponseWriter, r *http.Request, authReq *
|
||||
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplInitUser], data, nil)
|
||||
}
|
||||
|
||||
func (l *Login) renderInitUserDone(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
|
||||
func (l *Login) renderInitUserDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
|
||||
data := l.getUserData(r, authReq, "User Init Done", "", "")
|
||||
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplInitUserDone], data, nil)
|
||||
}
|
||||
|
@ -2,22 +2,21 @@ package handler
|
||||
|
||||
import (
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
)
|
||||
|
||||
const (
|
||||
tmplLinkUsersDone = "linkusersdone"
|
||||
)
|
||||
|
||||
func (l *Login) linkUsers(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
|
||||
func (l *Login) linkUsers(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
|
||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||
err = l.authRepo.LinkExternalUsers(setContext(r.Context(), authReq.UserOrgID), authReq.ID, userAgentID, model.BrowserInfoFromRequest(r))
|
||||
err = l.authRepo.LinkExternalUsers(setContext(r.Context(), authReq.UserOrgID), authReq.ID, userAgentID, domain.BrowserInfoFromRequest(r))
|
||||
l.renderLinkUsersDone(w, r, authReq, err)
|
||||
}
|
||||
|
||||
func (l *Login) renderLinkUsersDone(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
|
||||
func (l *Login) renderLinkUsersDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
|
||||
var errType, errMessage string
|
||||
data := l.getUserData(r, authReq, "Linking Users Done", errType, errMessage)
|
||||
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplLinkUsersDone], data, nil)
|
||||
|
@ -1,10 +1,10 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -62,7 +62,7 @@ func (l *Login) handleLoginNameCheck(w http.ResponseWriter, r *http.Request) {
|
||||
l.renderNextStep(w, r, authReq)
|
||||
}
|
||||
|
||||
func (l *Login) renderLogin(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
|
||||
func (l *Login) renderLogin(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
|
||||
var errType, errMessage string
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
|
@ -1,9 +1,8 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -47,21 +46,21 @@ func (l *Login) handleMailVerificationCheck(w http.ResponseWriter, r *http.Reque
|
||||
l.checkMailCode(w, r, authReq, data.UserID, data.Code)
|
||||
return
|
||||
}
|
||||
userOrg := login
|
||||
userOrg := ""
|
||||
if authReq != nil {
|
||||
userOrg = authReq.UserOrgID
|
||||
}
|
||||
err = l.authRepo.ResendEmailVerificationMail(setContext(r.Context(), userOrg), data.UserID)
|
||||
err = l.command.CreateHumanEmailVerificationCode(setContext(r.Context(), userOrg), data.UserID, userOrg)
|
||||
l.renderMailVerification(w, r, authReq, data.UserID, err)
|
||||
}
|
||||
|
||||
func (l *Login) checkMailCode(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, userID, code string) {
|
||||
userOrg := login
|
||||
func (l *Login) checkMailCode(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID, code string) {
|
||||
userOrg := ""
|
||||
if authReq != nil {
|
||||
userID = authReq.UserID
|
||||
userOrg = authReq.UserOrgID
|
||||
}
|
||||
err := l.authRepo.VerifyEmail(setContext(r.Context(), userOrg), userID, code)
|
||||
err := l.command.VerifyHumanEmail(setContext(r.Context(), userOrg), userID, code, userOrg)
|
||||
if err != nil {
|
||||
l.renderMailVerification(w, r, authReq, userID, err)
|
||||
return
|
||||
@ -69,7 +68,7 @@ func (l *Login) checkMailCode(w http.ResponseWriter, r *http.Request, authReq *m
|
||||
l.renderMailVerified(w, r, authReq)
|
||||
}
|
||||
|
||||
func (l *Login) renderMailVerification(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, userID string, err error) {
|
||||
func (l *Login) renderMailVerification(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID string, err error) {
|
||||
var errType, errMessage string
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
@ -85,7 +84,7 @@ func (l *Login) renderMailVerification(w http.ResponseWriter, r *http.Request, a
|
||||
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplMailVerification], data, nil)
|
||||
}
|
||||
|
||||
func (l *Login) renderMailVerified(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
|
||||
func (l *Login) renderMailVerified(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
|
||||
data := mailVerificationData{
|
||||
baseData: l.getBaseData(r, authReq, "Mail Verified", "", ""),
|
||||
profileData: l.getProfileData(authReq),
|
||||
|
@ -1,9 +1,8 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -13,7 +12,7 @@ const (
|
||||
type mfaInitDoneData struct {
|
||||
}
|
||||
|
||||
func (l *Login) renderMFAInitDone(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *mfaDoneData) {
|
||||
func (l *Login) renderMFAInitDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, data *mfaDoneData) {
|
||||
var errType, errMessage string
|
||||
data.baseData = l.getBaseData(r, authReq, "MFA Init Done", errType, errMessage)
|
||||
data.profileData = l.getProfileData(authReq)
|
||||
|
@ -2,11 +2,11 @@ package handler
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
user_model "github.com/caos/zitadel/internal/user/model"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -18,11 +18,11 @@ type u2fInitData struct {
|
||||
MFAType model.MFAType
|
||||
}
|
||||
|
||||
func (l *Login) renderRegisterU2F(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
|
||||
func (l *Login) renderRegisterU2F(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
|
||||
var errType, errMessage, credentialData string
|
||||
var u2f *user_model.WebAuthNToken
|
||||
var u2f *domain.WebAuthNToken
|
||||
if err == nil {
|
||||
u2f, err = l.authRepo.AddMFAU2F(setContext(r.Context(), authReq.UserOrgID), authReq.UserID)
|
||||
u2f, err = l.command.HumanAddU2FSetup(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID, true)
|
||||
}
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
@ -54,12 +54,12 @@ func (l *Login) handleRegisterU2F(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||
if err = l.authRepo.VerifyMFAU2FSetup(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, data.Name, userAgentID, credData); err != nil {
|
||||
if err = l.command.HumanVerifyU2FSetup(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID, data.Name, userAgentID, credData); err != nil {
|
||||
l.renderRegisterU2F(w, r, authReq, err)
|
||||
return
|
||||
}
|
||||
done := &mfaDoneData{
|
||||
MFAType: model.MFATypeU2F,
|
||||
MFAType: domain.MFATypeU2F,
|
||||
}
|
||||
l.renderMFAInitDone(w, r, authReq, done)
|
||||
}
|
||||
|
@ -2,13 +2,13 @@ package handler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
svg "github.com/ajstarks/svgo"
|
||||
"github.com/boombuler/barcode/qr"
|
||||
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
"github.com/caos/zitadel/internal/qrcode"
|
||||
)
|
||||
|
||||
@ -17,10 +17,10 @@ const (
|
||||
)
|
||||
|
||||
type mfaInitVerifyData struct {
|
||||
MFAType model.MFAType `schema:"mfaType"`
|
||||
Code string `schema:"code"`
|
||||
URL string `schema:"url"`
|
||||
Secret string `schema:"secret"`
|
||||
MFAType domain.MFAType `schema:"mfaType"`
|
||||
Code string `schema:"code"`
|
||||
URL string `schema:"url"`
|
||||
Secret string `schema:"secret"`
|
||||
}
|
||||
|
||||
func (l *Login) handleMFAInitVerify(w http.ResponseWriter, r *http.Request) {
|
||||
@ -32,7 +32,7 @@ func (l *Login) handleMFAInitVerify(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
var verifyData *mfaVerifyData
|
||||
switch data.MFAType {
|
||||
case model.MFATypeOTP:
|
||||
case domain.MFATypeOTP:
|
||||
verifyData = l.handleOTPVerify(w, r, authReq, data)
|
||||
}
|
||||
|
||||
@ -47,9 +47,9 @@ func (l *Login) handleMFAInitVerify(w http.ResponseWriter, r *http.Request) {
|
||||
l.renderMFAInitDone(w, r, authReq, done)
|
||||
}
|
||||
|
||||
func (l *Login) handleOTPVerify(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *mfaInitVerifyData) *mfaVerifyData {
|
||||
func (l *Login) handleOTPVerify(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, data *mfaInitVerifyData) *mfaVerifyData {
|
||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||
err := l.authRepo.VerifyMFAOTPSetup(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, data.Code, userAgentID)
|
||||
err := l.command.HumanCheckMFAOTPSetup(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, data.Code, userAgentID, authReq.UserOrgID)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
@ -64,14 +64,14 @@ func (l *Login) handleOTPVerify(w http.ResponseWriter, r *http.Request, authReq
|
||||
return mfadata
|
||||
}
|
||||
|
||||
func (l *Login) renderMFAInitVerify(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *mfaVerifyData, err error) {
|
||||
func (l *Login) renderMFAInitVerify(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, data *mfaVerifyData, err error) {
|
||||
var errType, errMessage string
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
}
|
||||
data.baseData = l.getBaseData(r, authReq, "MFA Init Verify", errType, errMessage)
|
||||
data.profileData = l.getProfileData(authReq)
|
||||
if data.MFAType == model.MFATypeOTP {
|
||||
if data.MFAType == domain.MFATypeOTP {
|
||||
code, err := generateQrCode(data.otpData.Url)
|
||||
if err == nil {
|
||||
data.otpData.QrCode = code
|
||||
|
@ -1,9 +1,9 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
)
|
||||
|
||||
@ -12,8 +12,8 @@ const (
|
||||
)
|
||||
|
||||
type mfaPromptData struct {
|
||||
MFAProvider model.MFAType `schema:"provider"`
|
||||
Skip bool `schema:"skip"`
|
||||
MFAProvider domain.MFAType `schema:"provider"`
|
||||
Skip bool `schema:"skip"`
|
||||
}
|
||||
|
||||
func (l *Login) handleMFAPrompt(w http.ResponseWriter, r *http.Request) {
|
||||
@ -29,7 +29,7 @@ func (l *Login) handleMFAPrompt(w http.ResponseWriter, r *http.Request) {
|
||||
l.handleMFACreation(w, r, authReq, mfaVerifyData)
|
||||
return
|
||||
}
|
||||
err = l.authRepo.SkipMFAInit(setContext(r.Context(), authReq.UserOrgID), authReq.UserID)
|
||||
err = l.command.HumanSkipMFAInit(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID)
|
||||
if err != nil {
|
||||
l.renderError(w, r, authReq, err)
|
||||
return
|
||||
@ -48,7 +48,7 @@ func (l *Login) handleMFAPromptSelection(w http.ResponseWriter, r *http.Request)
|
||||
l.renderNextStep(w, r, authReq)
|
||||
}
|
||||
|
||||
func (l *Login) renderMFAPrompt(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, mfaPromptData *model.MFAPromptStep, err error) {
|
||||
func (l *Login) renderMFAPrompt(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, mfaPromptData *domain.MFAPromptStep, err error) {
|
||||
var errType, errMessage string
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
@ -76,20 +76,20 @@ func (l *Login) renderMFAPrompt(w http.ResponseWriter, r *http.Request, authReq
|
||||
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplMFAPrompt], data, nil)
|
||||
}
|
||||
|
||||
func (l *Login) handleMFACreation(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *mfaVerifyData) {
|
||||
func (l *Login) handleMFACreation(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, data *mfaVerifyData) {
|
||||
switch data.MFAType {
|
||||
case model.MFATypeOTP:
|
||||
case domain.MFATypeOTP:
|
||||
l.handleOTPCreation(w, r, authReq, data)
|
||||
return
|
||||
case model.MFATypeU2F:
|
||||
case domain.MFATypeU2F:
|
||||
l.renderRegisterU2F(w, r, authReq, nil)
|
||||
return
|
||||
}
|
||||
l.renderError(w, r, authReq, caos_errs.ThrowPreconditionFailed(nil, "APP-Or3HO", "Errors.User.MFA.NoProviders"))
|
||||
}
|
||||
|
||||
func (l *Login) handleOTPCreation(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *mfaVerifyData) {
|
||||
otp, err := l.authRepo.AddMFAOTP(setContext(r.Context(), authReq.UserOrgID), authReq.UserID)
|
||||
func (l *Login) handleOTPCreation(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, data *mfaVerifyData) {
|
||||
otp, err := l.command.AddHumanOTP(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID)
|
||||
if err != nil {
|
||||
l.renderError(w, r, authReq, err)
|
||||
return
|
||||
|
@ -1,6 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
@ -25,7 +26,7 @@ func (l *Login) handleMFAVerify(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
if data.MFAType == model.MFATypeOTP {
|
||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||
err = l.authRepo.VerifyMFAOTP(setContext(r.Context(), authReq.UserOrgID), authReq.ID, authReq.UserID, data.Code, userAgentID, model.BrowserInfoFromRequest(r))
|
||||
err = l.authRepo.VerifyMFAOTP(setContext(r.Context(), authReq.UserOrgID), authReq.ID, authReq.UserID, authReq.UserOrgID, data.Code, userAgentID, domain.BrowserInfoFromRequest(r))
|
||||
}
|
||||
if err != nil {
|
||||
l.renderError(w, r, authReq, err)
|
||||
@ -34,7 +35,7 @@ func (l *Login) handleMFAVerify(w http.ResponseWriter, r *http.Request) {
|
||||
l.renderNextStep(w, r, authReq)
|
||||
}
|
||||
|
||||
func (l *Login) renderMFAVerify(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, verificationStep *model.MFAVerificationStep, err error) {
|
||||
func (l *Login) renderMFAVerify(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, verificationStep *domain.MFAVerificationStep, err error) {
|
||||
if verificationStep == nil {
|
||||
l.renderError(w, r, authReq, err)
|
||||
return
|
||||
@ -43,7 +44,7 @@ func (l *Login) renderMFAVerify(w http.ResponseWriter, r *http.Request, authReq
|
||||
l.renderMFAVerifySelected(w, r, authReq, verificationStep, provider, err)
|
||||
}
|
||||
|
||||
func (l *Login) renderMFAVerifySelected(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, verificationStep *model.MFAVerificationStep, selectedProvider model.MFAType, err error) {
|
||||
func (l *Login) renderMFAVerifySelected(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, verificationStep *domain.MFAVerificationStep, selectedProvider domain.MFAType, err error) {
|
||||
var errType, errMessage string
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
@ -54,12 +55,12 @@ func (l *Login) renderMFAVerifySelected(w http.ResponseWriter, r *http.Request,
|
||||
return
|
||||
}
|
||||
switch selectedProvider {
|
||||
case model.MFATypeU2F:
|
||||
l.renderU2FVerification(w, r, authReq, removeSelectedProviderFromList(verificationStep.MFAProviders, model.MFATypeU2F), nil)
|
||||
case domain.MFATypeU2F:
|
||||
l.renderU2FVerification(w, r, authReq, removeSelectedProviderFromList(verificationStep.MFAProviders, domain.MFATypeU2F), nil)
|
||||
return
|
||||
case model.MFATypeOTP:
|
||||
data.MFAProviders = removeSelectedProviderFromList(verificationStep.MFAProviders, model.MFATypeOTP)
|
||||
data.SelectedMFAProvider = model.MFATypeOTP
|
||||
case domain.MFATypeOTP:
|
||||
data.MFAProviders = removeSelectedProviderFromList(verificationStep.MFAProviders, domain.MFATypeOTP)
|
||||
data.SelectedMFAProvider = domain.MFATypeOTP
|
||||
default:
|
||||
l.renderError(w, r, authReq, err)
|
||||
return
|
||||
@ -67,7 +68,7 @@ func (l *Login) renderMFAVerifySelected(w http.ResponseWriter, r *http.Request,
|
||||
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplMFAVerify], data, nil)
|
||||
}
|
||||
|
||||
func removeSelectedProviderFromList(providers []model.MFAType, selected model.MFAType) []model.MFAType {
|
||||
func removeSelectedProviderFromList(providers []domain.MFAType, selected domain.MFAType) []domain.MFAType {
|
||||
for i := len(providers) - 1; i >= 0; i-- {
|
||||
if providers[i] == selected {
|
||||
copy(providers[i:], providers[i+1:])
|
||||
|
@ -2,11 +2,10 @@ package handler
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
user_model "github.com/caos/zitadel/internal/user/model"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -15,21 +14,21 @@ const (
|
||||
|
||||
type mfaU2FData struct {
|
||||
webAuthNData
|
||||
MFAProviders []model.MFAType
|
||||
SelectedProvider model.MFAType
|
||||
MFAProviders []domain.MFAType
|
||||
SelectedProvider domain.MFAType
|
||||
}
|
||||
|
||||
type mfaU2FFormData struct {
|
||||
webAuthNFormData
|
||||
SelectedProvider model.MFAType `schema:"provider"`
|
||||
SelectedProvider domain.MFAType `schema:"provider"`
|
||||
}
|
||||
|
||||
func (l *Login) renderU2FVerification(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, providers []model.MFAType, err error) {
|
||||
func (l *Login) renderU2FVerification(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, providers []domain.MFAType, err error) {
|
||||
var errType, errMessage, credentialData string
|
||||
var webAuthNLogin *user_model.WebAuthNLogin
|
||||
var webAuthNLogin *domain.WebAuthNLogin
|
||||
if err == nil {
|
||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||
webAuthNLogin, err = l.authRepo.BeginMFAU2FLogin(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.ID, userAgentID)
|
||||
webAuthNLogin, err = l.authRepo.BeginMFAU2FLogin(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID, authReq.ID, userAgentID)
|
||||
}
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
@ -55,7 +54,7 @@ func (l *Login) handleU2FVerification(w http.ResponseWriter, r *http.Request) {
|
||||
l.renderError(w, r, authReq, err)
|
||||
return
|
||||
}
|
||||
step, ok := authReq.PossibleSteps[0].(*model.MFAVerificationStep)
|
||||
step, ok := authReq.PossibleSteps[0].(*domain.MFAVerificationStep)
|
||||
if !ok {
|
||||
l.renderError(w, r, authReq, err)
|
||||
return
|
||||
@ -70,7 +69,7 @@ func (l *Login) handleU2FVerification(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||
err = l.authRepo.VerifyMFAU2F(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.ID, userAgentID, credData, model.BrowserInfoFromRequest(r))
|
||||
err = l.authRepo.VerifyMFAU2F(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID, authReq.ID, userAgentID, credData, domain.BrowserInfoFromRequest(r))
|
||||
if err != nil {
|
||||
l.renderU2FVerification(w, r, authReq, step.MFAProviders, err)
|
||||
return
|
||||
|
@ -1,10 +1,10 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -15,7 +15,7 @@ type passwordFormData struct {
|
||||
Password string `schema:"password"`
|
||||
}
|
||||
|
||||
func (l *Login) renderPassword(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
|
||||
func (l *Login) renderPassword(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
|
||||
var errType, errMessage string
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
@ -32,7 +32,7 @@ func (l *Login) handlePasswordCheck(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||
err = l.authRepo.VerifyPassword(setContext(r.Context(), authReq.UserOrgID), authReq.ID, authReq.UserID, data.Password, userAgentID, model.BrowserInfoFromRequest(r))
|
||||
err = l.authRepo.VerifyPassword(setContext(r.Context(), authReq.UserOrgID), authReq.ID, authReq.UserID, authReq.UserOrgID, data.Password, userAgentID, domain.BrowserInfoFromRequest(r))
|
||||
if err != nil {
|
||||
l.renderPassword(w, r, authReq, err)
|
||||
return
|
||||
|
@ -1,9 +1,8 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -16,11 +15,16 @@ func (l *Login) handlePasswordReset(w http.ResponseWriter, r *http.Request) {
|
||||
l.renderError(w, r, authReq, err)
|
||||
return
|
||||
}
|
||||
err = l.authRepo.RequestPasswordReset(setContext(r.Context(), authReq.UserOrgID), authReq.LoginName)
|
||||
user, err := l.authRepo.UserByLoginName(setContext(r.Context(), authReq.UserOrgID), authReq.LoginName)
|
||||
if err != nil {
|
||||
l.renderPasswordResetDone(w, r, authReq, err)
|
||||
return
|
||||
}
|
||||
err = l.command.RequestSetPassword(setContext(r.Context(), authReq.UserOrgID), user.ID, authReq.UserOrgID, domain.NotificationTypeEmail)
|
||||
l.renderPasswordResetDone(w, r, authReq, err)
|
||||
}
|
||||
|
||||
func (l *Login) renderPasswordResetDone(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
|
||||
func (l *Login) renderPasswordResetDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
|
||||
var errType, errMessage string
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
|
@ -2,11 +2,10 @@ package handler
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
user_model "github.com/caos/zitadel/internal/user/model"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -23,12 +22,12 @@ type passwordlessFormData struct {
|
||||
PasswordLogin bool `schema:"passwordlogin"`
|
||||
}
|
||||
|
||||
func (l *Login) renderPasswordlessVerification(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
|
||||
func (l *Login) renderPasswordlessVerification(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
|
||||
var errType, errMessage, credentialData string
|
||||
var webAuthNLogin *user_model.WebAuthNLogin
|
||||
var webAuthNLogin *domain.WebAuthNLogin
|
||||
if err == nil {
|
||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||
webAuthNLogin, err = l.authRepo.BeginPasswordlessLogin(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.ID, userAgentID)
|
||||
webAuthNLogin, err = l.authRepo.BeginPasswordlessLogin(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID, authReq.ID, userAgentID)
|
||||
}
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
@ -67,7 +66,7 @@ func (l *Login) handlePasswordlessVerification(w http.ResponseWriter, r *http.Re
|
||||
return
|
||||
}
|
||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||
err = l.authRepo.VerifyPasswordless(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.ID, userAgentID, credData, model.BrowserInfoFromRequest(r))
|
||||
err = l.authRepo.VerifyPasswordless(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID, authReq.ID, userAgentID, credData, domain.BrowserInfoFromRequest(r))
|
||||
if err != nil {
|
||||
l.renderPasswordlessVerification(w, r, authReq, err)
|
||||
return
|
||||
|
@ -1,20 +1,16 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||
)
|
||||
|
||||
const (
|
||||
tmplRegister = "register"
|
||||
orgProjectCreatorRole = "ORG_PROJECT_CREATOR"
|
||||
tmplRegister = "register"
|
||||
)
|
||||
|
||||
type registerFormData struct {
|
||||
@ -68,15 +64,13 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
resourceOwner := iam.GlobalOrgID
|
||||
member := &org_model.OrgMember{
|
||||
ObjectRoot: models.ObjectRoot{AggregateID: iam.GlobalOrgID},
|
||||
Roles: []string{orgProjectCreatorRole},
|
||||
}
|
||||
memberRoles := []string{domain.RoleOrgProjectCreator}
|
||||
|
||||
if authRequest.RequestedOrgID != "" && authRequest.RequestedOrgID != iam.GlobalOrgID {
|
||||
member = nil
|
||||
memberRoles = nil
|
||||
resourceOwner = authRequest.RequestedOrgID
|
||||
}
|
||||
user, err := l.authRepo.Register(setContext(r.Context(), resourceOwner), data.toUserModel(), member, resourceOwner)
|
||||
user, err := l.command.RegisterHuman(setContext(r.Context(), resourceOwner), resourceOwner, data.toHumanDomain(), nil, memberRoles)
|
||||
if err != nil {
|
||||
l.renderRegister(w, r, authRequest, data, err)
|
||||
return
|
||||
@ -89,7 +83,7 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) {
|
||||
l.renderNextStep(w, r, authRequest)
|
||||
}
|
||||
|
||||
func (l *Login) renderRegister(w http.ResponseWriter, r *http.Request, authRequest *model.AuthRequest, formData *registerFormData, err error) {
|
||||
func (l *Login) renderRegister(w http.ResponseWriter, r *http.Request, authRequest *domain.AuthRequest, formData *registerFormData, err error) {
|
||||
var errType, errMessage string
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
@ -142,21 +136,19 @@ func (l *Login) renderRegister(w http.ResponseWriter, r *http.Request, authReque
|
||||
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplRegister], data, funcs)
|
||||
}
|
||||
|
||||
func (d registerFormData) toUserModel() *usr_model.User {
|
||||
return &usr_model.User{
|
||||
Human: &usr_model.Human{
|
||||
Profile: &usr_model.Profile{
|
||||
FirstName: d.Firstname,
|
||||
LastName: d.Lastname,
|
||||
PreferredLanguage: language.Make(d.Language),
|
||||
Gender: usr_model.Gender(d.Gender),
|
||||
},
|
||||
Password: &usr_model.Password{
|
||||
SecretString: d.Password,
|
||||
},
|
||||
Email: &usr_model.Email{
|
||||
EmailAddress: d.Email,
|
||||
},
|
||||
func (d registerFormData) toHumanDomain() *domain.Human {
|
||||
return &domain.Human{
|
||||
Profile: &domain.Profile{
|
||||
FirstName: d.Firstname,
|
||||
LastName: d.Lastname,
|
||||
PreferredLanguage: language.Make(d.Language),
|
||||
Gender: domain.Gender(d.Gender),
|
||||
},
|
||||
Password: &domain.Password{
|
||||
SecretString: d.Password,
|
||||
},
|
||||
Email: &domain.Email{
|
||||
EmailAddress: d.Email,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
@ -27,7 +27,7 @@ func (l *Login) handleRegisterOption(w http.ResponseWriter, r *http.Request) {
|
||||
l.renderRegisterOption(w, r, authRequest, nil)
|
||||
}
|
||||
|
||||
func (l *Login) renderRegisterOption(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
|
||||
func (l *Login) renderRegisterOption(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
|
||||
var errType, errMessage string
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
|
@ -1,10 +1,10 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
auth_model "github.com/caos/zitadel/internal/auth/model"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||
@ -78,7 +78,7 @@ func (l *Login) handleRegisterOrgCheck(w http.ResponseWriter, r *http.Request) {
|
||||
l.renderNextStep(w, r, authRequest)
|
||||
}
|
||||
|
||||
func (l *Login) renderRegisterOrg(w http.ResponseWriter, r *http.Request, authRequest *model.AuthRequest, formData *registerOrgFormData, err error) {
|
||||
func (l *Login) renderRegisterOrg(w http.ResponseWriter, r *http.Request, authRequest *domain.AuthRequest, formData *registerOrgFormData, err error) {
|
||||
var errType, errMessage string
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
|
@ -3,6 +3,7 @@ package handler
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"path"
|
||||
@ -15,7 +16,6 @@ import (
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/i18n"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
"github.com/caos/zitadel/internal/renderer"
|
||||
)
|
||||
|
||||
@ -153,7 +153,7 @@ func CreateRenderer(pathPrefix string, staticDir http.FileSystem, cookieName str
|
||||
"hasExternalLogin": func() bool {
|
||||
return false
|
||||
},
|
||||
"idpProviderClass": func(stylingType iam_model.IDPStylingType) string {
|
||||
"idpProviderClass": func(stylingType domain.IDPConfigStylingType) string {
|
||||
return stylingType.GetCSSClass()
|
||||
},
|
||||
}
|
||||
@ -167,7 +167,7 @@ func CreateRenderer(pathPrefix string, staticDir http.FileSystem, cookieName str
|
||||
return r
|
||||
}
|
||||
|
||||
func (l *Login) renderNextStep(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
|
||||
func (l *Login) renderNextStep(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
|
||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||
authReq, err := l.authRepo.AuthRequestByID(r.Context(), authReq.ID, userAgentID)
|
||||
if err != nil {
|
||||
@ -181,7 +181,7 @@ func (l *Login) renderNextStep(w http.ResponseWriter, r *http.Request, authReq *
|
||||
l.chooseNextStep(w, r, authReq, 0, nil)
|
||||
}
|
||||
|
||||
func (l *Login) renderError(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
|
||||
func (l *Login) renderError(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
|
||||
if authReq == nil || len(authReq.PossibleSteps) == 0 {
|
||||
l.renderInternalError(w, r, authReq, caos_errs.ThrowInternal(err, "APP-OVOiT", "no possible steps"))
|
||||
return
|
||||
@ -189,54 +189,54 @@ func (l *Login) renderError(w http.ResponseWriter, r *http.Request, authReq *mod
|
||||
l.chooseNextStep(w, r, authReq, 0, err)
|
||||
}
|
||||
|
||||
func (l *Login) chooseNextStep(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, stepNumber int, err error) {
|
||||
func (l *Login) chooseNextStep(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, stepNumber int, err error) {
|
||||
switch step := authReq.PossibleSteps[stepNumber].(type) {
|
||||
case *model.LoginStep:
|
||||
case *domain.LoginStep:
|
||||
if len(authReq.PossibleSteps) > 1 {
|
||||
l.chooseNextStep(w, r, authReq, 1, err)
|
||||
return
|
||||
}
|
||||
l.renderLogin(w, r, authReq, err)
|
||||
case *model.SelectUserStep:
|
||||
case *domain.SelectUserStep:
|
||||
l.renderUserSelection(w, r, authReq, step)
|
||||
case *model.InitPasswordStep:
|
||||
case *domain.InitPasswordStep:
|
||||
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
||||
case *model.PasswordStep:
|
||||
case *domain.PasswordStep:
|
||||
l.renderPassword(w, r, authReq, nil)
|
||||
case *model.PasswordlessStep:
|
||||
case *domain.PasswordlessStep:
|
||||
l.renderPasswordlessVerification(w, r, authReq, nil)
|
||||
case *model.MFAVerificationStep:
|
||||
case *domain.MFAVerificationStep:
|
||||
l.renderMFAVerify(w, r, authReq, step, err)
|
||||
case *model.RedirectToCallbackStep:
|
||||
case *domain.RedirectToCallbackStep:
|
||||
if len(authReq.PossibleSteps) > 1 {
|
||||
l.chooseNextStep(w, r, authReq, 1, err)
|
||||
return
|
||||
}
|
||||
l.redirectToCallback(w, r, authReq)
|
||||
case *model.ChangePasswordStep:
|
||||
case *domain.ChangePasswordStep:
|
||||
l.renderChangePassword(w, r, authReq, err)
|
||||
case *model.VerifyEMailStep:
|
||||
case *domain.VerifyEMailStep:
|
||||
l.renderMailVerification(w, r, authReq, "", err)
|
||||
case *model.MFAPromptStep:
|
||||
case *domain.MFAPromptStep:
|
||||
l.renderMFAPrompt(w, r, authReq, step, err)
|
||||
case *model.InitUserStep:
|
||||
case *domain.InitUserStep:
|
||||
l.renderInitUser(w, r, authReq, "", "", step.PasswordSet, nil)
|
||||
case *model.ChangeUsernameStep:
|
||||
case *domain.ChangeUsernameStep:
|
||||
l.renderChangeUsername(w, r, authReq, nil)
|
||||
case *model.LinkUsersStep:
|
||||
case *domain.LinkUsersStep:
|
||||
l.linkUsers(w, r, authReq, err)
|
||||
case *model.ExternalNotFoundOptionStep:
|
||||
case *domain.ExternalNotFoundOptionStep:
|
||||
l.renderExternalNotFoundOption(w, r, authReq, err)
|
||||
case *model.ExternalLoginStep:
|
||||
case *domain.ExternalLoginStep:
|
||||
l.handleExternalLoginStep(w, r, authReq, step.SelectedIDPConfigID)
|
||||
case *model.GrantRequiredStep:
|
||||
case *domain.GrantRequiredStep:
|
||||
l.renderInternalError(w, r, authReq, caos_errs.ThrowPreconditionFailed(nil, "APP-asb43", "Errors.User.GrantRequired"))
|
||||
default:
|
||||
l.renderInternalError(w, r, authReq, caos_errs.ThrowInternal(nil, "APP-ds3QF", "step no possible"))
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Login) renderInternalError(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
|
||||
func (l *Login) renderInternalError(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
|
||||
var msg string
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
@ -245,7 +245,7 @@ func (l *Login) renderInternalError(w http.ResponseWriter, r *http.Request, auth
|
||||
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplError], data, nil)
|
||||
}
|
||||
|
||||
func (l *Login) getUserData(r *http.Request, authReq *model.AuthRequest, title string, errType, errMessage string) userData {
|
||||
func (l *Login) getUserData(r *http.Request, authReq *domain.AuthRequest, title string, errType, errMessage string) userData {
|
||||
userData := userData{
|
||||
baseData: l.getBaseData(r, authReq, title, errType, errMessage),
|
||||
profileData: l.getProfileData(authReq),
|
||||
@ -256,7 +256,7 @@ func (l *Login) getUserData(r *http.Request, authReq *model.AuthRequest, title s
|
||||
return userData
|
||||
}
|
||||
|
||||
func (l *Login) getBaseData(r *http.Request, authReq *model.AuthRequest, title string, errType, errMessage string) baseData {
|
||||
func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, title string, errType, errMessage string) baseData {
|
||||
baseData := baseData{
|
||||
errorData: errorData{
|
||||
ErrType: errType,
|
||||
@ -279,7 +279,7 @@ func (l *Login) getBaseData(r *http.Request, authReq *model.AuthRequest, title s
|
||||
return baseData
|
||||
}
|
||||
|
||||
func (l *Login) getProfileData(authReq *model.AuthRequest) profileData {
|
||||
func (l *Login) getProfileData(authReq *domain.AuthRequest) profileData {
|
||||
var loginName, displayName string
|
||||
if authReq != nil {
|
||||
loginName = authReq.LoginName
|
||||
@ -309,7 +309,7 @@ func (l *Login) getThemeMode(r *http.Request) string {
|
||||
return "" //TODO: impl
|
||||
}
|
||||
|
||||
func (l *Login) getOrgID(authReq *model.AuthRequest) string {
|
||||
func (l *Login) getOrgID(authReq *domain.AuthRequest) string {
|
||||
if authReq == nil {
|
||||
return ""
|
||||
}
|
||||
@ -319,14 +319,14 @@ func (l *Login) getOrgID(authReq *model.AuthRequest) string {
|
||||
return authReq.UserOrgID
|
||||
}
|
||||
|
||||
func (l *Login) getOrgName(authReq *model.AuthRequest) string {
|
||||
func (l *Login) getOrgName(authReq *domain.AuthRequest) string {
|
||||
if authReq == nil {
|
||||
return ""
|
||||
}
|
||||
return authReq.RequestedOrgName
|
||||
}
|
||||
|
||||
func getRequestID(authReq *model.AuthRequest, r *http.Request) string {
|
||||
func getRequestID(authReq *domain.AuthRequest, r *http.Request) string {
|
||||
if authReq != nil {
|
||||
return authReq.ID
|
||||
}
|
||||
@ -357,8 +357,8 @@ type baseData struct {
|
||||
AuthReqID string
|
||||
CSRF template.HTML
|
||||
Nonce string
|
||||
LoginPolicy *iam_model.LoginPolicyView
|
||||
IDPProviders []*iam_model.IDPProviderView
|
||||
LoginPolicy *domain.LoginPolicy
|
||||
IDPProviders []*domain.IDPProvider
|
||||
}
|
||||
|
||||
type errorData struct {
|
||||
@ -370,8 +370,8 @@ type userData struct {
|
||||
baseData
|
||||
profileData
|
||||
PasswordChecked string
|
||||
MFAProviders []model.MFAType
|
||||
SelectedMFAProvider model.MFAType
|
||||
MFAProviders []domain.MFAType
|
||||
SelectedMFAProvider domain.MFAType
|
||||
Linking bool
|
||||
}
|
||||
|
||||
@ -393,28 +393,28 @@ type passwordData struct {
|
||||
|
||||
type userSelectionData struct {
|
||||
baseData
|
||||
Users []model.UserSelection
|
||||
Users []domain.UserSelection
|
||||
Linking bool
|
||||
}
|
||||
|
||||
type mfaData struct {
|
||||
baseData
|
||||
profileData
|
||||
MFAProviders []model.MFAType
|
||||
MFAProviders []domain.MFAType
|
||||
MFARequired bool
|
||||
}
|
||||
|
||||
type mfaVerifyData struct {
|
||||
baseData
|
||||
profileData
|
||||
MFAType model.MFAType
|
||||
MFAType domain.MFAType
|
||||
otpData
|
||||
}
|
||||
|
||||
type mfaDoneData struct {
|
||||
baseData
|
||||
profileData
|
||||
MFAType model.MFAType
|
||||
MFAType domain.MFAType
|
||||
}
|
||||
|
||||
type otpData struct {
|
||||
|
@ -1,10 +1,10 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -15,7 +15,7 @@ type userSelectionFormData struct {
|
||||
UserID string `schema:"userID"`
|
||||
}
|
||||
|
||||
func (l *Login) renderUserSelection(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, selectionData *model.SelectUserStep) {
|
||||
func (l *Login) renderUserSelection(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, selectionData *domain.SelectUserStep) {
|
||||
data := userSelectionData{
|
||||
baseData: l.getBaseData(r, authReq, "Select User", "", ""),
|
||||
Users: selectionData.Users,
|
||||
|
@ -1,9 +1,8 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"net/http"
|
||||
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -15,7 +14,7 @@ type changeUsernameData struct {
|
||||
Username string `schema:"username"`
|
||||
}
|
||||
|
||||
func (l *Login) renderChangeUsername(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
|
||||
func (l *Login) renderChangeUsername(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
|
||||
var errType, errMessage string
|
||||
if err != nil {
|
||||
errMessage = l.getErrorMessage(r, err)
|
||||
@ -31,7 +30,7 @@ func (l *Login) handleChangeUsername(w http.ResponseWriter, r *http.Request) {
|
||||
l.renderError(w, r, authReq, err)
|
||||
return
|
||||
}
|
||||
err = l.authRepo.ChangeUsername(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, data.Username)
|
||||
err = l.command.ChangeUsername(setContext(r.Context(), authReq.UserOrgID), authReq.UserOrgID, authReq.UserID, data.Username)
|
||||
if err != nil {
|
||||
l.renderChangeUsername(w, r, authReq, err)
|
||||
return
|
||||
@ -39,7 +38,7 @@ func (l *Login) handleChangeUsername(w http.ResponseWriter, r *http.Request) {
|
||||
l.renderChangeUsernameDone(w, r, authReq)
|
||||
}
|
||||
|
||||
func (l *Login) renderChangeUsernameDone(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
|
||||
func (l *Login) renderChangeUsernameDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
|
||||
var errType, errMessage string
|
||||
data := l.getUserData(r, authReq, "Username Change Done", errType, errMessage)
|
||||
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplChangeUsernameDone], data, nil)
|
||||
|
@ -12,6 +12,8 @@
|
||||
|
||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||
|
||||
|
||||
{{template "error-message" .}}
|
||||
<div class="actions">
|
||||
<button class="primary right" type="submit">{{t "Actions.Next"}}</button>
|
||||
</div>
|
||||
|
@ -1,6 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
@ -117,21 +118,21 @@ func (r *UserSearchRequest) AppendMyOrgQuery(orgID string) {
|
||||
r.Queries = append(r.Queries, &UserSearchQuery{Key: UserSearchKeyResourceOwner, Method: model.SearchMethodEquals, Value: orgID})
|
||||
}
|
||||
|
||||
func (u *UserView) MFATypesSetupPossible(level req_model.MFALevel, policy *iam_model.LoginPolicyView) []req_model.MFAType {
|
||||
types := make([]req_model.MFAType, 0)
|
||||
func (u *UserView) MFATypesSetupPossible(level domain.MFALevel, policy *domain.LoginPolicy) []domain.MFAType {
|
||||
types := make([]domain.MFAType, 0)
|
||||
switch level {
|
||||
default:
|
||||
fallthrough
|
||||
case req_model.MFALevelSecondFactor:
|
||||
case domain.MFALevelSecondFactor:
|
||||
if policy.HasSecondFactors() {
|
||||
for _, mfaType := range policy.SecondFactors {
|
||||
switch mfaType {
|
||||
case iam_model.SecondFactorTypeOTP:
|
||||
case domain.SecondFactorTypeOTP:
|
||||
if u.OTPState != MFAStateReady {
|
||||
types = append(types, req_model.MFATypeOTP)
|
||||
types = append(types, domain.MFATypeOTP)
|
||||
}
|
||||
case iam_model.SecondFactorTypeU2F:
|
||||
types = append(types, req_model.MFATypeU2F)
|
||||
case domain.SecondFactorTypeU2F:
|
||||
types = append(types, domain.MFATypeU2F)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -140,24 +141,24 @@ func (u *UserView) MFATypesSetupPossible(level req_model.MFALevel, policy *iam_m
|
||||
return types
|
||||
}
|
||||
|
||||
func (u *UserView) MFATypesAllowed(level req_model.MFALevel, policy *iam_model.LoginPolicyView) ([]req_model.MFAType, bool) {
|
||||
types := make([]req_model.MFAType, 0)
|
||||
func (u *UserView) MFATypesAllowed(level domain.MFALevel, policy *domain.LoginPolicy) ([]domain.MFAType, bool) {
|
||||
types := make([]domain.MFAType, 0)
|
||||
required := true
|
||||
switch level {
|
||||
default:
|
||||
required = policy.ForceMFA
|
||||
fallthrough
|
||||
case req_model.MFALevelSecondFactor:
|
||||
case domain.MFALevelSecondFactor:
|
||||
if policy.HasSecondFactors() {
|
||||
for _, mfaType := range policy.SecondFactors {
|
||||
switch mfaType {
|
||||
case iam_model.SecondFactorTypeOTP:
|
||||
case domain.SecondFactorTypeOTP:
|
||||
if u.OTPState == MFAStateReady {
|
||||
types = append(types, req_model.MFATypeOTP)
|
||||
types = append(types, domain.MFATypeOTP)
|
||||
}
|
||||
case iam_model.SecondFactorTypeU2F:
|
||||
case domain.SecondFactorTypeU2F:
|
||||
if u.IsU2FReady() {
|
||||
types = append(types, req_model.MFATypeU2F)
|
||||
types = append(types, domain.MFATypeU2F)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ type NotifyUser struct {
|
||||
VerifiedPhone string `json:"-" gorm:"column:verified_phone"`
|
||||
PasswordSet bool `json:"-" gorm:"column:password_set"`
|
||||
Sequence uint64 `json:"-" gorm:"column:sequence"`
|
||||
State int32 `json:"-"`
|
||||
}
|
||||
|
||||
func NotifyUserFromModel(user *model.NotifyUser) *NotifyUser {
|
||||
@ -144,6 +145,8 @@ func (u *NotifyUser) AppendEvent(event *models.Event) (err error) {
|
||||
case es_model.UserPasswordChanged,
|
||||
es_model.HumanPasswordChanged:
|
||||
err = u.setPasswordData(event)
|
||||
case es_model.UserRemoved:
|
||||
u.State = int32(UserStateDeleted)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ func writeModelToLoginPolicy(wm *LoginPolicyWriteModel) *domain.LoginPolicy {
|
||||
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
|
||||
AllowUsernamePassword: wm.AllowUserNamePassword,
|
||||
AllowRegister: wm.AllowRegister,
|
||||
AllowExternalIdp: wm.AllowExternalIDP,
|
||||
AllowExternalIDP: wm.AllowExternalIDP,
|
||||
ForceMFA: wm.ForceMFA,
|
||||
PasswordlessType: wm.PasswordlessType,
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ func (wm *IAMIDPOIDCConfigWriteModel) NewChangedEvent(
|
||||
if userNameMapping.Valid() && wm.UserNameMapping != userNameMapping {
|
||||
changes = append(changes, idpconfig.ChangeUserNameMapping(userNameMapping))
|
||||
}
|
||||
if reflect.DeepEqual(wm.Scopes, scopes) {
|
||||
if !reflect.DeepEqual(wm.Scopes, scopes) {
|
||||
changes = append(changes, idpconfig.ChangeScopes(scopes))
|
||||
}
|
||||
if len(changes) == 0 {
|
||||
|
@ -44,7 +44,7 @@ func (r *CommandSide) addDefaultLoginPolicy(ctx context.Context, iamAgg *iam_rep
|
||||
return caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LoginPolicy.AlreadyExists")
|
||||
}
|
||||
|
||||
iamAgg.PushEvents(iam_repo.NewLoginPolicyAddedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIdp, policy.ForceMFA, policy.PasswordlessType))
|
||||
iamAgg.PushEvents(iam_repo.NewLoginPolicyAddedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, policy.PasswordlessType))
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -72,7 +72,7 @@ func (r *CommandSide) changeDefaultLoginPolicy(ctx context.Context, iamAgg *iam_
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "IAM-M0sif", "Errors.IAM.LoginPolicy.NotFound")
|
||||
}
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIdp, policy.ForceMFA, policy.PasswordlessType)
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, policy.PasswordlessType)
|
||||
if !hasChanged {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "IAM-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged")
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ func (r *CommandSide) AddLoginPolicy(ctx context.Context, policy *domain.LoginPo
|
||||
}
|
||||
|
||||
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.WriteModel)
|
||||
orgAgg.PushEvents(org.NewLoginPolicyAddedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIdp, policy.ForceMFA, policy.PasswordlessType))
|
||||
orgAgg.PushEvents(org.NewLoginPolicyAddedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, policy.PasswordlessType))
|
||||
|
||||
err = r.eventstore.PushAggregate(ctx, addedPolicy, orgAgg)
|
||||
if err != nil {
|
||||
@ -38,7 +38,7 @@ func (r *CommandSide) ChangeLoginPolicy(ctx context.Context, policy *domain.Logi
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "Org-M0sif", "Errors.Org.LoginPolicy.NotFound")
|
||||
}
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIdp, policy.ForceMFA, domain.PasswordlessType(policy.PasswordlessType))
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, domain.PasswordlessType(policy.PasswordlessType))
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-5M9vdd", "Errors.Org.LoginPolicy.NotChanged")
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
@ -144,7 +145,7 @@ func (r *CommandSide) ReactivateProject(ctx context.Context, projectID string, r
|
||||
return r.eventstore.PushAggregate(ctx, existingProject, projectAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) RemoveProject(ctx context.Context, projectID, resourceOwner string) error {
|
||||
func (r *CommandSide) RemoveProject(ctx context.Context, projectID, resourceOwner string, cascadingGrantIDs ...string) error {
|
||||
if projectID == "" || resourceOwner == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-66hM9", "Errors.Project.ProjectIDMissing")
|
||||
}
|
||||
@ -157,11 +158,21 @@ func (r *CommandSide) RemoveProject(ctx context.Context, projectID, resourceOwne
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-3M9sd", "Errors.Project.NotFound")
|
||||
}
|
||||
|
||||
aggregates := make([]eventstore.Aggregater, 0)
|
||||
projectAgg := ProjectAggregateFromWriteModel(&existingProject.WriteModel)
|
||||
projectAgg.PushEvents(project.NewProjectRemovedEvent(ctx, existingProject.Name, existingProject.ResourceOwner))
|
||||
//TODO: Remove User Grants by ProjectID
|
||||
aggregates = append(aggregates, projectAgg)
|
||||
|
||||
return r.eventstore.PushAggregate(ctx, existingProject, projectAgg)
|
||||
for _, grantID := range cascadingGrantIDs {
|
||||
grantAgg, _, err := r.removeUserGrant(ctx, grantID, "", true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
aggregates = append(aggregates, grantAgg)
|
||||
}
|
||||
|
||||
_, err = r.eventstore.PushAggregates(ctx, aggregates...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *CommandSide) getProjectWriteModelByID(ctx context.Context, projectID, resourceOwner string) (*ProjectWriteModel, error) {
|
||||
|
@ -91,7 +91,7 @@ func (r *CommandSide) SetupStep1(ctx context.Context, step1 *Step1) error {
|
||||
&domain.LoginPolicy{
|
||||
AllowUsernamePassword: step1.DefaultLoginPolicy.AllowUsernamePassword,
|
||||
AllowRegister: step1.DefaultLoginPolicy.AllowRegister,
|
||||
AllowExternalIdp: step1.DefaultLoginPolicy.AllowExternalIdp,
|
||||
AllowExternalIDP: step1.DefaultLoginPolicy.AllowExternalIdp,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -2,6 +2,10 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
auth_req_model "github.com/caos/zitadel/internal/auth_request/model"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
@ -21,7 +25,7 @@ func (r *CommandSide) ChangeUsername(ctx context.Context, orgID, userID, userNam
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-5N9ds", "Errors.User.NotFound")
|
||||
}
|
||||
if existingUser.UserName == userName {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.UsernameNotChanged")
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6m9gs", "Errors.User.UsernameNotChanged")
|
||||
}
|
||||
|
||||
orgIAMPolicy, err := r.getOrgIAMPolicy(ctx, orgID)
|
||||
@ -89,7 +93,7 @@ func (r *CommandSide) LockUser(ctx context.Context, userID, resourceOwner string
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-5M9fs", "Errors.User.NotFound")
|
||||
}
|
||||
if existingUser.UserState != domain.UserStateActive && existingUser.UserState != domain.UserStateInitial {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.ShouldBeActiveOrInitial")
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3NN8v", "Errors.User.ShouldBeActiveOrInitial")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||
userAgg.PushEvents(user.NewUserLockedEvent(ctx))
|
||||
@ -139,6 +143,69 @@ func (r *CommandSide) RemoveUser(ctx context.Context, userID, resourceOwner stri
|
||||
return r.eventstore.PushAggregate(ctx, existingUser, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) CreateUserToken(ctx context.Context, orgID, agentID, clientID, userID string, audience, scopes []string, lifetime time.Duration) (*domain.Token, error) {
|
||||
if orgID == "" || userID == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-55n8M", "Errors.IDMissing")
|
||||
}
|
||||
existingUser, err := r.userWriteModelByID(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-1d6Gg", "Errors.User.NotFound")
|
||||
}
|
||||
|
||||
for _, scope := range scopes {
|
||||
if strings.HasPrefix(scope, auth_req_model.ProjectIDScope) && strings.HasSuffix(scope, auth_req_model.AudSuffix) {
|
||||
audience = append(audience, strings.TrimSuffix(strings.TrimPrefix(scope, auth_req_model.ProjectIDScope), auth_req_model.AudSuffix))
|
||||
}
|
||||
}
|
||||
|
||||
preferredLanguage := ""
|
||||
existingHuman, err := r.getHumanWriteModelByID(ctx, userID, orgID)
|
||||
if existingHuman != nil {
|
||||
preferredLanguage = existingHuman.PreferredLanguage.String()
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
tokenID, err := r.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||
userAgg.PushEvents(user.NewUserTokenAddedEvent(ctx, tokenID, clientID, agentID, preferredLanguage, audience, scopes, now.Add(lifetime)))
|
||||
|
||||
err = r.eventstore.PushAggregate(ctx, existingUser, userAgg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &domain.Token{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: userID,
|
||||
},
|
||||
TokenID: tokenID,
|
||||
UserAgentID: agentID,
|
||||
ApplicationID: clientID,
|
||||
Audience: audience,
|
||||
Scopes: scopes,
|
||||
Expiration: now.Add(lifetime),
|
||||
PreferredLanguage: preferredLanguage,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) UserDomainClaimedSent(ctx context.Context, orgID, userID string) (err error) {
|
||||
existingUser, err := r.userWriteModelByID(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-5m9gK", "Errors.User.NotFound")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||
userAgg.PushEvents(user.NewDomainClaimedSentEvent(ctx))
|
||||
return r.eventstore.PushAggregate(ctx, existingUser, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) checkUserExists(ctx context.Context, userID, resourceOwner string) error {
|
||||
userWriteModel, err := r.userWriteModelByID(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
|
@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
func writeModelToHuman(wm *HumanWriteModel) *domain.Human {
|
||||
@ -77,6 +78,15 @@ func writeModelToMachine(wm *MachineWriteModel) *domain.Machine {
|
||||
}
|
||||
}
|
||||
|
||||
func keyWriteModelToMachineKey(wm *MachineKeyWriteModel) *domain.MachineKey {
|
||||
return &domain.MachineKey{
|
||||
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
|
||||
KeyID: wm.KeyID,
|
||||
Type: wm.KeyType,
|
||||
ExpirationDate: wm.ExpirationDate,
|
||||
}
|
||||
}
|
||||
|
||||
func readModelToU2FTokens(wm *HumanU2FTokensReadModel) []*domain.WebAuthNToken {
|
||||
tokens := make([]*domain.WebAuthNToken, len(wm.WebAuthNTokens))
|
||||
for i, token := range wm.WebAuthNTokens {
|
||||
@ -107,3 +117,19 @@ func writeModelToWebAuthN(wm *HumanWebAuthNWriteModel) *domain.WebAuthNToken {
|
||||
State: wm.State,
|
||||
}
|
||||
}
|
||||
|
||||
func authRequestDomainToAuthRequestInfo(authRequest *domain.AuthRequest) *user.AuthRequestInfo {
|
||||
info := &user.AuthRequestInfo{
|
||||
ID: authRequest.ID,
|
||||
UserAgentID: authRequest.AgentID,
|
||||
SelectedIDPConfigID: authRequest.SelectedIDPConfigID,
|
||||
}
|
||||
if authRequest.BrowserInfo != nil {
|
||||
info.BrowserInfo = &user.BrowserInfo{
|
||||
UserAgent: authRequest.BrowserInfo.UserAgent,
|
||||
AcceptLanguage: authRequest.BrowserInfo.AcceptLanguage,
|
||||
RemoteIP: authRequest.BrowserInfo.RemoteIP,
|
||||
}
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
@ -189,7 +189,7 @@ func (r *CommandSide) BulkRemoveUserGrant(ctx context.Context, grantIDs []string
|
||||
}
|
||||
|
||||
func (r *CommandSide) removeUserGrant(ctx context.Context, grantID, resourceOwner string, cascade bool) (_ *usergrant.Aggregate, _ *UserGrantWriteModel, err error) {
|
||||
if grantID == "" || resourceOwner == "" {
|
||||
if grantID == "" {
|
||||
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-J9sc5", "Errors.UserGrant.IDMissing")
|
||||
}
|
||||
|
||||
@ -205,7 +205,6 @@ func (r *CommandSide) removeUserGrant(ctx context.Context, grantID, resourceOwne
|
||||
return nil, nil, caos_errs.ThrowNotFound(nil, "COMMAND-1My0t", "Errors.UserGrant.NotFound")
|
||||
}
|
||||
|
||||
//TODO: Remove Uniqueness
|
||||
removeUserGrant := NewUserGrantWriteModel(grantID, resourceOwner)
|
||||
userGrantAgg := UserGrantAggregateFromWriteModel(&removeUserGrant.WriteModel)
|
||||
if !cascade {
|
||||
|
@ -62,9 +62,12 @@ func (wm *UserGrantWriteModel) Reduce() error {
|
||||
}
|
||||
|
||||
func (wm *UserGrantWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, usergrant.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
ResourceOwner(wm.ResourceOwner)
|
||||
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, usergrant.AggregateType).
|
||||
AggregateIDs(wm.AggregateID)
|
||||
if wm.ResourceOwner != "" {
|
||||
query.ResourceOwner(wm.ResourceOwner)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
func UserGrantAggregateFromWriteModel(wm *eventstore.WriteModel) *usergrant.Aggregate {
|
||||
|
@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
@ -27,9 +28,6 @@ func (r *CommandSide) AddHuman(ctx context.Context, orgID string, human *domain.
|
||||
}
|
||||
err = r.eventstore.PushAggregate(ctx, addedHuman, userAgg)
|
||||
if err != nil {
|
||||
if caos_errs.IsErrorAlreadyExists(err) {
|
||||
return nil, caos_errs.ThrowAlreadyExists(err, "COMMAND-4kSff", "Errors.User.AlreadyExists")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -43,19 +41,36 @@ func (r *CommandSide) addHuman(ctx context.Context, orgID string, human *domain.
|
||||
return r.createHuman(ctx, orgID, human, nil, false)
|
||||
}
|
||||
|
||||
func (r *CommandSide) RegisterHuman(ctx context.Context, orgID string, human *domain.Human, externalIDP *domain.ExternalIDP) (*domain.Human, error) {
|
||||
func (r *CommandSide) RegisterHuman(ctx context.Context, orgID string, human *domain.Human, externalIDP *domain.ExternalIDP, orgMemberRoles []string) (*domain.Human, error) {
|
||||
aggregates := make([]eventstore.Aggregater, 2)
|
||||
|
||||
userAgg, addedHuman, err := r.registerHuman(ctx, orgID, human, externalIDP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = r.eventstore.PushAggregate(ctx, addedHuman, userAgg)
|
||||
if err != nil {
|
||||
if caos_errs.IsErrorAlreadyExists(err) {
|
||||
return nil, caos_errs.ThrowAlreadyExists(err, "COMMAND-4kSff", "Errors.User.AlreadyExists")
|
||||
aggregates[0] = userAgg
|
||||
|
||||
orgMemberWriteModel := NewOrgMemberWriteModel(orgID, addedHuman.AggregateID)
|
||||
orgAgg := OrgAggregateFromWriteModel(&orgMemberWriteModel.WriteModel)
|
||||
if orgMemberRoles != nil {
|
||||
orgMember := &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: orgID,
|
||||
},
|
||||
UserID: userAgg.ID(),
|
||||
Roles: orgMemberRoles,
|
||||
}
|
||||
return nil, err
|
||||
r.addOrgMember(ctx, orgAgg, orgMemberWriteModel, orgMember)
|
||||
}
|
||||
|
||||
aggregates[1] = orgAgg
|
||||
|
||||
eventReader, err := r.eventstore.PushAggregates(ctx, aggregates...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addedHuman.AppendEvents(eventReader...)
|
||||
addedHuman.Reduce()
|
||||
return writeModelToHuman(addedHuman), nil
|
||||
}
|
||||
|
||||
@ -82,7 +97,7 @@ func (r *CommandSide) createHuman(ctx context.Context, orgID string, human *doma
|
||||
}
|
||||
|
||||
addedHuman := NewHumanWriteModel(human.AggregateID, orgID)
|
||||
if err := human.CheckOrgIAMPolicy(human.Username, orgIAMPolicy); err != nil {
|
||||
if err := human.CheckOrgIAMPolicy(orgIAMPolicy); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
human.SetNamesAsDisplayname()
|
||||
@ -100,11 +115,10 @@ func (r *CommandSide) createHuman(ctx context.Context, orgID string, human *doma
|
||||
userAgg.PushEvents(createEvent)
|
||||
|
||||
if externalIDP != nil {
|
||||
if !externalIDP.IsValid() {
|
||||
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4Dj9s", "Errors.User.ExternalIDP.Invalid")
|
||||
err = r.addHumanExternalIDP(ctx, userAgg, externalIDP)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
//TODO: check if idpconfig exists
|
||||
userAgg.PushEvents(user.NewHumanExternalIDPAddedEvent(ctx, externalIDP.IDPConfigID, externalIDP.DisplayName))
|
||||
}
|
||||
if human.IsInitialState() {
|
||||
initCode, err := domain.NewInitUserCode(r.initializeUserCode)
|
||||
@ -121,7 +135,7 @@ func (r *CommandSide) createHuman(ctx context.Context, orgID string, human *doma
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
user.NewHumanPhoneCodeAddedEvent(ctx, phoneCode.Code, phoneCode.Expiry)
|
||||
userAgg.PushEvents(user.NewHumanPhoneCodeAddedEvent(ctx, phoneCode.Code, phoneCode.Expiry))
|
||||
} else if human.Phone != nil && human.PhoneNumber != "" && human.IsPhoneVerified {
|
||||
userAgg.PushEvents(user.NewHumanPhoneVerifiedEvent(ctx))
|
||||
}
|
||||
@ -129,32 +143,21 @@ func (r *CommandSide) createHuman(ctx context.Context, orgID string, human *doma
|
||||
return userAgg, addedHuman, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) ResendInitialMail(ctx context.Context, userID, email, resourceowner string) (err error) {
|
||||
func (r *CommandSide) HumanSkipMFAInit(ctx context.Context, userID, resourceowner string) (err error) {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.UserIDMissing")
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2xpX9", "Errors.User.UserIDMissing")
|
||||
}
|
||||
|
||||
existingEmail, err := r.emailWriteModel(ctx, userID, resourceowner)
|
||||
existingHuman, err := r.getHumanWriteModelByID(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9df", "Errors.User.NotFound")
|
||||
if existingHuman.UserState == domain.UserStateUnspecified || existingHuman.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-m9cV8", "Errors.User.NotFound")
|
||||
}
|
||||
if existingEmail.UserState != domain.UserStateInitial {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sd", "Errors.User.AlreadyInitialised")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
|
||||
if email != "" && existingEmail.Email != email {
|
||||
changedEvent, _ := existingEmail.NewChangedEvent(ctx, email)
|
||||
userAgg.PushEvents(changedEvent)
|
||||
}
|
||||
initCode, err := domain.NewInitUserCode(r.initializeUserCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userAgg.PushEvents(user.NewHumanInitialCodeAddedEvent(ctx, initCode.Code, initCode.Expiry))
|
||||
return r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
|
||||
userAgg := UserAggregateFromWriteModel(&existingHuman.WriteModel)
|
||||
userAgg.PushEvents(user.NewHumanMFAInitSkippedEvent(ctx))
|
||||
return r.eventstore.PushAggregate(ctx, existingHuman, userAgg)
|
||||
}
|
||||
|
||||
func createAddHumanEvent(ctx context.Context, orgID string, human *domain.Human, userLoginMustBeDomain bool) *user.HumanAddedEvent {
|
||||
@ -219,6 +222,28 @@ func createRegisterHumanEvent(ctx context.Context, orgID string, human *domain.H
|
||||
return addEvent
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumansSignOut(ctx context.Context, agentID string, userIDs []string) error {
|
||||
if agentID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M0ds", "Errors.User.UserIDMissing")
|
||||
}
|
||||
aggregates := make([]eventstore.Aggregater, len(userIDs))
|
||||
for i, userID := range userIDs {
|
||||
existingUser, err := r.getHumanWriteModelByID(ctx, userID, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
|
||||
continue
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||
userAgg.PushEvents(user.NewHumanSignedOutEvent(ctx, agentID))
|
||||
aggregates[i] = userAgg
|
||||
}
|
||||
|
||||
_, err := r.eventstore.PushAggregates(ctx, aggregates...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *CommandSide) getHumanWriteModelByID(ctx context.Context, userID, resourceowner string) (*HumanWriteModel, error) {
|
||||
humanWriteModel := NewHumanWriteModel(userID, resourceowner)
|
||||
err := r.eventstore.FilterToQueryReducer(ctx, humanWriteModel)
|
||||
|
@ -24,7 +24,7 @@ func (r *CommandSide) ChangeHumanEmail(ctx context.Context, email *domain.Email)
|
||||
}
|
||||
changedEvent, hasChanged := existingEmail.NewChangedEvent(ctx, email.EmailAddress)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.Email.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2b7fM", "Errors.User.Email.NotChanged")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
|
||||
userAgg.PushEvents(changedEvent)
|
||||
@ -60,7 +60,7 @@ func (r *CommandSide) VerifyHumanEmail(ctx context.Context, userID, code, resour
|
||||
return err
|
||||
}
|
||||
if existingCode.Code == nil || existingCode.UserState == domain.UserStateUnspecified || existingCode.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9fs", "Errors.User.Code.NotFound")
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-3n8ud", "Errors.User.Code.NotFound")
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&existingCode.WriteModel)
|
||||
@ -103,6 +103,19 @@ func (r *CommandSide) CreateHumanEmailVerificationCode(ctx context.Context, user
|
||||
return r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanEmailVerificationCodeSent(ctx context.Context, orgID, userID string) (err error) {
|
||||
existingEmail, err := r.emailWriteModel(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-6n8uH", "Errors.User.Email.NotFound")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
|
||||
userAgg.PushEvents(user.NewHumanEmailCodeSentEvent(ctx))
|
||||
return r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) emailWriteModel(ctx context.Context, userID, resourceOwner string) (writeModel *HumanEmailWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
@ -66,9 +66,12 @@ func (wm *HumanEmailWriteModel) Reduce() error {
|
||||
}
|
||||
|
||||
func (wm *HumanEmailWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
ResourceOwner(wm.ResourceOwner)
|
||||
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID)
|
||||
if wm.ResourceOwner != "" {
|
||||
query.ResourceOwner(wm.ResourceOwner)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
func (wm *HumanEmailWriteModel) NewChangedEvent(
|
||||
|
@ -3,11 +3,40 @@ package command
|
||||
import (
|
||||
"context"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
func (r *CommandSide) BulkAddedHumanExternalIDP(ctx context.Context, userID, resourceOwner string, externalIDPs []*domain.ExternalIDP) error {
|
||||
if len(externalIDPs) == 0 {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Ek9s", "Errors.User.ExternalIDP.MinimumExternalIDPNeeded")
|
||||
}
|
||||
aggregates := make([]eventstore.Aggregater, len(externalIDPs))
|
||||
|
||||
for i, externalIDP := range externalIDPs {
|
||||
externalIDPWriteModel := NewHumanExternalIDPWriteModel(userID, externalIDP.IDPConfigID, externalIDP.ExternalUserID, resourceOwner)
|
||||
userAgg := UserAggregateFromWriteModel(&externalIDPWriteModel.WriteModel)
|
||||
err := r.addHumanExternalIDP(ctx, userAgg, externalIDP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
aggregates[i] = userAgg
|
||||
}
|
||||
_, err := r.eventstore.PushAggregates(ctx, aggregates...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *CommandSide) addHumanExternalIDP(ctx context.Context, userAgg *user.Aggregate, externalIDP *domain.ExternalIDP) error {
|
||||
if !externalIDP.IsValid() {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6m9Kd", "Errors.User.ExternalIDP.Invalid")
|
||||
}
|
||||
//TODO: check if idpconfig exists
|
||||
userAgg.PushEvents(user.NewHumanExternalIDPAddedEvent(ctx, externalIDP.IDPConfigID, externalIDP.DisplayName, externalIDP.ExternalUserID))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) RemoveHumanExternalIDP(ctx context.Context, externalIDP *domain.ExternalIDP) error {
|
||||
return r.removeHumanExternalIDP(ctx, externalIDP, false)
|
||||
}
|
||||
@ -37,6 +66,24 @@ func (r *CommandSide) removeHumanExternalIDP(ctx context.Context, externalIDP *d
|
||||
return r.eventstore.PushAggregate(ctx, existingExternalIDP, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanExternalLoginChecked(ctx context.Context, orgID, userID string, authRequest *domain.AuthRequest) (err error) {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-5n8sM", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
existingHuman, err := r.getHumanWriteModelByID(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingHuman.UserState == domain.UserStateUnspecified || existingHuman.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-dn88J", "Errors.User.NotFound")
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&existingHuman.WriteModel)
|
||||
userAgg.PushEvents(user.NewHumanExternalIDPCheckSucceededEvent(ctx, authRequestDomainToAuthRequestInfo(authRequest)))
|
||||
return r.eventstore.PushAggregate(ctx, existingHuman, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) externalIDPWriteModelByID(ctx context.Context, userID, idpConfigID, externalUserID, resourceOwner string) (writeModel *HumanExternalIDPWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
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 (
|
||||
"context"
|
||||
"github.com/caos/logging"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
@ -15,14 +16,17 @@ func (r *CommandSide) AddHumanOTP(ctx context.Context, userID, resourceowner str
|
||||
}
|
||||
human, err := r.getHuman(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
logging.Log("COMMAND-DAqe1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get human for loginname")
|
||||
return nil, err
|
||||
}
|
||||
org, err := r.getOrg(ctx, human.ResourceOwner)
|
||||
if err != nil {
|
||||
logging.Log("COMMAND-Cm0ds").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get org for loginname")
|
||||
return nil, err
|
||||
}
|
||||
orgPolicy, err := r.getOrgIAMPolicy(ctx, org.AggregateID)
|
||||
if err != nil {
|
||||
logging.Log("COMMAND-y5zv9").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get org policy for loginname")
|
||||
return nil, err
|
||||
}
|
||||
otpWriteModel, err := r.otpWriteModelByID(ctx, userID, resourceowner)
|
||||
@ -58,7 +62,7 @@ func (r *CommandSide) AddHumanOTP(ctx context.Context, userID, resourceowner str
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) CheckMFAOTPSetup(ctx context.Context, userID, code, userAgentID, resourceowner string) error {
|
||||
func (r *CommandSide) HumanCheckMFAOTPSetup(ctx context.Context, userID, code, userAgentID, resourceowner string) error {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-8N9ds", "Errors.User.UserIDMissing")
|
||||
}
|
||||
@ -84,7 +88,32 @@ func (r *CommandSide) CheckMFAOTPSetup(ctx context.Context, userID, code, userAg
|
||||
return r.eventstore.PushAggregate(ctx, existingOTP, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) RemoveHumanOTP(ctx context.Context, userID, resourceOwner string) error {
|
||||
func (r *CommandSide) HumanCheckMFAOTP(ctx context.Context, userID, code, resourceowner string, authRequest *domain.AuthRequest) error {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-8N9ds", "Errors.User.UserIDMissing")
|
||||
}
|
||||
existingOTP, err := r.otpWriteModelByID(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingOTP.State != domain.MFAStateReady {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3Mif9s", "Errors.User.MFA.OTP.NotReady")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingOTP.WriteModel)
|
||||
err = domain.VerifyMFAOTP(code, existingOTP.Secret, r.multifactors.OTP.CryptoMFA)
|
||||
if err == nil {
|
||||
userAgg.PushEvents(
|
||||
user.NewHumanOTPCheckSucceededEvent(ctx, authRequestDomainToAuthRequestInfo(authRequest)),
|
||||
)
|
||||
return r.eventstore.PushAggregate(ctx, existingOTP, userAgg)
|
||||
}
|
||||
userAgg.PushEvents(user.NewHumanOTPCheckFailedEvent(ctx, authRequestDomainToAuthRequestInfo(authRequest)))
|
||||
pushErr := r.eventstore.PushAggregate(ctx, existingOTP, userAgg)
|
||||
logging.Log("COMMAND-9fj7s").OnError(pushErr).Error("error create password check failed event")
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanRemoveOTP(ctx context.Context, userID, resourceOwner string) error {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-5M0sd", "Errors.User.UserIDMissing")
|
||||
}
|
||||
|
@ -45,7 +45,10 @@ func (wm *HumanOTPWriteModel) Reduce() error {
|
||||
}
|
||||
|
||||
func (wm *HumanOTPWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
ResourceOwner(wm.ResourceOwner)
|
||||
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID)
|
||||
if wm.ResourceOwner != "" {
|
||||
query.ResourceOwner(wm.ResourceOwner)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
@ -21,7 +22,34 @@ func (r *CommandSide) SetOneTimePassword(ctx context.Context, orgID, userID, pas
|
||||
SecretString: passwordString,
|
||||
ChangeRequired: true,
|
||||
}
|
||||
return r.changePassword(ctx, orgID, userID, "", password, existingPassword)
|
||||
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
|
||||
return r.changePassword(ctx, orgID, userID, "", password, userAgg, existingPassword)
|
||||
}
|
||||
|
||||
func (r *CommandSide) SetPassword(ctx context.Context, orgID, userID, code, passwordString, userAgentID string) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
existingCode, err := r.passwordWriteModel(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if existingCode.Code == nil || existingCode.UserState == domain.UserStateUnspecified || existingCode.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9fs", "Errors.User.Code.NotFound")
|
||||
}
|
||||
|
||||
err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, r.emailVerificationCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
password := &domain.Password{
|
||||
SecretString: passwordString,
|
||||
ChangeRequired: false,
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingCode.WriteModel)
|
||||
return r.changePassword(ctx, existingCode.ResourceOwner, userID, userAgentID, password, userAgg, existingCode)
|
||||
}
|
||||
|
||||
func (r *CommandSide) ChangePassword(ctx context.Context, orgID, userID, oldPassword, newPassword, userAgentID string) (err error) {
|
||||
@ -32,7 +60,7 @@ func (r *CommandSide) ChangePassword(ctx context.Context, orgID, userID, oldPass
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingPassword.Secret != nil {
|
||||
if existingPassword.Secret == nil {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Fds3s", "Errors.User.Password.Empty")
|
||||
}
|
||||
ctx, spanPasswordComparison := tracing.NewNamedSpan(ctx, "crypto.CompareHash")
|
||||
@ -44,12 +72,14 @@ func (r *CommandSide) ChangePassword(ctx context.Context, orgID, userID, oldPass
|
||||
}
|
||||
password := &domain.Password{
|
||||
SecretString: newPassword,
|
||||
ChangeRequired: true,
|
||||
ChangeRequired: false,
|
||||
}
|
||||
return r.changePassword(ctx, orgID, userID, userAgentID, password, existingPassword)
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
|
||||
return r.changePassword(ctx, orgID, userID, userAgentID, password, userAgg, existingPassword)
|
||||
}
|
||||
|
||||
func (r *CommandSide) changePassword(ctx context.Context, orgID, userID, userAgentID string, password *domain.Password, existingPassword *HumanPasswordWriteModel) (err error) {
|
||||
func (r *CommandSide) changePassword(ctx context.Context, orgID, userID, userAgentID string, password *domain.Password, userAgg *user.Aggregate, existingPassword *HumanPasswordWriteModel) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -69,7 +99,6 @@ func (r *CommandSide) changePassword(ctx context.Context, orgID, userID, userAge
|
||||
if err := password.HashPasswordIfExisting(pwPolicy, r.userPasswordAlg); err != nil {
|
||||
return err
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
|
||||
userAgg.PushEvents(user.NewHumanPasswordChangedEvent(ctx, password.SecretCrypto, password.ChangeRequired, userAgentID))
|
||||
return r.eventstore.PushAggregate(ctx, existingPassword, userAgg)
|
||||
}
|
||||
@ -94,6 +123,54 @@ func (r *CommandSide) RequestSetPassword(ctx context.Context, userID, resourceOw
|
||||
return r.eventstore.PushAggregate(ctx, existingHuman, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) PasswordCodeSent(ctx context.Context, orgID, userID string) (err error) {
|
||||
existingPassword, err := r.passwordWriteModel(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingPassword.UserState == domain.UserStateUnspecified || existingPassword.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-3n77z", "Errors.User.NotFound")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
|
||||
userAgg.PushEvents(user.NewHumanPasswordCodeSentEvent(ctx))
|
||||
return r.eventstore.PushAggregate(ctx, existingPassword, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanCheckPassword(ctx context.Context, orgID, userID, password string, authRequest *domain.AuthRequest) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
if password == "" {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-3n8fs", "Errors.User.Password.Empty")
|
||||
}
|
||||
|
||||
existingPassword, err := r.passwordWriteModel(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingPassword.UserState == domain.UserStateUnspecified || existingPassword.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-3n77z", "Errors.User.NotFound")
|
||||
}
|
||||
|
||||
if existingPassword.Secret == nil {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-3n77z", "Errors.User.Password.NotSet")
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
|
||||
ctx, spanPasswordComparison := tracing.NewNamedSpan(ctx, "crypto.CompareHash")
|
||||
err = crypto.CompareHash(existingPassword.Secret, []byte(password), r.userPasswordAlg)
|
||||
spanPasswordComparison.EndWithError(err)
|
||||
if err == nil {
|
||||
userAgg.PushEvents(user.NewHumanPasswordCheckSucceededEvent(ctx, authRequestDomainToAuthRequestInfo(authRequest)))
|
||||
return r.eventstore.PushAggregate(ctx, existingPassword, userAgg)
|
||||
}
|
||||
|
||||
userAgg.PushEvents(user.NewHumanPasswordCheckFailedEvent(ctx, authRequestDomainToAuthRequestInfo(authRequest)))
|
||||
err = r.eventstore.PushAggregate(ctx, existingPassword, userAgg)
|
||||
logging.Log("COMMAND-9fj7s").OnError(err).Error("error create password check failed event")
|
||||
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-452ad", "Errors.User.Password.Invalid")
|
||||
}
|
||||
|
||||
func (r *CommandSide) passwordWriteModel(ctx context.Context, userID, resourceOwner string) (writeModel *HumanPasswordWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
"time"
|
||||
)
|
||||
|
||||
type HumanPasswordWriteModel struct {
|
||||
@ -13,6 +14,10 @@ type HumanPasswordWriteModel struct {
|
||||
Secret *crypto.CryptoValue
|
||||
SecretChangeRequired bool
|
||||
|
||||
Code *crypto.CryptoValue
|
||||
CodeCreationDate time.Time
|
||||
CodeExpiry time.Duration
|
||||
|
||||
UserState domain.UserState
|
||||
}
|
||||
|
||||
@ -43,6 +48,11 @@ func (wm *HumanPasswordWriteModel) Reduce() error {
|
||||
case *user.HumanPasswordChangedEvent:
|
||||
wm.Secret = e.Secret
|
||||
wm.SecretChangeRequired = e.ChangeRequired
|
||||
wm.Code = nil
|
||||
case *user.HumanPasswordCodeAddedEvent:
|
||||
wm.Code = e.Code
|
||||
wm.CodeCreationDate = e.CreationDate()
|
||||
wm.CodeExpiry = e.Expiry
|
||||
case *user.HumanEmailVerifiedEvent:
|
||||
if wm.UserState == domain.UserStateInitial {
|
||||
wm.UserState = domain.UserStateActive
|
||||
@ -55,7 +65,10 @@ func (wm *HumanPasswordWriteModel) Reduce() error {
|
||||
}
|
||||
|
||||
func (wm *HumanPasswordWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
ResourceOwner(wm.ResourceOwner)
|
||||
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID)
|
||||
if wm.ResourceOwner != "" {
|
||||
query.ResourceOwner(wm.ResourceOwner)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ func (r *CommandSide) CreateHumanPhoneVerificationCode(ctx context.Context, user
|
||||
return err
|
||||
}
|
||||
if existingPhone.State == domain.PhoneStateUnspecified || existingPhone.State == domain.PhoneStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9fs", "Errors.User.Phone.NotFound")
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-2b7Hf", "Errors.User.Phone.NotFound")
|
||||
}
|
||||
if existingPhone.IsPhoneVerified {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sf", "Errors.User.Phone.AlreadyVerified")
|
||||
@ -107,6 +107,19 @@ func (r *CommandSide) CreateHumanPhoneVerificationCode(ctx context.Context, user
|
||||
return r.eventstore.PushAggregate(ctx, existingPhone, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanPhoneVerificationCodeSent(ctx context.Context, orgID, userID string) (err error) {
|
||||
existingPhone, err := r.phoneWriteModelByID(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingPhone.State == domain.PhoneStateUnspecified || existingPhone.State == domain.PhoneStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-66n8J", "Errors.User.Phone.NotFound")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingPhone.WriteModel)
|
||||
userAgg.PushEvents(user.NewHumanPhoneCodeSentEvent(ctx))
|
||||
return r.eventstore.PushAggregate(ctx, existingPhone, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) RemoveHumanPhone(ctx context.Context, userID, resourceOwner string) error {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M0ds", "Errors.User.UserIDMissing")
|
||||
|
@ -33,7 +33,35 @@ func (r *CommandSide) getHumanPasswordlessTokens(ctx context.Context, userID, re
|
||||
return readModelToPasswordlessTokens(tokenReadModel), nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) AddHumanU2F(ctx context.Context, userID, resourceowner string, isLoginUI bool) (*domain.WebAuthNToken, error) {
|
||||
func (r *CommandSide) getHumanU2FLogin(ctx context.Context, userID, authReqID, resourceowner string) (*domain.WebAuthNLogin, error) {
|
||||
tokenReadModel := NewHumanU2FLoginReadModel(userID, authReqID, resourceowner)
|
||||
err := r.eventstore.FilterToQueryReducer(ctx, tokenReadModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tokenReadModel.State == domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-5m88U", "Errors.User.NotFound")
|
||||
}
|
||||
return &domain.WebAuthNLogin{
|
||||
Challenge: tokenReadModel.Challenge,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) getHumanPasswordlessLogin(ctx context.Context, userID, authReqID, resourceowner string) (*domain.WebAuthNLogin, error) {
|
||||
tokenReadModel := NewHumanPasswordlessLoginReadModel(userID, authReqID, resourceowner)
|
||||
err := r.eventstore.FilterToQueryReducer(ctx, tokenReadModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tokenReadModel.State == domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-fm84R", "Errors.User.NotFound")
|
||||
}
|
||||
return &domain.WebAuthNLogin{
|
||||
Challenge: tokenReadModel.Challenge,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanAddU2FSetup(ctx context.Context, userID, resourceowner string, isLoginUI bool) (*domain.WebAuthNToken, error) {
|
||||
u2fTokens, err := r.getHumanU2FTokens(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -55,7 +83,7 @@ func (r *CommandSide) AddHumanU2F(ctx context.Context, userID, resourceowner str
|
||||
return createdWebAuthN, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) AddHumanPasswordless(ctx context.Context, userID, resourceowner string, isLoginUI bool) (*domain.WebAuthNToken, error) {
|
||||
func (r *CommandSide) HumanAddPasswordlessSetup(ctx context.Context, userID, resourceowner string, isLoginUI bool) (*domain.WebAuthNToken, error) {
|
||||
passwordlessTokens, err := r.getHumanPasswordlessTokens(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -64,7 +92,7 @@ func (r *CommandSide) AddHumanPasswordless(ctx context.Context, userID, resource
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userAgg.PushEvents(usr_repo.NewHumanU2FAddedEvent(ctx, addWebAuthN.WebauthNTokenID, webAuthN.Challenge))
|
||||
userAgg.PushEvents(usr_repo.NewHumanPasswordlessAddedEvent(ctx, addWebAuthN.WebauthNTokenID, webAuthN.Challenge))
|
||||
|
||||
err = r.eventstore.PushAggregate(ctx, addWebAuthN, userAgg)
|
||||
if err != nil {
|
||||
@ -114,7 +142,7 @@ func (r *CommandSide) addHumanWebAuthN(ctx context.Context, userID, resourceowne
|
||||
return addWebAuthN, userAgg, webAuthN, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) VerifyHumanU2F(ctx context.Context, userID, resourceowner, tokenName, userAgentID string, credentialData []byte) error {
|
||||
func (r *CommandSide) HumanVerifyU2FSetup(ctx context.Context, userID, resourceowner, tokenName, userAgentID string, credentialData []byte) error {
|
||||
u2fTokens, err := r.getHumanU2FTokens(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -139,7 +167,7 @@ func (r *CommandSide) VerifyHumanU2F(ctx context.Context, userID, resourceowner,
|
||||
return r.eventstore.PushAggregate(ctx, verifyWebAuthN, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) VerifyHumanPasswordless(ctx context.Context, userID, resourceowner, tokenName, userAgentID string, credentialData []byte) error {
|
||||
func (r *CommandSide) HumanHumanPasswordlessSetup(ctx context.Context, userID, resourceowner, tokenName, userAgentID string, credentialData []byte) error {
|
||||
u2fTokens, err := r.getHumanPasswordlessTokens(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -149,7 +177,7 @@ func (r *CommandSide) VerifyHumanPasswordless(ctx context.Context, userID, resou
|
||||
return err
|
||||
}
|
||||
userAgg.PushEvents(
|
||||
usr_repo.NewHumanU2FVerifiedEvent(
|
||||
usr_repo.NewHumanPasswordlessVerifiedEvent(
|
||||
ctx,
|
||||
verifyWebAuthN.WebauthNTokenID,
|
||||
webAuthN.WebAuthNTokenName,
|
||||
@ -186,12 +214,156 @@ func (r *CommandSide) verifyHumanWebAuthN(ctx context.Context, userID, resourceo
|
||||
return verifyWebAuthN, userAgg, webAuthN, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) RemoveHumanU2F(ctx context.Context, userID, webAuthNID, resourceOwner string) error {
|
||||
func (r *CommandSide) HumanBeginU2FLogin(ctx context.Context, userID, resourceOwner string, authRequest *domain.AuthRequest, isLoginUI bool) (*domain.WebAuthNLogin, error) {
|
||||
u2fTokens, err := r.getHumanU2FTokens(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
writeModel, userAgg, webAuthNLogin, err := r.beginWebAuthNLogin(ctx, userID, resourceOwner, u2fTokens, isLoginUI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userAgg.PushEvents(
|
||||
usr_repo.NewHumanU2FBeginLoginEvent(
|
||||
ctx,
|
||||
webAuthNLogin.Challenge,
|
||||
authRequestDomainToAuthRequestInfo(authRequest),
|
||||
),
|
||||
)
|
||||
|
||||
err = r.eventstore.PushAggregate(ctx, writeModel, userAgg)
|
||||
return webAuthNLogin, err
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanBeginPasswordlessLogin(ctx context.Context, userID, resourceOwner string, authRequest *domain.AuthRequest, isLoginUI bool) (*domain.WebAuthNLogin, error) {
|
||||
u2fTokens, err := r.getHumanPasswordlessTokens(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
writeModel, userAgg, webAuthNLogin, err := r.beginWebAuthNLogin(ctx, userID, resourceOwner, u2fTokens, isLoginUI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userAgg.PushEvents(
|
||||
usr_repo.NewHumanPasswordlessBeginLoginEvent(
|
||||
ctx,
|
||||
webAuthNLogin.Challenge,
|
||||
authRequestDomainToAuthRequestInfo(authRequest),
|
||||
),
|
||||
)
|
||||
|
||||
err = r.eventstore.PushAggregate(ctx, writeModel, userAgg)
|
||||
return webAuthNLogin, err
|
||||
}
|
||||
|
||||
func (r *CommandSide) beginWebAuthNLogin(ctx context.Context, userID, resourceOwner string, tokens []*domain.WebAuthNToken, isLoginUI bool) (*HumanWebAuthNWriteModel, *usr_repo.Aggregate, *domain.WebAuthNLogin, error) {
|
||||
if userID == "" {
|
||||
return nil, nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-hh8K9", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
human, err := r.getHuman(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
webAuthNLogin, err := r.webauthn.BeginLogin(human, domain.UserVerificationRequirementDiscouraged, isLoginUI, tokens...)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
writeModel, err := r.webauthNWriteModelByID(ctx, userID, "", resourceOwner)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&writeModel.WriteModel)
|
||||
return writeModel, userAgg, webAuthNLogin, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanFinishU2FLogin(ctx context.Context, userID, resourceOwner string, credentialData []byte, authRequest *domain.AuthRequest, isLoginUI bool) error {
|
||||
webAuthNLogin, err := r.getHumanU2FLogin(ctx, userID, authRequest.ID, resourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u2fTokens, err := r.getHumanU2FTokens(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
writeModel, userAgg, token, signCount, err := r.finishWebAuthNLogin(ctx, userID, resourceOwner, credentialData, webAuthNLogin, u2fTokens, isLoginUI)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userAgg.PushEvents(
|
||||
usr_repo.NewHumanU2FSignCountChangedEvent(
|
||||
ctx,
|
||||
token.WebAuthNTokenID,
|
||||
signCount,
|
||||
),
|
||||
)
|
||||
|
||||
return r.eventstore.PushAggregate(ctx, writeModel, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanFinishPasswordlessLogin(ctx context.Context, userID, resourceOwner string, credentialData []byte, authRequest *domain.AuthRequest, isLoginUI bool) error {
|
||||
webAuthNLogin, err := r.getHumanPasswordlessLogin(ctx, userID, authRequest.ID, resourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
passwordlessTokens, err := r.getHumanPasswordlessTokens(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
writeModel, userAgg, token, signCount, err := r.finishWebAuthNLogin(ctx, userID, resourceOwner, credentialData, webAuthNLogin, passwordlessTokens, isLoginUI)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userAgg.PushEvents(
|
||||
usr_repo.NewHumanPasswordlessSignCountChangedEvent(
|
||||
ctx,
|
||||
token.WebAuthNTokenID,
|
||||
signCount,
|
||||
),
|
||||
)
|
||||
|
||||
return r.eventstore.PushAggregate(ctx, writeModel, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) finishWebAuthNLogin(ctx context.Context, userID, resourceOwner string, credentialData []byte, webAuthN *domain.WebAuthNLogin, tokens []*domain.WebAuthNToken, isLoginUI bool) (*HumanWebAuthNWriteModel, *usr_repo.Aggregate, *domain.WebAuthNToken, uint32, error) {
|
||||
if userID == "" {
|
||||
return nil, nil, nil, 0, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-hh8K9", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
human, err := r.getHuman(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, nil, nil, 0, err
|
||||
}
|
||||
keyID, signCount, err := r.webauthn.FinishLogin(human, webAuthN, credentialData, isLoginUI, tokens...)
|
||||
if err != nil && keyID == nil {
|
||||
return nil, nil, nil, 0, err
|
||||
}
|
||||
|
||||
_, token := domain.GetTokenByKeyID(tokens, keyID)
|
||||
if token == nil {
|
||||
return nil, nil, nil, 0, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3b7zs", "Errors.User.WebAuthN.NotFound")
|
||||
}
|
||||
writeModel, err := r.webauthNWriteModelByID(ctx, userID, "", resourceOwner)
|
||||
if err != nil {
|
||||
return nil, nil, nil, 0, err
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&writeModel.WriteModel)
|
||||
return writeModel, userAgg, token, signCount, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanRemoveU2F(ctx context.Context, userID, webAuthNID, resourceOwner string) error {
|
||||
event := usr_repo.NewHumanU2FRemovedEvent(ctx, webAuthNID)
|
||||
return r.removeHumanWebAuthN(ctx, userID, webAuthNID, resourceOwner, event)
|
||||
}
|
||||
|
||||
func (r *CommandSide) RemoveHumanPasswordless(ctx context.Context, userID, webAuthNID, resourceOwner string) error {
|
||||
func (r *CommandSide) HumanRemovePasswordless(ctx context.Context, userID, webAuthNID, resourceOwner string) error {
|
||||
event := usr_repo.NewHumanPasswordlessRemovedEvent(ctx, webAuthNID)
|
||||
return r.removeHumanWebAuthN(ctx, userID, webAuthNID, resourceOwner, event)
|
||||
}
|
||||
|
@ -39,6 +39,14 @@ func (wm *HumanWebAuthNWriteModel) AppendEvents(events ...eventstore.EventReader
|
||||
if wm.WebauthNTokenID == e.WebAuthNTokenID {
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
}
|
||||
case *user.HumanWebAuthNVerifiedEvent:
|
||||
if wm.WebauthNTokenID == e.WebAuthNTokenID {
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
}
|
||||
case *user.HumanWebAuthNSignCountChangedEvent:
|
||||
if wm.WebauthNTokenID == e.WebAuthNTokenID {
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
}
|
||||
case *user.HumanWebAuthNRemovedEvent:
|
||||
if wm.WebauthNTokenID == e.WebAuthNTokenID {
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
@ -56,6 +64,8 @@ func (wm *HumanWebAuthNWriteModel) Reduce() error {
|
||||
wm.appendAddedEvent(e)
|
||||
case *user.HumanWebAuthNVerifiedEvent:
|
||||
wm.appendVerifiedEvent(e)
|
||||
case *user.HumanWebAuthNSignCountChangedEvent:
|
||||
wm.SignCount = e.SignCount
|
||||
case *user.HumanWebAuthNRemovedEvent:
|
||||
wm.State = domain.MFAStateRemoved
|
||||
case *user.UserRemovedEvent:
|
||||
@ -104,34 +114,35 @@ func NewHumanU2FTokensReadModel(userID, resourceOwner string) *HumanU2FTokensRea
|
||||
}
|
||||
|
||||
func (wm *HumanU2FTokensReadModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanWebAuthNAddedEvent:
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *user.HumanWebAuthNVerifiedEvent:
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *user.HumanWebAuthNRemovedEvent:
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *user.UserRemovedEvent:
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
}
|
||||
}
|
||||
wm.WriteModel.AppendEvents(events...)
|
||||
}
|
||||
|
||||
func (wm *HumanU2FTokensReadModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanWebAuthNAddedEvent:
|
||||
case *user.HumanU2FAddedEvent:
|
||||
token := &HumanWebAuthNWriteModel{}
|
||||
token.appendAddedEvent(e)
|
||||
wm.WebAuthNTokens = append(wm.WebAuthNTokens, token)
|
||||
case *user.HumanWebAuthNVerifiedEvent:
|
||||
token.appendAddedEvent(&e.HumanWebAuthNAddedEvent)
|
||||
token.WriteModel = eventstore.WriteModel{
|
||||
AggregateID: e.AggregateID(),
|
||||
}
|
||||
replaced := false
|
||||
for i, existingTokens := range wm.WebAuthNTokens {
|
||||
if existingTokens.State == domain.MFAStateNotReady {
|
||||
wm.WebAuthNTokens[i] = token
|
||||
replaced = true
|
||||
}
|
||||
}
|
||||
if !replaced {
|
||||
wm.WebAuthNTokens = append(wm.WebAuthNTokens, token)
|
||||
}
|
||||
case *user.HumanU2FVerifiedEvent:
|
||||
idx, token := wm.WebAuthNTokenByID(e.WebAuthNTokenID)
|
||||
if idx < 0 {
|
||||
continue
|
||||
}
|
||||
token.appendVerifiedEvent(e)
|
||||
case *user.HumanWebAuthNRemovedEvent:
|
||||
token.appendVerifiedEvent(&e.HumanWebAuthNVerifiedEvent)
|
||||
case *user.HumanU2FRemovedEvent:
|
||||
idx, _ := wm.WebAuthNTokenByID(e.WebAuthNTokenID)
|
||||
if idx < 0 {
|
||||
continue
|
||||
@ -153,8 +164,7 @@ func (rm *HumanU2FTokensReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
EventTypes(
|
||||
user.HumanU2FTokenAddedType,
|
||||
user.HumanU2FTokenVerifiedType,
|
||||
user.HumanU2FTokenRemovedType,
|
||||
user.UserV1MFAOTPRemovedType)
|
||||
user.HumanU2FTokenRemovedType)
|
||||
|
||||
}
|
||||
|
||||
@ -190,17 +200,29 @@ func (wm *HumanPasswordlessTokensReadModel) AppendEvents(events ...eventstore.Ev
|
||||
func (wm *HumanPasswordlessTokensReadModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanWebAuthNAddedEvent:
|
||||
case *user.HumanPasswordlessAddedEvent:
|
||||
token := &HumanWebAuthNWriteModel{}
|
||||
token.appendAddedEvent(e)
|
||||
wm.WebAuthNTokens = append(wm.WebAuthNTokens, token)
|
||||
case *user.HumanWebAuthNVerifiedEvent:
|
||||
token.appendAddedEvent(&e.HumanWebAuthNAddedEvent)
|
||||
token.WriteModel = eventstore.WriteModel{
|
||||
AggregateID: e.AggregateID(),
|
||||
}
|
||||
replaced := false
|
||||
for i, existingTokens := range wm.WebAuthNTokens {
|
||||
if existingTokens.State == domain.MFAStateNotReady {
|
||||
wm.WebAuthNTokens[i] = token
|
||||
replaced = true
|
||||
}
|
||||
}
|
||||
if !replaced {
|
||||
wm.WebAuthNTokens = append(wm.WebAuthNTokens, token)
|
||||
}
|
||||
case *user.HumanPasswordlessVerifiedEvent:
|
||||
idx, token := wm.WebAuthNTokenByID(e.WebAuthNTokenID)
|
||||
if idx < 0 {
|
||||
continue
|
||||
}
|
||||
token.appendVerifiedEvent(e)
|
||||
case *user.HumanWebAuthNRemovedEvent:
|
||||
token.appendVerifiedEvent(&e.HumanWebAuthNVerifiedEvent)
|
||||
case *user.HumanPasswordlessRemovedEvent:
|
||||
idx, _ := wm.WebAuthNTokenByID(e.WebAuthNTokenID)
|
||||
if idx < 0 {
|
||||
continue
|
||||
@ -222,8 +244,7 @@ func (rm *HumanPasswordlessTokensReadModel) Query() *eventstore.SearchQueryBuild
|
||||
EventTypes(
|
||||
user.HumanPasswordlessTokenAddedType,
|
||||
user.HumanPasswordlessTokenVerifiedType,
|
||||
user.HumanPasswordlessTokenRemovedType,
|
||||
user.UserV1MFAOTPRemovedType)
|
||||
user.HumanPasswordlessTokenRemovedType)
|
||||
|
||||
}
|
||||
|
||||
@ -235,3 +256,114 @@ func (wm *HumanPasswordlessTokensReadModel) WebAuthNTokenByID(id string) (idx in
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
type HumanU2FLoginReadModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
AuthReqID string
|
||||
Challenge string
|
||||
State domain.UserState
|
||||
}
|
||||
|
||||
func NewHumanU2FLoginReadModel(userID, authReqID, resourceOwner string) *HumanU2FLoginReadModel {
|
||||
return &HumanU2FLoginReadModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
AuthReqID: authReqID,
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanU2FLoginReadModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanU2FBeginLoginEvent:
|
||||
if e.AuthRequestInfo.ID != wm.AuthReqID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *user.UserRemovedEvent:
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanU2FLoginReadModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanU2FBeginLoginEvent:
|
||||
wm.Challenge = e.Challenge
|
||||
wm.State = domain.UserStateActive
|
||||
case *user.UserRemovedEvent:
|
||||
wm.State = domain.UserStateDeleted
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (rm *HumanU2FLoginReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(rm.AggregateID).
|
||||
ResourceOwner(rm.ResourceOwner).
|
||||
EventTypes(
|
||||
user.HumanU2FTokenBeginLoginType,
|
||||
user.UserRemovedType,
|
||||
)
|
||||
}
|
||||
|
||||
type HumanPasswordlessLoginReadModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
AuthReqID string
|
||||
Challenge string
|
||||
State domain.UserState
|
||||
}
|
||||
|
||||
func NewHumanPasswordlessLoginReadModel(userID, authReqID, resourceOwner string) *HumanPasswordlessLoginReadModel {
|
||||
return &HumanPasswordlessLoginReadModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
AuthReqID: authReqID,
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanPasswordlessLoginReadModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanPasswordlessBeginLoginEvent:
|
||||
if e.AuthRequestInfo.ID != wm.AuthReqID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *user.UserRemovedEvent:
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanPasswordlessLoginReadModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanPasswordlessBeginLoginEvent:
|
||||
wm.Challenge = e.Challenge
|
||||
wm.State = domain.UserStateActive
|
||||
case *user.UserRemovedEvent:
|
||||
wm.State = domain.UserStateDeleted
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (rm *HumanPasswordlessLoginReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(rm.AggregateID).
|
||||
ResourceOwner(rm.ResourceOwner).
|
||||
EventTypes(
|
||||
user.HumanPasswordlessTokenBeginLoginType,
|
||||
user.UserRemovedType,
|
||||
)
|
||||
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ func (r *CommandSide) ChangeMachine(ctx context.Context, machine *domain.Machine
|
||||
|
||||
changedEvent, hasChanged := existingUser.NewChangedEvent(ctx, machine.Name, machine.Description)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.Email.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2n8vs", "Errors.User.Email.NotChanged")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||
userAgg.PushEvents(changedEvent)
|
||||
|
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:
|
||||
wm.UserName = e.UserName
|
||||
wm.UserState = domain.UserStateInitial
|
||||
case *user.HumanInitializedCheckSucceededEvent:
|
||||
wm.UserState = domain.UserStateActive
|
||||
case *user.MachineAddedEvent:
|
||||
wm.UserName = e.UserName
|
||||
wm.UserState = domain.UserStateActive
|
||||
|
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())
|
||||
}
|
||||
|
||||
func (u *Human) CheckOrgIAMPolicy(userName string, policy *OrgIAMPolicy) error {
|
||||
func (u *Human) CheckOrgIAMPolicy(policy *OrgIAMPolicy) error {
|
||||
if policy == nil {
|
||||
return caos_errors.ThrowPreconditionFailed(nil, "DOMAIN-zSH7j", "Errors.Users.OrgIamPolicyNil")
|
||||
}
|
||||
if policy.UserLoginMustBeDomain && strings.Contains(userName, "@") {
|
||||
if policy.UserLoginMustBeDomain && strings.Contains(u.Username, "@") {
|
||||
return caos_errors.ThrowPreconditionFailed(nil, "DOMAIN-se4sJ", "Errors.User.EmailAsUsernameNotAllowed")
|
||||
}
|
||||
if !policy.UserLoginMustBeDomain && u.Profile != nil && userName == "" && u.Email != nil {
|
||||
userName = u.EmailAddress
|
||||
if !policy.UserLoginMustBeDomain && u.Profile != nil && u.Username == "" && u.Email != nil {
|
||||
u.Username = u.EmailAddress
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
package domain
|
||||
|
||||
import es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
import (
|
||||
"bytes"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
)
|
||||
|
||||
type WebAuthNToken struct {
|
||||
es_models.ObjectRoot
|
||||
@ -55,3 +58,12 @@ func GetTokenToVerify(tokens []*WebAuthNToken) (int, *WebAuthNToken) {
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func GetTokenByKeyID(tokens []*WebAuthNToken, keyID []byte) (int, *WebAuthNToken) {
|
||||
for i, token := range tokens {
|
||||
if bytes.Compare(token.KeyID, keyID) == 0 {
|
||||
return i, token
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user