feat: User login commands (#1228)

* feat: change login to command side

* feat: change login to command side

* fix: fix push on user

* feat: user command side

* feat: sign out

* feat: command side login

* feat: command side login

* feat: fix register user

* feat: fix register user

* feat: fix web auth n events

* feat: add machine keys

* feat: send codes

* feat: move authrequest to domain

* feat: move authrequest to domain

* feat: webauthn working

* feat: external users

* feat: external users login

* feat: notify users

* fix: tests

* feat: cascade remove user grants on project remove

* fix: webauthn

* fix: pr requests

* fix: register human with member

* fix: fix bugs

* fix: fix bugs
This commit is contained in:
Fabi
2021-02-08 11:30:30 +01:00
committed by GitHub
parent c65331df1a
commit 320679467b
123 changed files with 2949 additions and 1212 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -89,3 +89,12 @@ const (
func (f IDPConfigStylingType) Valid() bool {
return f >= 0 && f < idpConfigStylingTypeCount
}
func (st IDPConfigStylingType) GetCSSClass() string {
switch st {
case IDPConfigStylingTypeGoogle:
return "google"
default:
return ""
}
}

View File

@@ -1,5 +1,21 @@
package domain
import (
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/eventstore/models"
"time"
)
type MachineKey struct {
models.ObjectRoot
KeyID string
Type MachineKeyType
ExpirationDate time.Time
PrivateKey []byte
PublicKey []byte
}
type MachineKeyType int32
const (
@@ -9,6 +25,33 @@ const (
keyCount
)
type MachineKeyState int32
const (
MachineKeyStateUnspecified MachineKeyState = iota
MachineKeyStateActive
MachineKeyStateRemoved
machineKeyStateCount
)
func (f MachineKeyState) Valid() bool {
return f >= 0 && f < machineKeyStateCount
}
func (f MachineKeyType) Valid() bool {
return f >= 0 && f < keyCount
}
func (key *MachineKey) GenerateNewMachineKeyPair(keySize int) error {
privateKey, publicKey, err := crypto.GenerateKeyPair(keySize)
if err != nil {
return err
}
key.PublicKey, err = crypto.PublicKeyToBytes(publicKey)
if err != nil {
return err
}
key.PrivateKey = crypto.PrivateKeyToBytes(privateKey)
return nil
}

View File

@@ -0,0 +1,149 @@
package domain
type NextStep interface {
Type() NextStepType
}
type NextStepType int32
const (
NextStepUnspecified NextStepType = iota
NextStepLogin
NextStepUserSelection
NextStepInitUser
NextStepPassword
NextStepChangePassword
NextStepInitPassword
NextStepVerifyEmail
NextStepMFAPrompt
NextStepMFAVerify
NextStepRedirectToCallback
NextStepChangeUsername
NextStepLinkUsers
NextStepExternalNotFoundOption
NextStepExternalLogin
NextStepGrantRequired
NextStepPasswordless
)
type LoginStep struct{}
func (s *LoginStep) Type() NextStepType {
return NextStepLogin
}
type SelectUserStep struct {
Users []UserSelection
}
func (s *SelectUserStep) Type() NextStepType {
return NextStepUserSelection
}
type UserSelection struct {
UserID string
DisplayName string
LoginName string
UserSessionState UserSessionState
SelectionPossible bool
}
type UserSessionState int32
const (
UserSessionStateActive UserSessionState = iota
UserSessionStateTerminated
)
type InitUserStep struct {
PasswordSet bool
}
func (s *InitUserStep) Type() NextStepType {
return NextStepInitUser
}
type ExternalNotFoundOptionStep struct{}
func (s *ExternalNotFoundOptionStep) Type() NextStepType {
return NextStepExternalNotFoundOption
}
type PasswordStep struct{}
func (s *PasswordStep) Type() NextStepType {
return NextStepPassword
}
type ExternalLoginStep struct {
SelectedIDPConfigID string
}
func (s *ExternalLoginStep) Type() NextStepType {
return NextStepExternalLogin
}
type PasswordlessStep struct{}
func (s *PasswordlessStep) Type() NextStepType {
return NextStepPasswordless
}
type ChangePasswordStep struct{}
func (s *ChangePasswordStep) Type() NextStepType {
return NextStepChangePassword
}
type InitPasswordStep struct{}
func (s *InitPasswordStep) Type() NextStepType {
return NextStepInitPassword
}
type ChangeUsernameStep struct{}
func (s *ChangeUsernameStep) Type() NextStepType {
return NextStepChangeUsername
}
type VerifyEMailStep struct{}
func (s *VerifyEMailStep) Type() NextStepType {
return NextStepVerifyEmail
}
type MFAPromptStep struct {
Required bool
MFAProviders []MFAType
}
func (s *MFAPromptStep) Type() NextStepType {
return NextStepMFAPrompt
}
type MFAVerificationStep struct {
MFAProviders []MFAType
}
func (s *MFAVerificationStep) Type() NextStepType {
return NextStepMFAVerify
}
type LinkUsersStep struct{}
func (s *LinkUsersStep) Type() NextStepType {
return NextStepLinkUsers
}
type GrantRequiredStep struct{}
func (s *GrantRequiredStep) Type() NextStepType {
return NextStepGrantRequired
}
type RedirectToCallbackStep struct{}
func (s *RedirectToCallbackStep) Type() NextStepType {
return NextStepRedirectToCallback
}

View File

@@ -0,0 +1,17 @@
package domain
type OIDCCodeChallenge struct {
Challenge string
Method OIDCCodeChallengeMethod
}
func (c *OIDCCodeChallenge) IsValid() bool {
return c.Challenge != ""
}
type OIDCCodeChallengeMethod int32
const (
CodeChallengeMethodPlain OIDCCodeChallengeMethod = iota
CodeChallengeMethodS256
)

View File

@@ -8,7 +8,7 @@ type LoginPolicy struct {
Default bool
AllowUsernamePassword bool
AllowRegister bool
AllowExternalIdp bool
AllowExternalIDP bool
IDPProviders []*IDPProvider
ForceMFA bool
SecondFactors []SecondFactorType
@@ -20,6 +20,11 @@ type IDPProvider struct {
models.ObjectRoot
Type IdentityProviderType
IDPConfigID string
Name string
StylingType IDPConfigStylingType
IDPConfigType IDPConfigType
IDPState IDPConfigState
}
type PasswordlessType int32
@@ -34,3 +39,11 @@ const (
func (f PasswordlessType) Valid() bool {
return f >= 0 && f < passwordlessCount
}
func (p *LoginPolicy) HasSecondFactors() bool {
return len(p.SecondFactors) > 0
}
func (p *LoginPolicy) HasMultiFactors() bool {
return len(p.MultiFactors) > 0
}

View File

@@ -0,0 +1,54 @@
package domain
const (
OrgDomainPrimaryScope = "urn:zitadel:iam:org:domain:primary:"
OrgDomainPrimaryClaim = "urn:zitadel:iam:org:domain:primary"
ProjectIDScope = "urn:zitadel:iam:org:project:id:"
AudSuffix = ":aud"
)
//TODO: Change AuthRequest to interface and let oidcauthreqesut implement it
type Request interface {
Type() AuthRequestType
IsValid() bool
}
type AuthRequestType int32
var (
authRequestTypeMapping = map[AuthRequestType]Request{
AuthRequestTypeOIDC: &AuthRequestOIDC{},
}
)
const (
AuthRequestTypeOIDC AuthRequestType = iota
AuthRequestTypeSAML
)
type AuthRequestOIDC struct {
Scopes []string
ResponseType OIDCResponseType
Nonce string
CodeChallenge *OIDCCodeChallenge
}
func (a *AuthRequestOIDC) Type() AuthRequestType {
return AuthRequestTypeOIDC
}
func (a *AuthRequestOIDC) IsValid() bool {
return len(a.Scopes) > 0 &&
a.CodeChallenge == nil || a.CodeChallenge != nil && a.CodeChallenge.IsValid()
}
type AuthRequestSAML struct {
}
func (a *AuthRequestSAML) Type() AuthRequestType {
return AuthRequestTypeSAML
}
func (a *AuthRequestSAML) IsValid() bool {
return true
}

View File

@@ -2,6 +2,7 @@ package domain
const (
RoleOrgOwner = "ORG_OWNER"
RoleOrgProjectCreator = "ORG_PROJECT_CREATOR"
RoleIAMOwner = "IAM_OWNER"
RoleProjectOwner = "PROJECT_OWNER"
RoleProjectOwnerGlobal = "PROJECT_OWNER_GLOBAL"

View File

@@ -0,0 +1,18 @@
package domain
import (
es_models "github.com/caos/zitadel/internal/eventstore/models"
"time"
)
type Token struct {
es_models.ObjectRoot
TokenID string
ApplicationID string
UserAgentID string
Audience []string
Expiration time.Time
Scopes []string
PreferredLanguage string
}

View File

@@ -2,6 +2,7 @@ package query
import (
"context"
usr_repo "github.com/caos/zitadel/internal/v2/repository/user"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
@@ -31,6 +32,7 @@ func StartQuerySide(config *Config) (repo *QuerySide, err error) {
idGenerator: id.SonyFlakeGenerator,
}
iam_repo.RegisterEventMappers(repo.eventstore)
usr_repo.RegisterEventMappers(repo.eventstore)
repo.secretCrypto, err = crypto.NewAESCrypto(config.SystemDefaults.IDPConfigVerificationKey)
if err != nil {

11
internal/v2/query/user.go Normal file
View File

@@ -0,0 +1,11 @@
package query
import (
"context"
"github.com/caos/zitadel/internal/eventstore/v2"
)
func (q *QuerySide) UserEvents(ctx context.Context, orgID, userID string, sequence uint64) ([]eventstore.EventReader, error) {
query := NewUserEventSearchQuery(userID, orgID, sequence)
return q.eventstore.FilterEvents(ctx, query)
}

View File

@@ -62,3 +62,8 @@ func UserAggregateFromReadModel(rm *UserReadModel) *user.Aggregate {
),
}
}
func NewUserEventSearchQuery(userID, orgID string, sequence uint64) *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(userID).ResourceOwner(orgID).SequenceGreater(sequence)
}

View File

@@ -22,7 +22,7 @@ type OIDCConfigAddedEvent struct {
ClientID string `json:"clientId,omitempty"`
ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"`
Issuer string `json:"issuer,omitempty"`
Scopes []string `json:"scpoes,omitempty"`
Scopes []string `json:"scopes,omitempty"`
IDPDisplayNameMapping domain.OIDCMappingField `json:"idpDisplayNameMapping,omitempty"`
UserNameMapping domain.OIDCMappingField `json:"usernameMapping,omitempty"`
@@ -80,7 +80,7 @@ type OIDCConfigChangedEvent struct {
ClientID *string `json:"clientId,omitempty"`
ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"`
Issuer *string `json:"issuer,omitempty"`
Scopes []string `json:"scpoes,omitempty"`
Scopes []string `json:"scopes,omitempty"`
IDPDisplayNameMapping *domain.OIDCMappingField `json:"idpDisplayNameMapping,omitempty"`
UserNameMapping *domain.OIDCMappingField `json:"usernameMapping,omitempty"`

View File

@@ -0,0 +1,16 @@
package user
import "net"
type AuthRequestInfo struct {
ID string `json:"id,omitempty"`
UserAgentID string `json:"userAgentID,omitempty"`
SelectedIDPConfigID string `json:"selectedIDPConfigID,omitempty"`
*BrowserInfo
}
type BrowserInfo struct {
UserAgent string `json:"userAgent,omitempty"`
AcceptLanguage string `json:"acceptLanguage,omitempty"`
RemoteIP net.IP `json:"remoteIP,omitempty"`
}

View File

@@ -80,20 +80,20 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
RegisterFilterEventMapper(HumanMFAOTPRemovedType, HumanOTPRemovedEventMapper).
RegisterFilterEventMapper(HumanMFAOTPCheckSucceededType, HumanOTPCheckSucceededEventMapper).
RegisterFilterEventMapper(HumanMFAOTPCheckFailedType, HumanOTPCheckFailedEventMapper).
RegisterFilterEventMapper(HumanU2FTokenAddedType, WebAuthNAddedEventMapper).
RegisterFilterEventMapper(HumanU2FTokenVerifiedType, HumanWebAuthNVerifiedEventMapper).
RegisterFilterEventMapper(HumanU2FTokenSignCountChangedType, HumanWebAuthNSignCountChangedEventMapper).
RegisterFilterEventMapper(HumanU2FTokenRemovedType, HumanWebAuthNRemovedEventMapper).
RegisterFilterEventMapper(HumanU2FTokenBeginLoginType, HumanWebAuthNBeginLoginEventMapper).
RegisterFilterEventMapper(HumanU2FTokenCheckSucceededType, HumanWebAuthNCheckSucceededEventMapper).
RegisterFilterEventMapper(HumanU2FTokenCheckFailedType, HumanWebAuthNCheckFailedEventMapper).
RegisterFilterEventMapper(HumanPasswordlessTokenAddedType, WebAuthNAddedEventMapper).
RegisterFilterEventMapper(HumanPasswordlessTokenVerifiedType, HumanWebAuthNVerifiedEventMapper).
RegisterFilterEventMapper(HumanPasswordlessTokenSignCountChangedType, HumanWebAuthNSignCountChangedEventMapper).
RegisterFilterEventMapper(HumanPasswordlessTokenRemovedType, HumanWebAuthNRemovedEventMapper).
RegisterFilterEventMapper(HumanPasswordlessTokenBeginLoginType, HumanWebAuthNBeginLoginEventMapper).
RegisterFilterEventMapper(HumanPasswordlessTokenCheckSucceededType, HumanWebAuthNCheckSucceededEventMapper).
RegisterFilterEventMapper(HumanPasswordlessTokenCheckFailedType, HumanWebAuthNCheckFailedEventMapper).
RegisterFilterEventMapper(HumanU2FTokenAddedType, HumanU2FAddedEventMapper).
RegisterFilterEventMapper(HumanU2FTokenVerifiedType, HumanU2FVerifiedEventMapper).
RegisterFilterEventMapper(HumanU2FTokenSignCountChangedType, HumanU2FSignCountChangedEventMapper).
RegisterFilterEventMapper(HumanU2FTokenRemovedType, HumanU2FRemovedEventMapper).
RegisterFilterEventMapper(HumanU2FTokenBeginLoginType, HumanU2FBeginLoginEventMapper).
RegisterFilterEventMapper(HumanU2FTokenCheckSucceededType, HumanU2FCheckSucceededEventMapper).
RegisterFilterEventMapper(HumanU2FTokenCheckFailedType, HumanU2FCheckFailedEventMapper).
RegisterFilterEventMapper(HumanPasswordlessTokenAddedType, HumanPasswordlessAddedEventMapper).
RegisterFilterEventMapper(HumanPasswordlessTokenVerifiedType, HumanPasswordlessVerifiedEventMapper).
RegisterFilterEventMapper(HumanPasswordlessTokenSignCountChangedType, HumanPasswordlessSignCountChangedEventMapper).
RegisterFilterEventMapper(HumanPasswordlessTokenRemovedType, HumanPasswordlessRemovedEventMapper).
RegisterFilterEventMapper(HumanPasswordlessTokenBeginLoginType, HumanPasswordlessBeginLoginEventMapper).
RegisterFilterEventMapper(HumanPasswordlessTokenCheckSucceededType, HumanPasswordlessCheckSucceededEventMapper).
RegisterFilterEventMapper(HumanPasswordlessTokenCheckFailedType, HumanPasswordlessCheckFailedEventMapper).
RegisterFilterEventMapper(MachineAddedEventType, MachineAddedEventMapper).
RegisterFilterEventMapper(MachineChangedEventType, MachineChangedEventMapper).
RegisterFilterEventMapper(MachineKeyAddedEventType, MachineKeyAddedEventMapper).

View File

@@ -359,6 +359,8 @@ func HumanInitializedCheckFailedEventMapper(event *repository.Event) (eventstore
type HumanSignedOutEvent struct {
eventstore.BaseEvent `json:"-"`
UserAgentID string `json:"userAgentID"`
}
func (e *HumanSignedOutEvent) Data() interface{} {
@@ -369,12 +371,13 @@ func (e *HumanSignedOutEvent) UniqueConstraints() []*eventstore.EventUniqueConst
return nil
}
func NewHumanSignedOutEvent(ctx context.Context) *HumanSignedOutEvent {
func NewHumanSignedOutEvent(ctx context.Context, userAgentID string) *HumanSignedOutEvent {
return &HumanSignedOutEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanSignedOutType,
),
UserAgentID: userAgentID,
}
}

View File

@@ -13,10 +13,6 @@ const (
externalIDPEventPrefix = humanEventPrefix + "externalidp."
externalLoginEventPrefix = humanEventPrefix + "externallogin."
//TODO: Handle unique Aggregate
HumanExternalIDPReservedType = externalIDPEventPrefix + "reserved"
HumanExternalIDPReleasedType = externalIDPEventPrefix + "released"
HumanExternalIDPAddedType = externalIDPEventPrefix + "added"
HumanExternalIDPRemovedType = externalIDPEventPrefix + "removed"
HumanExternalIDPCascadeRemovedType = externalIDPEventPrefix + "cascade.removed"
@@ -53,7 +49,7 @@ func (e *HumanExternalIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniq
return []*eventstore.EventUniqueConstraint{NewAddExternalIDPUniqueConstraint(e.IDPConfigID, e.UserID)}
}
func NewHumanExternalIDPAddedEvent(ctx context.Context, idpConfigID, displayName string) *HumanExternalIDPAddedEvent {
func NewHumanExternalIDPAddedEvent(ctx context.Context, idpConfigID, displayName, externalUserID string) *HumanExternalIDPAddedEvent {
return &HumanExternalIDPAddedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
@@ -61,6 +57,7 @@ func NewHumanExternalIDPAddedEvent(ctx context.Context, idpConfigID, displayName
),
IDPConfigID: idpConfigID,
DisplayName: displayName,
UserID: externalUserID,
}
}
@@ -157,27 +154,36 @@ func HumanExternalIDPCascadeRemovedEventMapper(event *repository.Event) (eventst
type HumanExternalIDPCheckSucceededEvent struct {
eventstore.BaseEvent `json:"-"`
*AuthRequestInfo
}
func (e *HumanExternalIDPCheckSucceededEvent) Data() interface{} {
return nil
return e
}
func (e *HumanExternalIDPCheckSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewHumanExternalIDPCheckSucceededEvent(ctx context.Context) *HumanExternalIDPCheckSucceededEvent {
func NewHumanExternalIDPCheckSucceededEvent(ctx context.Context, info *AuthRequestInfo) *HumanExternalIDPCheckSucceededEvent {
return &HumanExternalIDPCheckSucceededEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanExternalLoginCheckSucceededType,
),
AuthRequestInfo: info,
}
}
func HumanExternalIDPCheckSucceededEventMapper(event *repository.Event) (eventstore.EventReader, error) {
return &HumanExternalIDPCheckSucceededEvent{
e := &HumanExternalIDPCheckSucceededEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}, nil
}
err := json.Unmarshal(event.Data, e)
if err != nil {
return nil, errors.ThrowInternal(err, "USER-2M0sd", "unable to unmarshal user external idp check succeeded")
}
return e, nil
}

View File

@@ -112,54 +112,68 @@ func HumanOTPRemovedEventMapper(event *repository.Event) (eventstore.EventReader
type HumanOTPCheckSucceededEvent struct {
eventstore.BaseEvent `json:"-"`
*AuthRequestInfo
}
func (e *HumanOTPCheckSucceededEvent) Data() interface{} {
return nil
return e
}
func (e *HumanOTPCheckSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewHumanOTPCheckSucceededEvent(ctx context.Context) *HumanOTPCheckSucceededEvent {
func NewHumanOTPCheckSucceededEvent(ctx context.Context, info *AuthRequestInfo) *HumanOTPCheckSucceededEvent {
return &HumanOTPCheckSucceededEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanMFAOTPCheckSucceededType,
),
AuthRequestInfo: info,
}
}
func HumanOTPCheckSucceededEventMapper(event *repository.Event) (eventstore.EventReader, error) {
return &HumanOTPCheckSucceededEvent{
otpAdded := &HumanOTPCheckSucceededEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}, nil
}
err := json.Unmarshal(event.Data, otpAdded)
if err != nil {
return nil, errors.ThrowInternal(err, "USER-Ns9df", "unable to unmarshal human otp check succeeded")
}
return otpAdded, nil
}
type HumanOTPCheckFailedEvent struct {
eventstore.BaseEvent `json:"-"`
*AuthRequestInfo
}
func (e *HumanOTPCheckFailedEvent) Data() interface{} {
return nil
return e
}
func (e *HumanOTPCheckFailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewHumanOTPCheckFailedEvent(ctx context.Context) *HumanOTPCheckFailedEvent {
func NewHumanOTPCheckFailedEvent(ctx context.Context, info *AuthRequestInfo) *HumanOTPCheckFailedEvent {
return &HumanOTPCheckFailedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanMFAOTPCheckFailedType,
),
AuthRequestInfo: info,
}
}
func HumanOTPCheckFailedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
return &HumanOTPCheckFailedEvent{
otpAdded := &HumanOTPCheckFailedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}, nil
}
err := json.Unmarshal(event.Data, otpAdded)
if err != nil {
return nil, errors.ThrowInternal(err, "USER-Ns9df", "unable to unmarshal human otp check failed")
}
return otpAdded, nil
}

View File

@@ -0,0 +1,224 @@
package user
import (
"context"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/eventstore/v2/repository"
)
const (
passwordlessEventPrefix = humanEventPrefix + "passwordless.token."
HumanPasswordlessTokenAddedType = passwordlessEventPrefix + "added"
HumanPasswordlessTokenVerifiedType = passwordlessEventPrefix + "verified"
HumanPasswordlessTokenSignCountChangedType = passwordlessEventPrefix + "signcount.changed"
HumanPasswordlessTokenRemovedType = passwordlessEventPrefix + "removed"
HumanPasswordlessTokenBeginLoginType = passwordlessEventPrefix + "begin.login"
HumanPasswordlessTokenCheckSucceededType = passwordlessEventPrefix + "check.succeeded"
HumanPasswordlessTokenCheckFailedType = passwordlessEventPrefix + "check.failed"
)
type HumanPasswordlessAddedEvent struct {
HumanWebAuthNAddedEvent
}
func NewHumanPasswordlessAddedEvent(
ctx context.Context,
webAuthNTokenID,
challenge string,
) *HumanPasswordlessAddedEvent {
return &HumanPasswordlessAddedEvent{
HumanWebAuthNAddedEvent: *NewHumanWebAuthNAddedEvent(
eventstore.NewBaseEventForPush(
ctx,
HumanPasswordlessTokenAddedType,
),
webAuthNTokenID,
challenge,
),
}
}
func HumanPasswordlessAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := HumanWebAuthNAddedEventMapper(event)
if err != nil {
return nil, err
}
return &HumanPasswordlessAddedEvent{HumanWebAuthNAddedEvent: *e.(*HumanWebAuthNAddedEvent)}, nil
}
type HumanPasswordlessVerifiedEvent struct {
HumanWebAuthNVerifiedEvent
}
func NewHumanPasswordlessVerifiedEvent(
ctx context.Context,
webAuthNTokenID,
webAuthNTokenName,
attestationType string,
keyID,
publicKey,
aaguid []byte,
signCount uint32,
) *HumanPasswordlessVerifiedEvent {
return &HumanPasswordlessVerifiedEvent{
HumanWebAuthNVerifiedEvent: *NewHumanWebAuthNVerifiedEvent(
eventstore.NewBaseEventForPush(
ctx,
HumanPasswordlessTokenVerifiedType,
),
webAuthNTokenID,
webAuthNTokenName,
attestationType,
keyID,
publicKey,
aaguid,
signCount,
),
}
}
func HumanPasswordlessVerifiedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := HumanWebAuthNVerifiedEventMapper(event)
if err != nil {
return nil, err
}
return &HumanPasswordlessVerifiedEvent{HumanWebAuthNVerifiedEvent: *e.(*HumanWebAuthNVerifiedEvent)}, nil
}
type HumanPasswordlessSignCountChangedEvent struct {
HumanWebAuthNSignCountChangedEvent
}
func NewHumanPasswordlessSignCountChangedEvent(
ctx context.Context,
webAuthNTokenID string,
signCount uint32,
) *HumanPasswordlessSignCountChangedEvent {
return &HumanPasswordlessSignCountChangedEvent{
HumanWebAuthNSignCountChangedEvent: *NewHumanWebAuthNSignCountChangedEvent(
eventstore.NewBaseEventForPush(
ctx,
HumanPasswordlessTokenSignCountChangedType,
),
webAuthNTokenID,
signCount,
),
}
}
func HumanPasswordlessSignCountChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := HumanWebAuthNSignCountChangedEventMapper(event)
if err != nil {
return nil, err
}
return &HumanPasswordlessSignCountChangedEvent{HumanWebAuthNSignCountChangedEvent: *e.(*HumanWebAuthNSignCountChangedEvent)}, nil
}
type HumanPasswordlessRemovedEvent struct {
HumanWebAuthNRemovedEvent
}
func NewHumanPasswordlessRemovedEvent(
ctx context.Context,
webAuthNTokenID string,
) *HumanPasswordlessRemovedEvent {
return &HumanPasswordlessRemovedEvent{
HumanWebAuthNRemovedEvent: *NewHumanWebAuthNRemovedEvent(
eventstore.NewBaseEventForPush(
ctx,
HumanPasswordlessTokenRemovedType,
),
webAuthNTokenID,
),
}
}
func HumanPasswordlessRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := HumanWebAuthNRemovedEventMapper(event)
if err != nil {
return nil, err
}
return &HumanPasswordlessRemovedEvent{HumanWebAuthNRemovedEvent: *e.(*HumanWebAuthNRemovedEvent)}, nil
}
type HumanPasswordlessBeginLoginEvent struct {
HumanWebAuthNBeginLoginEvent
}
func NewHumanPasswordlessBeginLoginEvent(
ctx context.Context,
challenge string,
info *AuthRequestInfo,
) *HumanPasswordlessBeginLoginEvent {
return &HumanPasswordlessBeginLoginEvent{
HumanWebAuthNBeginLoginEvent: *NewHumanWebAuthNBeginLoginEvent(
eventstore.NewBaseEventForPush(
ctx,
HumanPasswordlessTokenVerifiedType,
),
challenge,
info,
),
}
}
func HumanPasswordlessBeginLoginEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := HumanWebAuthNBeginLoginEventMapper(event)
if err != nil {
return nil, err
}
return &HumanPasswordlessBeginLoginEvent{HumanWebAuthNBeginLoginEvent: *e.(*HumanWebAuthNBeginLoginEvent)}, nil
}
type HumanPasswordlessCheckSucceededEvent struct {
HumanWebAuthNCheckSucceededEvent
}
func NewHumanPasswordlessCheckSucceededEvent(ctx context.Context) *HumanPasswordlessCheckSucceededEvent {
return &HumanPasswordlessCheckSucceededEvent{
HumanWebAuthNCheckSucceededEvent: *NewHumanWebAuthNCheckSucceededEvent(
eventstore.NewBaseEventForPush(
ctx,
HumanPasswordlessTokenCheckSucceededType,
),
),
}
}
func HumanPasswordlessCheckSucceededEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := HumanWebAuthNCheckSucceededEventMapper(event)
if err != nil {
return nil, err
}
return &HumanPasswordlessCheckSucceededEvent{HumanWebAuthNCheckSucceededEvent: *e.(*HumanWebAuthNCheckSucceededEvent)}, nil
}
type HumanPasswordlessCheckFailedEvent struct {
HumanWebAuthNCheckFailedEvent
}
func NewHumanPasswordlessCheckFailedEvent(ctx context.Context) *HumanPasswordlessCheckFailedEvent {
return &HumanPasswordlessCheckFailedEvent{
HumanWebAuthNCheckFailedEvent: *NewHumanWebAuthNCheckFailedEvent(
eventstore.NewBaseEventForPush(
ctx,
HumanPasswordlessTokenCheckFailedType,
),
),
}
}
func HumanPasswordlessCheckFailedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := HumanWebAuthNCheckFailedEventMapper(event)
if err != nil {
return nil, err
}
return &HumanPasswordlessCheckFailedEvent{HumanWebAuthNCheckFailedEvent: *e.(*HumanWebAuthNCheckFailedEvent)}, nil
}

View File

@@ -0,0 +1,224 @@
package user
import (
"context"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/eventstore/v2/repository"
)
const (
u2fEventPrefix = mfaEventPrefix + "u2f.token."
HumanU2FTokenAddedType = u2fEventPrefix + "added"
HumanU2FTokenVerifiedType = u2fEventPrefix + "verified"
HumanU2FTokenSignCountChangedType = u2fEventPrefix + "signcount.changed"
HumanU2FTokenRemovedType = u2fEventPrefix + "removed"
HumanU2FTokenBeginLoginType = u2fEventPrefix + "begin.login"
HumanU2FTokenCheckSucceededType = u2fEventPrefix + "check.succeeded"
HumanU2FTokenCheckFailedType = u2fEventPrefix + "check.failed"
)
type HumanU2FAddedEvent struct {
HumanWebAuthNAddedEvent
}
func NewHumanU2FAddedEvent(
ctx context.Context,
webAuthNTokenID,
challenge string,
) *HumanU2FAddedEvent {
return &HumanU2FAddedEvent{
HumanWebAuthNAddedEvent: *NewHumanWebAuthNAddedEvent(
eventstore.NewBaseEventForPush(
ctx,
HumanU2FTokenAddedType,
),
webAuthNTokenID,
challenge,
),
}
}
func HumanU2FAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := HumanWebAuthNAddedEventMapper(event)
if err != nil {
return nil, err
}
return &HumanU2FAddedEvent{HumanWebAuthNAddedEvent: *e.(*HumanWebAuthNAddedEvent)}, nil
}
type HumanU2FVerifiedEvent struct {
HumanWebAuthNVerifiedEvent
}
func NewHumanU2FVerifiedEvent(
ctx context.Context,
webAuthNTokenID,
webAuthNTokenName,
attestationType string,
keyID,
publicKey,
aaguid []byte,
signCount uint32,
) *HumanU2FVerifiedEvent {
return &HumanU2FVerifiedEvent{
HumanWebAuthNVerifiedEvent: *NewHumanWebAuthNVerifiedEvent(
eventstore.NewBaseEventForPush(
ctx,
HumanU2FTokenVerifiedType,
),
webAuthNTokenID,
webAuthNTokenName,
attestationType,
keyID,
publicKey,
aaguid,
signCount,
),
}
}
func HumanU2FVerifiedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := HumanWebAuthNVerifiedEventMapper(event)
if err != nil {
return nil, err
}
return &HumanU2FVerifiedEvent{HumanWebAuthNVerifiedEvent: *e.(*HumanWebAuthNVerifiedEvent)}, nil
}
type HumanU2FSignCountChangedEvent struct {
HumanWebAuthNSignCountChangedEvent
}
func NewHumanU2FSignCountChangedEvent(
ctx context.Context,
webAuthNTokenID string,
signCount uint32,
) *HumanU2FSignCountChangedEvent {
return &HumanU2FSignCountChangedEvent{
HumanWebAuthNSignCountChangedEvent: *NewHumanWebAuthNSignCountChangedEvent(
eventstore.NewBaseEventForPush(
ctx,
HumanU2FTokenSignCountChangedType,
),
webAuthNTokenID,
signCount,
),
}
}
func HumanU2FSignCountChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := HumanWebAuthNSignCountChangedEventMapper(event)
if err != nil {
return nil, err
}
return &HumanU2FSignCountChangedEvent{HumanWebAuthNSignCountChangedEvent: *e.(*HumanWebAuthNSignCountChangedEvent)}, nil
}
type HumanU2FRemovedEvent struct {
HumanWebAuthNRemovedEvent
}
func NewHumanU2FRemovedEvent(
ctx context.Context,
webAuthNTokenID string,
) *HumanU2FRemovedEvent {
return &HumanU2FRemovedEvent{
HumanWebAuthNRemovedEvent: *NewHumanWebAuthNRemovedEvent(
eventstore.NewBaseEventForPush(
ctx,
HumanU2FTokenRemovedType,
),
webAuthNTokenID,
),
}
}
func HumanU2FRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := HumanWebAuthNRemovedEventMapper(event)
if err != nil {
return nil, err
}
return &HumanU2FRemovedEvent{HumanWebAuthNRemovedEvent: *e.(*HumanWebAuthNRemovedEvent)}, nil
}
type HumanU2FBeginLoginEvent struct {
HumanWebAuthNBeginLoginEvent
}
func NewHumanU2FBeginLoginEvent(
ctx context.Context,
challenge string,
info *AuthRequestInfo,
) *HumanU2FBeginLoginEvent {
return &HumanU2FBeginLoginEvent{
HumanWebAuthNBeginLoginEvent: *NewHumanWebAuthNBeginLoginEvent(
eventstore.NewBaseEventForPush(
ctx,
HumanU2FTokenVerifiedType,
),
challenge,
info,
),
}
}
func HumanU2FBeginLoginEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := HumanWebAuthNBeginLoginEventMapper(event)
if err != nil {
return nil, err
}
return &HumanU2FBeginLoginEvent{HumanWebAuthNBeginLoginEvent: *e.(*HumanWebAuthNBeginLoginEvent)}, nil
}
type HumanU2FCheckSucceededEvent struct {
HumanWebAuthNCheckSucceededEvent
}
func NewHumanU2FCheckSucceededEvent(ctx context.Context) *HumanU2FCheckSucceededEvent {
return &HumanU2FCheckSucceededEvent{
HumanWebAuthNCheckSucceededEvent: *NewHumanWebAuthNCheckSucceededEvent(
eventstore.NewBaseEventForPush(
ctx,
HumanU2FTokenCheckSucceededType,
),
),
}
}
func HumanU2FCheckSucceededEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := HumanWebAuthNCheckSucceededEventMapper(event)
if err != nil {
return nil, err
}
return &HumanU2FCheckSucceededEvent{HumanWebAuthNCheckSucceededEvent: *e.(*HumanWebAuthNCheckSucceededEvent)}, nil
}
type HumanU2FCheckFailedEvent struct {
HumanWebAuthNCheckFailedEvent
}
func NewHumanU2FCheckFailedEvent(ctx context.Context) *HumanU2FCheckFailedEvent {
return &HumanU2FCheckFailedEvent{
HumanWebAuthNCheckFailedEvent: *NewHumanWebAuthNCheckFailedEvent(
eventstore.NewBaseEventForPush(
ctx,
HumanU2FTokenCheckFailedType,
),
),
}
}
func HumanU2FCheckFailedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := HumanWebAuthNCheckFailedEventMapper(event)
if err != nil {
return nil, err
}
return &HumanU2FCheckFailedEvent{HumanWebAuthNCheckFailedEvent: *e.(*HumanWebAuthNCheckFailedEvent)}, nil
}

View File

@@ -1,7 +1,6 @@
package user
import (
"context"
"encoding/json"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v2"
@@ -9,26 +8,6 @@ import (
"github.com/caos/zitadel/internal/v2/domain"
)
const (
u2fEventPrefix = mfaEventPrefix + "u2f.token."
HumanU2FTokenAddedType = u2fEventPrefix + "added"
HumanU2FTokenVerifiedType = u2fEventPrefix + "verified"
HumanU2FTokenSignCountChangedType = u2fEventPrefix + "signcount.changed"
HumanU2FTokenRemovedType = u2fEventPrefix + "removed"
HumanU2FTokenBeginLoginType = u2fEventPrefix + "begin.login"
HumanU2FTokenCheckSucceededType = u2fEventPrefix + "check.succeeded"
HumanU2FTokenCheckFailedType = u2fEventPrefix + "check.failed"
passwordlessEventPrefix = humanEventPrefix + "passwordless.token."
HumanPasswordlessTokenAddedType = passwordlessEventPrefix + "added"
HumanPasswordlessTokenVerifiedType = passwordlessEventPrefix + "verified"
HumanPasswordlessTokenSignCountChangedType = passwordlessEventPrefix + "signcount.changed"
HumanPasswordlessTokenRemovedType = passwordlessEventPrefix + "removed"
HumanPasswordlessTokenBeginLoginType = passwordlessEventPrefix + "begin.login"
HumanPasswordlessTokenCheckSucceededType = passwordlessEventPrefix + "check.succeeded"
HumanPasswordlessTokenCheckFailedType = passwordlessEventPrefix + "check.failed"
)
type HumanWebAuthNAddedEvent struct {
eventstore.BaseEvent `json:"-"`
@@ -44,37 +23,19 @@ func (e *HumanWebAuthNAddedEvent) UniqueConstraints() []*eventstore.EventUniqueC
return nil
}
func NewHumanU2FAddedEvent(
ctx context.Context,
func NewHumanWebAuthNAddedEvent(
base *eventstore.BaseEvent,
webAuthNTokenID,
challenge string,
) *HumanWebAuthNAddedEvent {
return &HumanWebAuthNAddedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanU2FTokenAddedType,
),
BaseEvent: *base,
WebAuthNTokenID: webAuthNTokenID,
Challenge: challenge,
}
}
func NewHumanPasswordlessAddedEvent(
ctx context.Context,
webAuthNTokenID,
challenge string,
) *HumanWebAuthNAddedEvent {
return &HumanWebAuthNAddedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanPasswordlessTokenAddedType,
),
WebAuthNTokenID: webAuthNTokenID,
Challenge: challenge,
}
}
func WebAuthNAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
func HumanWebAuthNAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
webAuthNAdded := &HumanWebAuthNAddedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
@@ -105,8 +66,8 @@ func (e *HumanWebAuthNVerifiedEvent) UniqueConstraints() []*eventstore.EventUniq
return nil
}
func NewHumanU2FVerifiedEvent(
ctx context.Context,
func NewHumanWebAuthNVerifiedEvent(
base *eventstore.BaseEvent,
webAuthNTokenID,
webAuthNTokenName,
attestationType string,
@@ -116,35 +77,7 @@ func NewHumanU2FVerifiedEvent(
signCount uint32,
) *HumanWebAuthNVerifiedEvent {
return &HumanWebAuthNVerifiedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanU2FTokenVerifiedType,
),
WebAuthNTokenID: webAuthNTokenID,
KeyID: keyID,
PublicKey: publicKey,
AttestationType: attestationType,
AAGUID: aaguid,
SignCount: signCount,
WebAuthNTokenName: webAuthNTokenName,
}
}
func NewHumanPasswordlessVerifiedEvent(
ctx context.Context,
webAuthNTokenID,
webAuthNTokenName,
attestationType string,
keyID,
publicKey,
aaguid []byte,
signCount uint32,
) *HumanWebAuthNVerifiedEvent {
return &HumanWebAuthNVerifiedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanPasswordlessTokenVerifiedType,
),
BaseEvent: *base,
WebAuthNTokenID: webAuthNTokenID,
KeyID: keyID,
PublicKey: publicKey,
@@ -181,31 +114,13 @@ func (e *HumanWebAuthNSignCountChangedEvent) UniqueConstraints() []*eventstore.E
return nil
}
func NewHumanU2FSignCountChangedEvent(
ctx context.Context,
func NewHumanWebAuthNSignCountChangedEvent(
base *eventstore.BaseEvent,
webAuthNTokenID string,
signCount uint32,
) *HumanWebAuthNSignCountChangedEvent {
return &HumanWebAuthNSignCountChangedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanU2FTokenSignCountChangedType,
),
WebAuthNTokenID: webAuthNTokenID,
SignCount: signCount,
}
}
func NewHumanPasswordlessSignCountChangedEvent(
ctx context.Context,
webAuthNTokenID string,
signCount uint32,
) *HumanWebAuthNSignCountChangedEvent {
return &HumanWebAuthNSignCountChangedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanPasswordlessTokenSignCountChangedType,
),
BaseEvent: *base,
WebAuthNTokenID: webAuthNTokenID,
SignCount: signCount,
}
@@ -237,28 +152,12 @@ func (e *HumanWebAuthNRemovedEvent) UniqueConstraints() []*eventstore.EventUniqu
return nil
}
func NewHumanU2FRemovedEvent(
ctx context.Context,
func NewHumanWebAuthNRemovedEvent(
base *eventstore.BaseEvent,
webAuthNTokenID string,
) *HumanWebAuthNRemovedEvent {
return &HumanWebAuthNRemovedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanU2FTokenRemovedType,
),
WebAuthNTokenID: webAuthNTokenID,
}
}
func NewHumanPasswordlessRemovedEvent(
ctx context.Context,
webAuthNTokenID string,
) *HumanWebAuthNRemovedEvent {
return &HumanWebAuthNRemovedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanPasswordlessTokenRemovedType,
),
BaseEvent: *base,
WebAuthNTokenID: webAuthNTokenID,
}
}
@@ -277,10 +176,8 @@ func HumanWebAuthNRemovedEventMapper(event *repository.Event) (eventstore.EventR
type HumanWebAuthNBeginLoginEvent struct {
eventstore.BaseEvent `json:"-"`
WebAuthNTokenID string `json:"webAuthNTokenId"`
Challenge string `json:"challenge"`
//TODO: Handle Auth Req??
//*AuthRequest
Challenge string `json:"challenge"`
*AuthRequestInfo
}
func (e *HumanWebAuthNBeginLoginEvent) Data() interface{} {
@@ -291,33 +188,15 @@ func (e *HumanWebAuthNBeginLoginEvent) UniqueConstraints() []*eventstore.EventUn
return nil
}
func NewHumanU2FBeginLoginEvent(
ctx context.Context,
webAuthNTokenID,
func NewHumanWebAuthNBeginLoginEvent(
base *eventstore.BaseEvent,
challenge string,
info *AuthRequestInfo,
) *HumanWebAuthNBeginLoginEvent {
return &HumanWebAuthNBeginLoginEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanU2FTokenRemovedType,
),
WebAuthNTokenID: webAuthNTokenID,
Challenge: challenge,
}
}
func NewHumanPasswordlessBeginLoginEvent(
ctx context.Context,
webAuthNTokenID,
challenge string,
) *HumanWebAuthNBeginLoginEvent {
return &HumanWebAuthNBeginLoginEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanPasswordlessTokenRemovedType,
),
WebAuthNTokenID: webAuthNTokenID,
BaseEvent: *base,
Challenge: challenge,
AuthRequestInfo: info,
}
}
@@ -347,21 +226,9 @@ func (e *HumanWebAuthNCheckSucceededEvent) UniqueConstraints() []*eventstore.Eve
return nil
}
func NewHumanU2FCheckSucceededEvent(ctx context.Context) *HumanWebAuthNCheckSucceededEvent {
func NewHumanWebAuthNCheckSucceededEvent(base *eventstore.BaseEvent) *HumanWebAuthNCheckSucceededEvent {
return &HumanWebAuthNCheckSucceededEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanU2FTokenCheckSucceededType,
),
}
}
func NewHumanPasswordlessCheckSucceededEvent(ctx context.Context) *HumanWebAuthNCheckSucceededEvent {
return &HumanWebAuthNCheckSucceededEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanPasswordlessTokenCheckSucceededType,
),
BaseEvent: *base,
}
}
@@ -391,21 +258,9 @@ func (e *HumanWebAuthNCheckFailedEvent) UniqueConstraints() []*eventstore.EventU
return nil
}
func NewHumanU2FCheckFailedEvent(ctx context.Context) *HumanWebAuthNCheckFailedEvent {
func NewHumanWebAuthNCheckFailedEvent(base *eventstore.BaseEvent) *HumanWebAuthNCheckFailedEvent {
return &HumanWebAuthNCheckFailedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanU2FTokenCheckFailedType,
),
}
}
func NewHumanPasswordlessCheckFailedEvent(ctx context.Context) *HumanWebAuthNCheckFailedEvent {
return &HumanWebAuthNCheckFailedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanPasswordlessTokenCheckFailedType,
),
BaseEvent: *base,
}
}

View File

@@ -139,54 +139,70 @@ func HumanPasswordCodeSentEventMapper(event *repository.Event) (eventstore.Event
type HumanPasswordCheckSucceededEvent struct {
eventstore.BaseEvent `json:"-"`
*AuthRequestInfo
}
func (e *HumanPasswordCheckSucceededEvent) Data() interface{} {
return nil
return e
}
func (e *HumanPasswordCheckSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewHumanPasswordCheckSucceededEvent(ctx context.Context) *HumanPasswordCheckSucceededEvent {
func NewHumanPasswordCheckSucceededEvent(ctx context.Context, info *AuthRequestInfo) *HumanPasswordCheckSucceededEvent {
return &HumanPasswordCheckSucceededEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanPasswordCheckSucceededType,
),
AuthRequestInfo: info,
}
}
func HumanPasswordCheckSucceededEventMapper(event *repository.Event) (eventstore.EventReader, error) {
return &HumanPasswordCheckSucceededEvent{
humanAdded := &HumanPasswordCheckSucceededEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}, nil
}
err := json.Unmarshal(event.Data, humanAdded)
if err != nil {
return nil, errors.ThrowInternal(err, "USER-5M9sd", "unable to unmarshal human password check succeeded")
}
return humanAdded, nil
}
type HumanPasswordCheckFailedEvent struct {
eventstore.BaseEvent `json:"-"`
*AuthRequestInfo
}
func (e *HumanPasswordCheckFailedEvent) Data() interface{} {
return nil
return e
}
func (e *HumanPasswordCheckFailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewHumanPasswordCheckFailedEvent(ctx context.Context) *HumanPasswordCheckFailedEvent {
func NewHumanPasswordCheckFailedEvent(ctx context.Context, info *AuthRequestInfo) *HumanPasswordCheckFailedEvent {
return &HumanPasswordCheckFailedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanPasswordCheckFailedType,
),
AuthRequestInfo: info,
}
}
func HumanPasswordCheckFailedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
return &HumanPasswordCheckFailedEvent{
humanAdded := &HumanPasswordCheckFailedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}, nil
}
err := json.Unmarshal(event.Data, humanAdded)
if err != nil {
return nil, errors.ThrowInternal(err, "USER-4m9fs", "unable to unmarshal human password check failed")
}
return humanAdded, nil
}

View File

@@ -11,7 +11,7 @@ import (
const (
uniqueUserGrant = "user_grant"
userGrantEventTypePrefix = eventstore.EventType("user.grant")
userGrantEventTypePrefix = eventstore.EventType("user.grant.")
UserGrantAddedType = userGrantEventTypePrefix + "added"
UserGrantChangedType = userGrantEventTypePrefix + "changed"
UserGrantCascadeChangedType = userGrantEventTypePrefix + "cascade.changed"
@@ -78,7 +78,7 @@ func UserGrantAddedEventMapper(event *repository.Event) (eventstore.EventReader,
err := json.Unmarshal(event.Data, e)
if err != nil {
return nil, errors.ThrowInternal(err, "UGRANT-2M9fs", "unable to unmarshal user grant")
return nil, errors.ThrowInternal(err, "UGRANT-0p9ol", "unable to unmarshal user grant")
}
return e, nil