mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:37:32 +00:00
feat: User login commands (#1228)
* feat: change login to command side * feat: change login to command side * fix: fix push on user * feat: user command side * feat: sign out * feat: command side login * feat: command side login * feat: fix register user * feat: fix register user * feat: fix web auth n events * feat: add machine keys * feat: send codes * feat: move authrequest to domain * feat: move authrequest to domain * feat: webauthn working * feat: external users * feat: external users login * feat: notify users * fix: tests * feat: cascade remove user grants on project remove * fix: webauthn * fix: pr requests * fix: register human with member * fix: fix bugs * fix: fix bugs
This commit is contained in:
@@ -38,7 +38,7 @@ func writeModelToLoginPolicy(wm *LoginPolicyWriteModel) *domain.LoginPolicy {
|
||||
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
|
||||
AllowUsernamePassword: wm.AllowUserNamePassword,
|
||||
AllowRegister: wm.AllowRegister,
|
||||
AllowExternalIdp: wm.AllowExternalIDP,
|
||||
AllowExternalIDP: wm.AllowExternalIDP,
|
||||
ForceMFA: wm.ForceMFA,
|
||||
PasswordlessType: wm.PasswordlessType,
|
||||
}
|
||||
|
@@ -108,7 +108,7 @@ func (wm *IAMIDPOIDCConfigWriteModel) NewChangedEvent(
|
||||
if userNameMapping.Valid() && wm.UserNameMapping != userNameMapping {
|
||||
changes = append(changes, idpconfig.ChangeUserNameMapping(userNameMapping))
|
||||
}
|
||||
if reflect.DeepEqual(wm.Scopes, scopes) {
|
||||
if !reflect.DeepEqual(wm.Scopes, scopes) {
|
||||
changes = append(changes, idpconfig.ChangeScopes(scopes))
|
||||
}
|
||||
if len(changes) == 0 {
|
||||
|
@@ -44,7 +44,7 @@ func (r *CommandSide) addDefaultLoginPolicy(ctx context.Context, iamAgg *iam_rep
|
||||
return caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LoginPolicy.AlreadyExists")
|
||||
}
|
||||
|
||||
iamAgg.PushEvents(iam_repo.NewLoginPolicyAddedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIdp, policy.ForceMFA, policy.PasswordlessType))
|
||||
iamAgg.PushEvents(iam_repo.NewLoginPolicyAddedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, policy.PasswordlessType))
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -72,7 +72,7 @@ func (r *CommandSide) changeDefaultLoginPolicy(ctx context.Context, iamAgg *iam_
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "IAM-M0sif", "Errors.IAM.LoginPolicy.NotFound")
|
||||
}
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIdp, policy.ForceMFA, policy.PasswordlessType)
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, policy.PasswordlessType)
|
||||
if !hasChanged {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "IAM-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged")
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ func (r *CommandSide) AddLoginPolicy(ctx context.Context, policy *domain.LoginPo
|
||||
}
|
||||
|
||||
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.WriteModel)
|
||||
orgAgg.PushEvents(org.NewLoginPolicyAddedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIdp, policy.ForceMFA, policy.PasswordlessType))
|
||||
orgAgg.PushEvents(org.NewLoginPolicyAddedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, policy.PasswordlessType))
|
||||
|
||||
err = r.eventstore.PushAggregate(ctx, addedPolicy, orgAgg)
|
||||
if err != nil {
|
||||
@@ -38,7 +38,7 @@ func (r *CommandSide) ChangeLoginPolicy(ctx context.Context, policy *domain.Logi
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "Org-M0sif", "Errors.Org.LoginPolicy.NotFound")
|
||||
}
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIdp, policy.ForceMFA, domain.PasswordlessType(policy.PasswordlessType))
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, domain.PasswordlessType(policy.PasswordlessType))
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-5M9vdd", "Errors.Org.LoginPolicy.NotChanged")
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
@@ -144,7 +145,7 @@ func (r *CommandSide) ReactivateProject(ctx context.Context, projectID string, r
|
||||
return r.eventstore.PushAggregate(ctx, existingProject, projectAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) RemoveProject(ctx context.Context, projectID, resourceOwner string) error {
|
||||
func (r *CommandSide) RemoveProject(ctx context.Context, projectID, resourceOwner string, cascadingGrantIDs ...string) error {
|
||||
if projectID == "" || resourceOwner == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-66hM9", "Errors.Project.ProjectIDMissing")
|
||||
}
|
||||
@@ -157,11 +158,21 @@ func (r *CommandSide) RemoveProject(ctx context.Context, projectID, resourceOwne
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-3M9sd", "Errors.Project.NotFound")
|
||||
}
|
||||
|
||||
aggregates := make([]eventstore.Aggregater, 0)
|
||||
projectAgg := ProjectAggregateFromWriteModel(&existingProject.WriteModel)
|
||||
projectAgg.PushEvents(project.NewProjectRemovedEvent(ctx, existingProject.Name, existingProject.ResourceOwner))
|
||||
//TODO: Remove User Grants by ProjectID
|
||||
aggregates = append(aggregates, projectAgg)
|
||||
|
||||
return r.eventstore.PushAggregate(ctx, existingProject, projectAgg)
|
||||
for _, grantID := range cascadingGrantIDs {
|
||||
grantAgg, _, err := r.removeUserGrant(ctx, grantID, "", true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
aggregates = append(aggregates, grantAgg)
|
||||
}
|
||||
|
||||
_, err = r.eventstore.PushAggregates(ctx, aggregates...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *CommandSide) getProjectWriteModelByID(ctx context.Context, projectID, resourceOwner string) (*ProjectWriteModel, error) {
|
||||
|
@@ -91,7 +91,7 @@ func (r *CommandSide) SetupStep1(ctx context.Context, step1 *Step1) error {
|
||||
&domain.LoginPolicy{
|
||||
AllowUsernamePassword: step1.DefaultLoginPolicy.AllowUsernamePassword,
|
||||
AllowRegister: step1.DefaultLoginPolicy.AllowRegister,
|
||||
AllowExternalIdp: step1.DefaultLoginPolicy.AllowExternalIdp,
|
||||
AllowExternalIDP: step1.DefaultLoginPolicy.AllowExternalIdp,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@@ -2,6 +2,10 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
auth_req_model "github.com/caos/zitadel/internal/auth_request/model"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
@@ -21,7 +25,7 @@ func (r *CommandSide) ChangeUsername(ctx context.Context, orgID, userID, userNam
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-5N9ds", "Errors.User.NotFound")
|
||||
}
|
||||
if existingUser.UserName == userName {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.UsernameNotChanged")
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6m9gs", "Errors.User.UsernameNotChanged")
|
||||
}
|
||||
|
||||
orgIAMPolicy, err := r.getOrgIAMPolicy(ctx, orgID)
|
||||
@@ -89,7 +93,7 @@ func (r *CommandSide) LockUser(ctx context.Context, userID, resourceOwner string
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-5M9fs", "Errors.User.NotFound")
|
||||
}
|
||||
if existingUser.UserState != domain.UserStateActive && existingUser.UserState != domain.UserStateInitial {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.ShouldBeActiveOrInitial")
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3NN8v", "Errors.User.ShouldBeActiveOrInitial")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||
userAgg.PushEvents(user.NewUserLockedEvent(ctx))
|
||||
@@ -139,6 +143,69 @@ func (r *CommandSide) RemoveUser(ctx context.Context, userID, resourceOwner stri
|
||||
return r.eventstore.PushAggregate(ctx, existingUser, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) CreateUserToken(ctx context.Context, orgID, agentID, clientID, userID string, audience, scopes []string, lifetime time.Duration) (*domain.Token, error) {
|
||||
if orgID == "" || userID == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-55n8M", "Errors.IDMissing")
|
||||
}
|
||||
existingUser, err := r.userWriteModelByID(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-1d6Gg", "Errors.User.NotFound")
|
||||
}
|
||||
|
||||
for _, scope := range scopes {
|
||||
if strings.HasPrefix(scope, auth_req_model.ProjectIDScope) && strings.HasSuffix(scope, auth_req_model.AudSuffix) {
|
||||
audience = append(audience, strings.TrimSuffix(strings.TrimPrefix(scope, auth_req_model.ProjectIDScope), auth_req_model.AudSuffix))
|
||||
}
|
||||
}
|
||||
|
||||
preferredLanguage := ""
|
||||
existingHuman, err := r.getHumanWriteModelByID(ctx, userID, orgID)
|
||||
if existingHuman != nil {
|
||||
preferredLanguage = existingHuman.PreferredLanguage.String()
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
tokenID, err := r.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||
userAgg.PushEvents(user.NewUserTokenAddedEvent(ctx, tokenID, clientID, agentID, preferredLanguage, audience, scopes, now.Add(lifetime)))
|
||||
|
||||
err = r.eventstore.PushAggregate(ctx, existingUser, userAgg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &domain.Token{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: userID,
|
||||
},
|
||||
TokenID: tokenID,
|
||||
UserAgentID: agentID,
|
||||
ApplicationID: clientID,
|
||||
Audience: audience,
|
||||
Scopes: scopes,
|
||||
Expiration: now.Add(lifetime),
|
||||
PreferredLanguage: preferredLanguage,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) UserDomainClaimedSent(ctx context.Context, orgID, userID string) (err error) {
|
||||
existingUser, err := r.userWriteModelByID(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-5m9gK", "Errors.User.NotFound")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||
userAgg.PushEvents(user.NewDomainClaimedSentEvent(ctx))
|
||||
return r.eventstore.PushAggregate(ctx, existingUser, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) checkUserExists(ctx context.Context, userID, resourceOwner string) error {
|
||||
userWriteModel, err := r.userWriteModelByID(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
|
@@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
func writeModelToHuman(wm *HumanWriteModel) *domain.Human {
|
||||
@@ -77,6 +78,15 @@ func writeModelToMachine(wm *MachineWriteModel) *domain.Machine {
|
||||
}
|
||||
}
|
||||
|
||||
func keyWriteModelToMachineKey(wm *MachineKeyWriteModel) *domain.MachineKey {
|
||||
return &domain.MachineKey{
|
||||
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
|
||||
KeyID: wm.KeyID,
|
||||
Type: wm.KeyType,
|
||||
ExpirationDate: wm.ExpirationDate,
|
||||
}
|
||||
}
|
||||
|
||||
func readModelToU2FTokens(wm *HumanU2FTokensReadModel) []*domain.WebAuthNToken {
|
||||
tokens := make([]*domain.WebAuthNToken, len(wm.WebAuthNTokens))
|
||||
for i, token := range wm.WebAuthNTokens {
|
||||
@@ -107,3 +117,19 @@ func writeModelToWebAuthN(wm *HumanWebAuthNWriteModel) *domain.WebAuthNToken {
|
||||
State: wm.State,
|
||||
}
|
||||
}
|
||||
|
||||
func authRequestDomainToAuthRequestInfo(authRequest *domain.AuthRequest) *user.AuthRequestInfo {
|
||||
info := &user.AuthRequestInfo{
|
||||
ID: authRequest.ID,
|
||||
UserAgentID: authRequest.AgentID,
|
||||
SelectedIDPConfigID: authRequest.SelectedIDPConfigID,
|
||||
}
|
||||
if authRequest.BrowserInfo != nil {
|
||||
info.BrowserInfo = &user.BrowserInfo{
|
||||
UserAgent: authRequest.BrowserInfo.UserAgent,
|
||||
AcceptLanguage: authRequest.BrowserInfo.AcceptLanguage,
|
||||
RemoteIP: authRequest.BrowserInfo.RemoteIP,
|
||||
}
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
@@ -189,7 +189,7 @@ func (r *CommandSide) BulkRemoveUserGrant(ctx context.Context, grantIDs []string
|
||||
}
|
||||
|
||||
func (r *CommandSide) removeUserGrant(ctx context.Context, grantID, resourceOwner string, cascade bool) (_ *usergrant.Aggregate, _ *UserGrantWriteModel, err error) {
|
||||
if grantID == "" || resourceOwner == "" {
|
||||
if grantID == "" {
|
||||
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-J9sc5", "Errors.UserGrant.IDMissing")
|
||||
}
|
||||
|
||||
@@ -205,7 +205,6 @@ func (r *CommandSide) removeUserGrant(ctx context.Context, grantID, resourceOwne
|
||||
return nil, nil, caos_errs.ThrowNotFound(nil, "COMMAND-1My0t", "Errors.UserGrant.NotFound")
|
||||
}
|
||||
|
||||
//TODO: Remove Uniqueness
|
||||
removeUserGrant := NewUserGrantWriteModel(grantID, resourceOwner)
|
||||
userGrantAgg := UserGrantAggregateFromWriteModel(&removeUserGrant.WriteModel)
|
||||
if !cascade {
|
||||
|
@@ -62,9 +62,12 @@ func (wm *UserGrantWriteModel) Reduce() error {
|
||||
}
|
||||
|
||||
func (wm *UserGrantWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, usergrant.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
ResourceOwner(wm.ResourceOwner)
|
||||
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, usergrant.AggregateType).
|
||||
AggregateIDs(wm.AggregateID)
|
||||
if wm.ResourceOwner != "" {
|
||||
query.ResourceOwner(wm.ResourceOwner)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
func UserGrantAggregateFromWriteModel(wm *eventstore.WriteModel) *usergrant.Aggregate {
|
||||
|
@@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
@@ -27,9 +28,6 @@ func (r *CommandSide) AddHuman(ctx context.Context, orgID string, human *domain.
|
||||
}
|
||||
err = r.eventstore.PushAggregate(ctx, addedHuman, userAgg)
|
||||
if err != nil {
|
||||
if caos_errs.IsErrorAlreadyExists(err) {
|
||||
return nil, caos_errs.ThrowAlreadyExists(err, "COMMAND-4kSff", "Errors.User.AlreadyExists")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -43,19 +41,36 @@ func (r *CommandSide) addHuman(ctx context.Context, orgID string, human *domain.
|
||||
return r.createHuman(ctx, orgID, human, nil, false)
|
||||
}
|
||||
|
||||
func (r *CommandSide) RegisterHuman(ctx context.Context, orgID string, human *domain.Human, externalIDP *domain.ExternalIDP) (*domain.Human, error) {
|
||||
func (r *CommandSide) RegisterHuman(ctx context.Context, orgID string, human *domain.Human, externalIDP *domain.ExternalIDP, orgMemberRoles []string) (*domain.Human, error) {
|
||||
aggregates := make([]eventstore.Aggregater, 2)
|
||||
|
||||
userAgg, addedHuman, err := r.registerHuman(ctx, orgID, human, externalIDP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = r.eventstore.PushAggregate(ctx, addedHuman, userAgg)
|
||||
if err != nil {
|
||||
if caos_errs.IsErrorAlreadyExists(err) {
|
||||
return nil, caos_errs.ThrowAlreadyExists(err, "COMMAND-4kSff", "Errors.User.AlreadyExists")
|
||||
aggregates[0] = userAgg
|
||||
|
||||
orgMemberWriteModel := NewOrgMemberWriteModel(orgID, addedHuman.AggregateID)
|
||||
orgAgg := OrgAggregateFromWriteModel(&orgMemberWriteModel.WriteModel)
|
||||
if orgMemberRoles != nil {
|
||||
orgMember := &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: orgID,
|
||||
},
|
||||
UserID: userAgg.ID(),
|
||||
Roles: orgMemberRoles,
|
||||
}
|
||||
return nil, err
|
||||
r.addOrgMember(ctx, orgAgg, orgMemberWriteModel, orgMember)
|
||||
}
|
||||
|
||||
aggregates[1] = orgAgg
|
||||
|
||||
eventReader, err := r.eventstore.PushAggregates(ctx, aggregates...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addedHuman.AppendEvents(eventReader...)
|
||||
addedHuman.Reduce()
|
||||
return writeModelToHuman(addedHuman), nil
|
||||
}
|
||||
|
||||
@@ -82,7 +97,7 @@ func (r *CommandSide) createHuman(ctx context.Context, orgID string, human *doma
|
||||
}
|
||||
|
||||
addedHuman := NewHumanWriteModel(human.AggregateID, orgID)
|
||||
if err := human.CheckOrgIAMPolicy(human.Username, orgIAMPolicy); err != nil {
|
||||
if err := human.CheckOrgIAMPolicy(orgIAMPolicy); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
human.SetNamesAsDisplayname()
|
||||
@@ -100,11 +115,10 @@ func (r *CommandSide) createHuman(ctx context.Context, orgID string, human *doma
|
||||
userAgg.PushEvents(createEvent)
|
||||
|
||||
if externalIDP != nil {
|
||||
if !externalIDP.IsValid() {
|
||||
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4Dj9s", "Errors.User.ExternalIDP.Invalid")
|
||||
err = r.addHumanExternalIDP(ctx, userAgg, externalIDP)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
//TODO: check if idpconfig exists
|
||||
userAgg.PushEvents(user.NewHumanExternalIDPAddedEvent(ctx, externalIDP.IDPConfigID, externalIDP.DisplayName))
|
||||
}
|
||||
if human.IsInitialState() {
|
||||
initCode, err := domain.NewInitUserCode(r.initializeUserCode)
|
||||
@@ -121,7 +135,7 @@ func (r *CommandSide) createHuman(ctx context.Context, orgID string, human *doma
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
user.NewHumanPhoneCodeAddedEvent(ctx, phoneCode.Code, phoneCode.Expiry)
|
||||
userAgg.PushEvents(user.NewHumanPhoneCodeAddedEvent(ctx, phoneCode.Code, phoneCode.Expiry))
|
||||
} else if human.Phone != nil && human.PhoneNumber != "" && human.IsPhoneVerified {
|
||||
userAgg.PushEvents(user.NewHumanPhoneVerifiedEvent(ctx))
|
||||
}
|
||||
@@ -129,32 +143,21 @@ func (r *CommandSide) createHuman(ctx context.Context, orgID string, human *doma
|
||||
return userAgg, addedHuman, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) ResendInitialMail(ctx context.Context, userID, email, resourceowner string) (err error) {
|
||||
func (r *CommandSide) HumanSkipMFAInit(ctx context.Context, userID, resourceowner string) (err error) {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.UserIDMissing")
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2xpX9", "Errors.User.UserIDMissing")
|
||||
}
|
||||
|
||||
existingEmail, err := r.emailWriteModel(ctx, userID, resourceowner)
|
||||
existingHuman, err := r.getHumanWriteModelByID(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9df", "Errors.User.NotFound")
|
||||
if existingHuman.UserState == domain.UserStateUnspecified || existingHuman.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-m9cV8", "Errors.User.NotFound")
|
||||
}
|
||||
if existingEmail.UserState != domain.UserStateInitial {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sd", "Errors.User.AlreadyInitialised")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
|
||||
if email != "" && existingEmail.Email != email {
|
||||
changedEvent, _ := existingEmail.NewChangedEvent(ctx, email)
|
||||
userAgg.PushEvents(changedEvent)
|
||||
}
|
||||
initCode, err := domain.NewInitUserCode(r.initializeUserCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userAgg.PushEvents(user.NewHumanInitialCodeAddedEvent(ctx, initCode.Code, initCode.Expiry))
|
||||
return r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
|
||||
userAgg := UserAggregateFromWriteModel(&existingHuman.WriteModel)
|
||||
userAgg.PushEvents(user.NewHumanMFAInitSkippedEvent(ctx))
|
||||
return r.eventstore.PushAggregate(ctx, existingHuman, userAgg)
|
||||
}
|
||||
|
||||
func createAddHumanEvent(ctx context.Context, orgID string, human *domain.Human, userLoginMustBeDomain bool) *user.HumanAddedEvent {
|
||||
@@ -219,6 +222,28 @@ func createRegisterHumanEvent(ctx context.Context, orgID string, human *domain.H
|
||||
return addEvent
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumansSignOut(ctx context.Context, agentID string, userIDs []string) error {
|
||||
if agentID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M0ds", "Errors.User.UserIDMissing")
|
||||
}
|
||||
aggregates := make([]eventstore.Aggregater, len(userIDs))
|
||||
for i, userID := range userIDs {
|
||||
existingUser, err := r.getHumanWriteModelByID(ctx, userID, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
|
||||
continue
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||
userAgg.PushEvents(user.NewHumanSignedOutEvent(ctx, agentID))
|
||||
aggregates[i] = userAgg
|
||||
}
|
||||
|
||||
_, err := r.eventstore.PushAggregates(ctx, aggregates...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *CommandSide) getHumanWriteModelByID(ctx context.Context, userID, resourceowner string) (*HumanWriteModel, error) {
|
||||
humanWriteModel := NewHumanWriteModel(userID, resourceowner)
|
||||
err := r.eventstore.FilterToQueryReducer(ctx, humanWriteModel)
|
||||
|
@@ -24,7 +24,7 @@ func (r *CommandSide) ChangeHumanEmail(ctx context.Context, email *domain.Email)
|
||||
}
|
||||
changedEvent, hasChanged := existingEmail.NewChangedEvent(ctx, email.EmailAddress)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.Email.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2b7fM", "Errors.User.Email.NotChanged")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
|
||||
userAgg.PushEvents(changedEvent)
|
||||
@@ -60,7 +60,7 @@ func (r *CommandSide) VerifyHumanEmail(ctx context.Context, userID, code, resour
|
||||
return err
|
||||
}
|
||||
if existingCode.Code == nil || existingCode.UserState == domain.UserStateUnspecified || existingCode.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9fs", "Errors.User.Code.NotFound")
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-3n8ud", "Errors.User.Code.NotFound")
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&existingCode.WriteModel)
|
||||
@@ -103,6 +103,19 @@ func (r *CommandSide) CreateHumanEmailVerificationCode(ctx context.Context, user
|
||||
return r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanEmailVerificationCodeSent(ctx context.Context, orgID, userID string) (err error) {
|
||||
existingEmail, err := r.emailWriteModel(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-6n8uH", "Errors.User.Email.NotFound")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
|
||||
userAgg.PushEvents(user.NewHumanEmailCodeSentEvent(ctx))
|
||||
return r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) emailWriteModel(ctx context.Context, userID, resourceOwner string) (writeModel *HumanEmailWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
@@ -66,9 +66,12 @@ func (wm *HumanEmailWriteModel) Reduce() error {
|
||||
}
|
||||
|
||||
func (wm *HumanEmailWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
ResourceOwner(wm.ResourceOwner)
|
||||
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID)
|
||||
if wm.ResourceOwner != "" {
|
||||
query.ResourceOwner(wm.ResourceOwner)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
func (wm *HumanEmailWriteModel) NewChangedEvent(
|
||||
|
@@ -3,11 +3,40 @@ package command
|
||||
import (
|
||||
"context"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
func (r *CommandSide) BulkAddedHumanExternalIDP(ctx context.Context, userID, resourceOwner string, externalIDPs []*domain.ExternalIDP) error {
|
||||
if len(externalIDPs) == 0 {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Ek9s", "Errors.User.ExternalIDP.MinimumExternalIDPNeeded")
|
||||
}
|
||||
aggregates := make([]eventstore.Aggregater, len(externalIDPs))
|
||||
|
||||
for i, externalIDP := range externalIDPs {
|
||||
externalIDPWriteModel := NewHumanExternalIDPWriteModel(userID, externalIDP.IDPConfigID, externalIDP.ExternalUserID, resourceOwner)
|
||||
userAgg := UserAggregateFromWriteModel(&externalIDPWriteModel.WriteModel)
|
||||
err := r.addHumanExternalIDP(ctx, userAgg, externalIDP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
aggregates[i] = userAgg
|
||||
}
|
||||
_, err := r.eventstore.PushAggregates(ctx, aggregates...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *CommandSide) addHumanExternalIDP(ctx context.Context, userAgg *user.Aggregate, externalIDP *domain.ExternalIDP) error {
|
||||
if !externalIDP.IsValid() {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6m9Kd", "Errors.User.ExternalIDP.Invalid")
|
||||
}
|
||||
//TODO: check if idpconfig exists
|
||||
userAgg.PushEvents(user.NewHumanExternalIDPAddedEvent(ctx, externalIDP.IDPConfigID, externalIDP.DisplayName, externalIDP.ExternalUserID))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) RemoveHumanExternalIDP(ctx context.Context, externalIDP *domain.ExternalIDP) error {
|
||||
return r.removeHumanExternalIDP(ctx, externalIDP, false)
|
||||
}
|
||||
@@ -37,6 +66,24 @@ func (r *CommandSide) removeHumanExternalIDP(ctx context.Context, externalIDP *d
|
||||
return r.eventstore.PushAggregate(ctx, existingExternalIDP, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanExternalLoginChecked(ctx context.Context, orgID, userID string, authRequest *domain.AuthRequest) (err error) {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-5n8sM", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
existingHuman, err := r.getHumanWriteModelByID(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingHuman.UserState == domain.UserStateUnspecified || existingHuman.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-dn88J", "Errors.User.NotFound")
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&existingHuman.WriteModel)
|
||||
userAgg.PushEvents(user.NewHumanExternalIDPCheckSucceededEvent(ctx, authRequestDomainToAuthRequestInfo(authRequest)))
|
||||
return r.eventstore.PushAggregate(ctx, existingHuman, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) externalIDPWriteModelByID(ctx context.Context, userID, idpConfigID, externalUserID, resourceOwner string) (writeModel *HumanExternalIDPWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
104
internal/v2/command/user_human_init.go
Normal file
104
internal/v2/command/user_human_init.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
//ResendInitialMail resend inital mail and changes email if provided
|
||||
func (r *CommandSide) ResendInitialMail(ctx context.Context, userID, email, resourceOwner string) (err error) {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2n8vs", "Errors.User.UserIDMissing")
|
||||
}
|
||||
|
||||
existingCode, err := r.getHumanInitWriteModelByID(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingCode.UserState == domain.UserStateUnspecified || existingCode.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9df", "Errors.User.NotFound")
|
||||
}
|
||||
if existingCode.UserState != domain.UserStateInitial {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sd", "Errors.User.AlreadyInitialised")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingCode.WriteModel)
|
||||
if email != "" && existingCode.Email != email {
|
||||
changedEvent, _ := existingCode.NewChangedEvent(ctx, email)
|
||||
userAgg.PushEvents(changedEvent)
|
||||
}
|
||||
initCode, err := domain.NewInitUserCode(r.initializeUserCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userAgg.PushEvents(user.NewHumanInitialCodeAddedEvent(ctx, initCode.Code, initCode.Expiry))
|
||||
return r.eventstore.PushAggregate(ctx, existingCode, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanVerifyInitCode(ctx context.Context, userID, resourceOwner, code, passwordString string) error {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-mkM9f", "Errors.User.UserIDMissing")
|
||||
}
|
||||
if code == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-44G8s", "Errors.User.Code.Empty")
|
||||
}
|
||||
|
||||
existingCode, err := r.getHumanInitWriteModelByID(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingCode.Code == nil || existingCode.UserState == domain.UserStateUnspecified || existingCode.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-mmn5f", "Errors.User.Code.NotFound")
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&existingCode.WriteModel)
|
||||
err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, r.initializeUserCode)
|
||||
if err != nil {
|
||||
userAgg.PushEvents(user.NewHumanInitializedCheckFailedEvent(ctx))
|
||||
err = r.eventstore.PushAggregate(ctx, existingCode, userAgg)
|
||||
logging.LogWithFields("COMMAND-Dg2z5", "userID", userAgg.ID()).OnError(err).Error("NewHumanInitializedCheckFailedEvent push failed")
|
||||
return caos_errs.ThrowInvalidArgument(err, "COMMAND-11v6G", "Errors.User.Code.Invalid")
|
||||
}
|
||||
|
||||
userAgg.PushEvents(user.NewHumanInitializedCheckSucceededEvent(ctx))
|
||||
if !existingCode.IsEmailVerified {
|
||||
userAgg.PushEvents(user.NewHumanEmailVerifiedEvent(ctx))
|
||||
}
|
||||
if passwordString != "" {
|
||||
passwordWriteModel := NewHumanPasswordWriteModel(userID, existingCode.ResourceOwner)
|
||||
password := &domain.Password{
|
||||
SecretString: passwordString,
|
||||
ChangeRequired: false,
|
||||
}
|
||||
err = r.changePassword(ctx, existingCode.ResourceOwner, userID, "", password, userAgg, passwordWriteModel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return r.eventstore.PushAggregate(ctx, existingCode, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanInitCodeSent(ctx context.Context, orgID, userID string) (err error) {
|
||||
existingInitCode, err := r.getHumanInitWriteModelByID(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingInitCode.UserState == domain.UserStateUnspecified || existingInitCode.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-556zg", "Errors.User.Code.NotFound")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingInitCode.WriteModel)
|
||||
userAgg.PushEvents(user.NewHumanInitialCodeSentEvent(ctx))
|
||||
return r.eventstore.PushAggregate(ctx, existingInitCode, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) getHumanInitWriteModelByID(ctx context.Context, userID, resourceowner string) (*HumanInitCodeWriteModel, error) {
|
||||
initWriteModel := NewHumanInitCodeWriteModel(userID, resourceowner)
|
||||
err := r.eventstore.FilterToQueryReducer(ctx, initWriteModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return initWriteModel, nil
|
||||
}
|
89
internal/v2/command/user_human_init_model.go
Normal file
89
internal/v2/command/user_human_init_model.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
"time"
|
||||
)
|
||||
|
||||
type HumanInitCodeWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
Email string
|
||||
IsEmailVerified bool
|
||||
|
||||
Code *crypto.CryptoValue
|
||||
CodeCreationDate time.Time
|
||||
CodeExpiry time.Duration
|
||||
|
||||
UserState domain.UserState
|
||||
}
|
||||
|
||||
func NewHumanInitCodeWriteModel(userID, resourceOwner string) *HumanInitCodeWriteModel {
|
||||
return &HumanInitCodeWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanInitCodeWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
wm.WriteModel.AppendEvents(events...)
|
||||
}
|
||||
|
||||
func (wm *HumanInitCodeWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanAddedEvent:
|
||||
wm.Email = e.EmailAddress
|
||||
wm.UserState = domain.UserStateInitial
|
||||
case *user.HumanRegisteredEvent:
|
||||
wm.Email = e.EmailAddress
|
||||
wm.UserState = domain.UserStateInitial
|
||||
case *user.HumanEmailChangedEvent:
|
||||
wm.Email = e.EmailAddress
|
||||
wm.IsEmailVerified = false
|
||||
case *user.HumanEmailVerifiedEvent:
|
||||
wm.IsEmailVerified = true
|
||||
wm.Code = nil
|
||||
if wm.UserState == domain.UserStateInitial {
|
||||
wm.UserState = domain.UserStateActive
|
||||
}
|
||||
case *user.HumanInitialCodeAddedEvent:
|
||||
wm.Code = e.Code
|
||||
wm.CodeCreationDate = e.CreationDate()
|
||||
wm.CodeExpiry = e.Expiry
|
||||
case *user.HumanInitializedCheckSucceededEvent:
|
||||
wm.Code = nil
|
||||
case *user.UserRemovedEvent:
|
||||
wm.UserState = domain.UserStateDeleted
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *HumanInitCodeWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID)
|
||||
if wm.ResourceOwner != "" {
|
||||
query.ResourceOwner(wm.ResourceOwner)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
func (wm *HumanInitCodeWriteModel) NewChangedEvent(
|
||||
ctx context.Context,
|
||||
email string,
|
||||
) (*user.HumanEmailChangedEvent, bool) {
|
||||
hasChanged := false
|
||||
changedEvent := user.NewHumanEmailChangedEvent(ctx)
|
||||
if wm.Email != email {
|
||||
hasChanged = true
|
||||
changedEvent.EmailAddress = email
|
||||
}
|
||||
return changedEvent, hasChanged
|
||||
}
|
@@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/logging"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
@@ -15,14 +16,17 @@ func (r *CommandSide) AddHumanOTP(ctx context.Context, userID, resourceowner str
|
||||
}
|
||||
human, err := r.getHuman(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
logging.Log("COMMAND-DAqe1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get human for loginname")
|
||||
return nil, err
|
||||
}
|
||||
org, err := r.getOrg(ctx, human.ResourceOwner)
|
||||
if err != nil {
|
||||
logging.Log("COMMAND-Cm0ds").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get org for loginname")
|
||||
return nil, err
|
||||
}
|
||||
orgPolicy, err := r.getOrgIAMPolicy(ctx, org.AggregateID)
|
||||
if err != nil {
|
||||
logging.Log("COMMAND-y5zv9").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get org policy for loginname")
|
||||
return nil, err
|
||||
}
|
||||
otpWriteModel, err := r.otpWriteModelByID(ctx, userID, resourceowner)
|
||||
@@ -58,7 +62,7 @@ func (r *CommandSide) AddHumanOTP(ctx context.Context, userID, resourceowner str
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) CheckMFAOTPSetup(ctx context.Context, userID, code, userAgentID, resourceowner string) error {
|
||||
func (r *CommandSide) HumanCheckMFAOTPSetup(ctx context.Context, userID, code, userAgentID, resourceowner string) error {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-8N9ds", "Errors.User.UserIDMissing")
|
||||
}
|
||||
@@ -84,7 +88,32 @@ func (r *CommandSide) CheckMFAOTPSetup(ctx context.Context, userID, code, userAg
|
||||
return r.eventstore.PushAggregate(ctx, existingOTP, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) RemoveHumanOTP(ctx context.Context, userID, resourceOwner string) error {
|
||||
func (r *CommandSide) HumanCheckMFAOTP(ctx context.Context, userID, code, resourceowner string, authRequest *domain.AuthRequest) error {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-8N9ds", "Errors.User.UserIDMissing")
|
||||
}
|
||||
existingOTP, err := r.otpWriteModelByID(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingOTP.State != domain.MFAStateReady {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3Mif9s", "Errors.User.MFA.OTP.NotReady")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingOTP.WriteModel)
|
||||
err = domain.VerifyMFAOTP(code, existingOTP.Secret, r.multifactors.OTP.CryptoMFA)
|
||||
if err == nil {
|
||||
userAgg.PushEvents(
|
||||
user.NewHumanOTPCheckSucceededEvent(ctx, authRequestDomainToAuthRequestInfo(authRequest)),
|
||||
)
|
||||
return r.eventstore.PushAggregate(ctx, existingOTP, userAgg)
|
||||
}
|
||||
userAgg.PushEvents(user.NewHumanOTPCheckFailedEvent(ctx, authRequestDomainToAuthRequestInfo(authRequest)))
|
||||
pushErr := r.eventstore.PushAggregate(ctx, existingOTP, userAgg)
|
||||
logging.Log("COMMAND-9fj7s").OnError(pushErr).Error("error create password check failed event")
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanRemoveOTP(ctx context.Context, userID, resourceOwner string) error {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-5M0sd", "Errors.User.UserIDMissing")
|
||||
}
|
||||
|
@@ -45,7 +45,10 @@ func (wm *HumanOTPWriteModel) Reduce() error {
|
||||
}
|
||||
|
||||
func (wm *HumanOTPWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
ResourceOwner(wm.ResourceOwner)
|
||||
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID)
|
||||
if wm.ResourceOwner != "" {
|
||||
query.ResourceOwner(wm.ResourceOwner)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
@@ -21,7 +22,34 @@ func (r *CommandSide) SetOneTimePassword(ctx context.Context, orgID, userID, pas
|
||||
SecretString: passwordString,
|
||||
ChangeRequired: true,
|
||||
}
|
||||
return r.changePassword(ctx, orgID, userID, "", password, existingPassword)
|
||||
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
|
||||
return r.changePassword(ctx, orgID, userID, "", password, userAgg, existingPassword)
|
||||
}
|
||||
|
||||
func (r *CommandSide) SetPassword(ctx context.Context, orgID, userID, code, passwordString, userAgentID string) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
existingCode, err := r.passwordWriteModel(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if existingCode.Code == nil || existingCode.UserState == domain.UserStateUnspecified || existingCode.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9fs", "Errors.User.Code.NotFound")
|
||||
}
|
||||
|
||||
err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, r.emailVerificationCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
password := &domain.Password{
|
||||
SecretString: passwordString,
|
||||
ChangeRequired: false,
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingCode.WriteModel)
|
||||
return r.changePassword(ctx, existingCode.ResourceOwner, userID, userAgentID, password, userAgg, existingCode)
|
||||
}
|
||||
|
||||
func (r *CommandSide) ChangePassword(ctx context.Context, orgID, userID, oldPassword, newPassword, userAgentID string) (err error) {
|
||||
@@ -32,7 +60,7 @@ func (r *CommandSide) ChangePassword(ctx context.Context, orgID, userID, oldPass
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingPassword.Secret != nil {
|
||||
if existingPassword.Secret == nil {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Fds3s", "Errors.User.Password.Empty")
|
||||
}
|
||||
ctx, spanPasswordComparison := tracing.NewNamedSpan(ctx, "crypto.CompareHash")
|
||||
@@ -44,12 +72,14 @@ func (r *CommandSide) ChangePassword(ctx context.Context, orgID, userID, oldPass
|
||||
}
|
||||
password := &domain.Password{
|
||||
SecretString: newPassword,
|
||||
ChangeRequired: true,
|
||||
ChangeRequired: false,
|
||||
}
|
||||
return r.changePassword(ctx, orgID, userID, userAgentID, password, existingPassword)
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
|
||||
return r.changePassword(ctx, orgID, userID, userAgentID, password, userAgg, existingPassword)
|
||||
}
|
||||
|
||||
func (r *CommandSide) changePassword(ctx context.Context, orgID, userID, userAgentID string, password *domain.Password, existingPassword *HumanPasswordWriteModel) (err error) {
|
||||
func (r *CommandSide) changePassword(ctx context.Context, orgID, userID, userAgentID string, password *domain.Password, userAgg *user.Aggregate, existingPassword *HumanPasswordWriteModel) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@@ -69,7 +99,6 @@ func (r *CommandSide) changePassword(ctx context.Context, orgID, userID, userAge
|
||||
if err := password.HashPasswordIfExisting(pwPolicy, r.userPasswordAlg); err != nil {
|
||||
return err
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
|
||||
userAgg.PushEvents(user.NewHumanPasswordChangedEvent(ctx, password.SecretCrypto, password.ChangeRequired, userAgentID))
|
||||
return r.eventstore.PushAggregate(ctx, existingPassword, userAgg)
|
||||
}
|
||||
@@ -94,6 +123,54 @@ func (r *CommandSide) RequestSetPassword(ctx context.Context, userID, resourceOw
|
||||
return r.eventstore.PushAggregate(ctx, existingHuman, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) PasswordCodeSent(ctx context.Context, orgID, userID string) (err error) {
|
||||
existingPassword, err := r.passwordWriteModel(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingPassword.UserState == domain.UserStateUnspecified || existingPassword.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-3n77z", "Errors.User.NotFound")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
|
||||
userAgg.PushEvents(user.NewHumanPasswordCodeSentEvent(ctx))
|
||||
return r.eventstore.PushAggregate(ctx, existingPassword, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanCheckPassword(ctx context.Context, orgID, userID, password string, authRequest *domain.AuthRequest) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
if password == "" {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-3n8fs", "Errors.User.Password.Empty")
|
||||
}
|
||||
|
||||
existingPassword, err := r.passwordWriteModel(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingPassword.UserState == domain.UserStateUnspecified || existingPassword.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-3n77z", "Errors.User.NotFound")
|
||||
}
|
||||
|
||||
if existingPassword.Secret == nil {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-3n77z", "Errors.User.Password.NotSet")
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
|
||||
ctx, spanPasswordComparison := tracing.NewNamedSpan(ctx, "crypto.CompareHash")
|
||||
err = crypto.CompareHash(existingPassword.Secret, []byte(password), r.userPasswordAlg)
|
||||
spanPasswordComparison.EndWithError(err)
|
||||
if err == nil {
|
||||
userAgg.PushEvents(user.NewHumanPasswordCheckSucceededEvent(ctx, authRequestDomainToAuthRequestInfo(authRequest)))
|
||||
return r.eventstore.PushAggregate(ctx, existingPassword, userAgg)
|
||||
}
|
||||
|
||||
userAgg.PushEvents(user.NewHumanPasswordCheckFailedEvent(ctx, authRequestDomainToAuthRequestInfo(authRequest)))
|
||||
err = r.eventstore.PushAggregate(ctx, existingPassword, userAgg)
|
||||
logging.Log("COMMAND-9fj7s").OnError(err).Error("error create password check failed event")
|
||||
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-452ad", "Errors.User.Password.Invalid")
|
||||
}
|
||||
|
||||
func (r *CommandSide) passwordWriteModel(ctx context.Context, userID, resourceOwner string) (writeModel *HumanPasswordWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
"time"
|
||||
)
|
||||
|
||||
type HumanPasswordWriteModel struct {
|
||||
@@ -13,6 +14,10 @@ type HumanPasswordWriteModel struct {
|
||||
Secret *crypto.CryptoValue
|
||||
SecretChangeRequired bool
|
||||
|
||||
Code *crypto.CryptoValue
|
||||
CodeCreationDate time.Time
|
||||
CodeExpiry time.Duration
|
||||
|
||||
UserState domain.UserState
|
||||
}
|
||||
|
||||
@@ -43,6 +48,11 @@ func (wm *HumanPasswordWriteModel) Reduce() error {
|
||||
case *user.HumanPasswordChangedEvent:
|
||||
wm.Secret = e.Secret
|
||||
wm.SecretChangeRequired = e.ChangeRequired
|
||||
wm.Code = nil
|
||||
case *user.HumanPasswordCodeAddedEvent:
|
||||
wm.Code = e.Code
|
||||
wm.CodeCreationDate = e.CreationDate()
|
||||
wm.CodeExpiry = e.Expiry
|
||||
case *user.HumanEmailVerifiedEvent:
|
||||
if wm.UserState == domain.UserStateInitial {
|
||||
wm.UserState = domain.UserStateActive
|
||||
@@ -55,7 +65,10 @@ func (wm *HumanPasswordWriteModel) Reduce() error {
|
||||
}
|
||||
|
||||
func (wm *HumanPasswordWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
ResourceOwner(wm.ResourceOwner)
|
||||
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID)
|
||||
if wm.ResourceOwner != "" {
|
||||
query.ResourceOwner(wm.ResourceOwner)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
@@ -93,7 +93,7 @@ func (r *CommandSide) CreateHumanPhoneVerificationCode(ctx context.Context, user
|
||||
return err
|
||||
}
|
||||
if existingPhone.State == domain.PhoneStateUnspecified || existingPhone.State == domain.PhoneStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9fs", "Errors.User.Phone.NotFound")
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-2b7Hf", "Errors.User.Phone.NotFound")
|
||||
}
|
||||
if existingPhone.IsPhoneVerified {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sf", "Errors.User.Phone.AlreadyVerified")
|
||||
@@ -107,6 +107,19 @@ func (r *CommandSide) CreateHumanPhoneVerificationCode(ctx context.Context, user
|
||||
return r.eventstore.PushAggregate(ctx, existingPhone, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanPhoneVerificationCodeSent(ctx context.Context, orgID, userID string) (err error) {
|
||||
existingPhone, err := r.phoneWriteModelByID(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingPhone.State == domain.PhoneStateUnspecified || existingPhone.State == domain.PhoneStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-66n8J", "Errors.User.Phone.NotFound")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingPhone.WriteModel)
|
||||
userAgg.PushEvents(user.NewHumanPhoneCodeSentEvent(ctx))
|
||||
return r.eventstore.PushAggregate(ctx, existingPhone, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) RemoveHumanPhone(ctx context.Context, userID, resourceOwner string) error {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M0ds", "Errors.User.UserIDMissing")
|
||||
|
@@ -33,7 +33,35 @@ func (r *CommandSide) getHumanPasswordlessTokens(ctx context.Context, userID, re
|
||||
return readModelToPasswordlessTokens(tokenReadModel), nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) AddHumanU2F(ctx context.Context, userID, resourceowner string, isLoginUI bool) (*domain.WebAuthNToken, error) {
|
||||
func (r *CommandSide) getHumanU2FLogin(ctx context.Context, userID, authReqID, resourceowner string) (*domain.WebAuthNLogin, error) {
|
||||
tokenReadModel := NewHumanU2FLoginReadModel(userID, authReqID, resourceowner)
|
||||
err := r.eventstore.FilterToQueryReducer(ctx, tokenReadModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tokenReadModel.State == domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-5m88U", "Errors.User.NotFound")
|
||||
}
|
||||
return &domain.WebAuthNLogin{
|
||||
Challenge: tokenReadModel.Challenge,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) getHumanPasswordlessLogin(ctx context.Context, userID, authReqID, resourceowner string) (*domain.WebAuthNLogin, error) {
|
||||
tokenReadModel := NewHumanPasswordlessLoginReadModel(userID, authReqID, resourceowner)
|
||||
err := r.eventstore.FilterToQueryReducer(ctx, tokenReadModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tokenReadModel.State == domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-fm84R", "Errors.User.NotFound")
|
||||
}
|
||||
return &domain.WebAuthNLogin{
|
||||
Challenge: tokenReadModel.Challenge,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanAddU2FSetup(ctx context.Context, userID, resourceowner string, isLoginUI bool) (*domain.WebAuthNToken, error) {
|
||||
u2fTokens, err := r.getHumanU2FTokens(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -55,7 +83,7 @@ func (r *CommandSide) AddHumanU2F(ctx context.Context, userID, resourceowner str
|
||||
return createdWebAuthN, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) AddHumanPasswordless(ctx context.Context, userID, resourceowner string, isLoginUI bool) (*domain.WebAuthNToken, error) {
|
||||
func (r *CommandSide) HumanAddPasswordlessSetup(ctx context.Context, userID, resourceowner string, isLoginUI bool) (*domain.WebAuthNToken, error) {
|
||||
passwordlessTokens, err := r.getHumanPasswordlessTokens(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -64,7 +92,7 @@ func (r *CommandSide) AddHumanPasswordless(ctx context.Context, userID, resource
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userAgg.PushEvents(usr_repo.NewHumanU2FAddedEvent(ctx, addWebAuthN.WebauthNTokenID, webAuthN.Challenge))
|
||||
userAgg.PushEvents(usr_repo.NewHumanPasswordlessAddedEvent(ctx, addWebAuthN.WebauthNTokenID, webAuthN.Challenge))
|
||||
|
||||
err = r.eventstore.PushAggregate(ctx, addWebAuthN, userAgg)
|
||||
if err != nil {
|
||||
@@ -114,7 +142,7 @@ func (r *CommandSide) addHumanWebAuthN(ctx context.Context, userID, resourceowne
|
||||
return addWebAuthN, userAgg, webAuthN, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) VerifyHumanU2F(ctx context.Context, userID, resourceowner, tokenName, userAgentID string, credentialData []byte) error {
|
||||
func (r *CommandSide) HumanVerifyU2FSetup(ctx context.Context, userID, resourceowner, tokenName, userAgentID string, credentialData []byte) error {
|
||||
u2fTokens, err := r.getHumanU2FTokens(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -139,7 +167,7 @@ func (r *CommandSide) VerifyHumanU2F(ctx context.Context, userID, resourceowner,
|
||||
return r.eventstore.PushAggregate(ctx, verifyWebAuthN, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) VerifyHumanPasswordless(ctx context.Context, userID, resourceowner, tokenName, userAgentID string, credentialData []byte) error {
|
||||
func (r *CommandSide) HumanHumanPasswordlessSetup(ctx context.Context, userID, resourceowner, tokenName, userAgentID string, credentialData []byte) error {
|
||||
u2fTokens, err := r.getHumanPasswordlessTokens(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -149,7 +177,7 @@ func (r *CommandSide) VerifyHumanPasswordless(ctx context.Context, userID, resou
|
||||
return err
|
||||
}
|
||||
userAgg.PushEvents(
|
||||
usr_repo.NewHumanU2FVerifiedEvent(
|
||||
usr_repo.NewHumanPasswordlessVerifiedEvent(
|
||||
ctx,
|
||||
verifyWebAuthN.WebauthNTokenID,
|
||||
webAuthN.WebAuthNTokenName,
|
||||
@@ -186,12 +214,156 @@ func (r *CommandSide) verifyHumanWebAuthN(ctx context.Context, userID, resourceo
|
||||
return verifyWebAuthN, userAgg, webAuthN, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) RemoveHumanU2F(ctx context.Context, userID, webAuthNID, resourceOwner string) error {
|
||||
func (r *CommandSide) HumanBeginU2FLogin(ctx context.Context, userID, resourceOwner string, authRequest *domain.AuthRequest, isLoginUI bool) (*domain.WebAuthNLogin, error) {
|
||||
u2fTokens, err := r.getHumanU2FTokens(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
writeModel, userAgg, webAuthNLogin, err := r.beginWebAuthNLogin(ctx, userID, resourceOwner, u2fTokens, isLoginUI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userAgg.PushEvents(
|
||||
usr_repo.NewHumanU2FBeginLoginEvent(
|
||||
ctx,
|
||||
webAuthNLogin.Challenge,
|
||||
authRequestDomainToAuthRequestInfo(authRequest),
|
||||
),
|
||||
)
|
||||
|
||||
err = r.eventstore.PushAggregate(ctx, writeModel, userAgg)
|
||||
return webAuthNLogin, err
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanBeginPasswordlessLogin(ctx context.Context, userID, resourceOwner string, authRequest *domain.AuthRequest, isLoginUI bool) (*domain.WebAuthNLogin, error) {
|
||||
u2fTokens, err := r.getHumanPasswordlessTokens(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
writeModel, userAgg, webAuthNLogin, err := r.beginWebAuthNLogin(ctx, userID, resourceOwner, u2fTokens, isLoginUI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userAgg.PushEvents(
|
||||
usr_repo.NewHumanPasswordlessBeginLoginEvent(
|
||||
ctx,
|
||||
webAuthNLogin.Challenge,
|
||||
authRequestDomainToAuthRequestInfo(authRequest),
|
||||
),
|
||||
)
|
||||
|
||||
err = r.eventstore.PushAggregate(ctx, writeModel, userAgg)
|
||||
return webAuthNLogin, err
|
||||
}
|
||||
|
||||
func (r *CommandSide) beginWebAuthNLogin(ctx context.Context, userID, resourceOwner string, tokens []*domain.WebAuthNToken, isLoginUI bool) (*HumanWebAuthNWriteModel, *usr_repo.Aggregate, *domain.WebAuthNLogin, error) {
|
||||
if userID == "" {
|
||||
return nil, nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-hh8K9", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
human, err := r.getHuman(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
webAuthNLogin, err := r.webauthn.BeginLogin(human, domain.UserVerificationRequirementDiscouraged, isLoginUI, tokens...)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
writeModel, err := r.webauthNWriteModelByID(ctx, userID, "", resourceOwner)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&writeModel.WriteModel)
|
||||
return writeModel, userAgg, webAuthNLogin, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanFinishU2FLogin(ctx context.Context, userID, resourceOwner string, credentialData []byte, authRequest *domain.AuthRequest, isLoginUI bool) error {
|
||||
webAuthNLogin, err := r.getHumanU2FLogin(ctx, userID, authRequest.ID, resourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u2fTokens, err := r.getHumanU2FTokens(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
writeModel, userAgg, token, signCount, err := r.finishWebAuthNLogin(ctx, userID, resourceOwner, credentialData, webAuthNLogin, u2fTokens, isLoginUI)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userAgg.PushEvents(
|
||||
usr_repo.NewHumanU2FSignCountChangedEvent(
|
||||
ctx,
|
||||
token.WebAuthNTokenID,
|
||||
signCount,
|
||||
),
|
||||
)
|
||||
|
||||
return r.eventstore.PushAggregate(ctx, writeModel, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanFinishPasswordlessLogin(ctx context.Context, userID, resourceOwner string, credentialData []byte, authRequest *domain.AuthRequest, isLoginUI bool) error {
|
||||
webAuthNLogin, err := r.getHumanPasswordlessLogin(ctx, userID, authRequest.ID, resourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
passwordlessTokens, err := r.getHumanPasswordlessTokens(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
writeModel, userAgg, token, signCount, err := r.finishWebAuthNLogin(ctx, userID, resourceOwner, credentialData, webAuthNLogin, passwordlessTokens, isLoginUI)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userAgg.PushEvents(
|
||||
usr_repo.NewHumanPasswordlessSignCountChangedEvent(
|
||||
ctx,
|
||||
token.WebAuthNTokenID,
|
||||
signCount,
|
||||
),
|
||||
)
|
||||
|
||||
return r.eventstore.PushAggregate(ctx, writeModel, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) finishWebAuthNLogin(ctx context.Context, userID, resourceOwner string, credentialData []byte, webAuthN *domain.WebAuthNLogin, tokens []*domain.WebAuthNToken, isLoginUI bool) (*HumanWebAuthNWriteModel, *usr_repo.Aggregate, *domain.WebAuthNToken, uint32, error) {
|
||||
if userID == "" {
|
||||
return nil, nil, nil, 0, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-hh8K9", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
human, err := r.getHuman(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, nil, nil, 0, err
|
||||
}
|
||||
keyID, signCount, err := r.webauthn.FinishLogin(human, webAuthN, credentialData, isLoginUI, tokens...)
|
||||
if err != nil && keyID == nil {
|
||||
return nil, nil, nil, 0, err
|
||||
}
|
||||
|
||||
_, token := domain.GetTokenByKeyID(tokens, keyID)
|
||||
if token == nil {
|
||||
return nil, nil, nil, 0, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3b7zs", "Errors.User.WebAuthN.NotFound")
|
||||
}
|
||||
writeModel, err := r.webauthNWriteModelByID(ctx, userID, "", resourceOwner)
|
||||
if err != nil {
|
||||
return nil, nil, nil, 0, err
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&writeModel.WriteModel)
|
||||
return writeModel, userAgg, token, signCount, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) HumanRemoveU2F(ctx context.Context, userID, webAuthNID, resourceOwner string) error {
|
||||
event := usr_repo.NewHumanU2FRemovedEvent(ctx, webAuthNID)
|
||||
return r.removeHumanWebAuthN(ctx, userID, webAuthNID, resourceOwner, event)
|
||||
}
|
||||
|
||||
func (r *CommandSide) RemoveHumanPasswordless(ctx context.Context, userID, webAuthNID, resourceOwner string) error {
|
||||
func (r *CommandSide) HumanRemovePasswordless(ctx context.Context, userID, webAuthNID, resourceOwner string) error {
|
||||
event := usr_repo.NewHumanPasswordlessRemovedEvent(ctx, webAuthNID)
|
||||
return r.removeHumanWebAuthN(ctx, userID, webAuthNID, resourceOwner, event)
|
||||
}
|
||||
|
@@ -39,6 +39,14 @@ func (wm *HumanWebAuthNWriteModel) AppendEvents(events ...eventstore.EventReader
|
||||
if wm.WebauthNTokenID == e.WebAuthNTokenID {
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
}
|
||||
case *user.HumanWebAuthNVerifiedEvent:
|
||||
if wm.WebauthNTokenID == e.WebAuthNTokenID {
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
}
|
||||
case *user.HumanWebAuthNSignCountChangedEvent:
|
||||
if wm.WebauthNTokenID == e.WebAuthNTokenID {
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
}
|
||||
case *user.HumanWebAuthNRemovedEvent:
|
||||
if wm.WebauthNTokenID == e.WebAuthNTokenID {
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
@@ -56,6 +64,8 @@ func (wm *HumanWebAuthNWriteModel) Reduce() error {
|
||||
wm.appendAddedEvent(e)
|
||||
case *user.HumanWebAuthNVerifiedEvent:
|
||||
wm.appendVerifiedEvent(e)
|
||||
case *user.HumanWebAuthNSignCountChangedEvent:
|
||||
wm.SignCount = e.SignCount
|
||||
case *user.HumanWebAuthNRemovedEvent:
|
||||
wm.State = domain.MFAStateRemoved
|
||||
case *user.UserRemovedEvent:
|
||||
@@ -104,34 +114,35 @@ func NewHumanU2FTokensReadModel(userID, resourceOwner string) *HumanU2FTokensRea
|
||||
}
|
||||
|
||||
func (wm *HumanU2FTokensReadModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanWebAuthNAddedEvent:
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *user.HumanWebAuthNVerifiedEvent:
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *user.HumanWebAuthNRemovedEvent:
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *user.UserRemovedEvent:
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
}
|
||||
}
|
||||
wm.WriteModel.AppendEvents(events...)
|
||||
}
|
||||
|
||||
func (wm *HumanU2FTokensReadModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanWebAuthNAddedEvent:
|
||||
case *user.HumanU2FAddedEvent:
|
||||
token := &HumanWebAuthNWriteModel{}
|
||||
token.appendAddedEvent(e)
|
||||
wm.WebAuthNTokens = append(wm.WebAuthNTokens, token)
|
||||
case *user.HumanWebAuthNVerifiedEvent:
|
||||
token.appendAddedEvent(&e.HumanWebAuthNAddedEvent)
|
||||
token.WriteModel = eventstore.WriteModel{
|
||||
AggregateID: e.AggregateID(),
|
||||
}
|
||||
replaced := false
|
||||
for i, existingTokens := range wm.WebAuthNTokens {
|
||||
if existingTokens.State == domain.MFAStateNotReady {
|
||||
wm.WebAuthNTokens[i] = token
|
||||
replaced = true
|
||||
}
|
||||
}
|
||||
if !replaced {
|
||||
wm.WebAuthNTokens = append(wm.WebAuthNTokens, token)
|
||||
}
|
||||
case *user.HumanU2FVerifiedEvent:
|
||||
idx, token := wm.WebAuthNTokenByID(e.WebAuthNTokenID)
|
||||
if idx < 0 {
|
||||
continue
|
||||
}
|
||||
token.appendVerifiedEvent(e)
|
||||
case *user.HumanWebAuthNRemovedEvent:
|
||||
token.appendVerifiedEvent(&e.HumanWebAuthNVerifiedEvent)
|
||||
case *user.HumanU2FRemovedEvent:
|
||||
idx, _ := wm.WebAuthNTokenByID(e.WebAuthNTokenID)
|
||||
if idx < 0 {
|
||||
continue
|
||||
@@ -153,8 +164,7 @@ func (rm *HumanU2FTokensReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
EventTypes(
|
||||
user.HumanU2FTokenAddedType,
|
||||
user.HumanU2FTokenVerifiedType,
|
||||
user.HumanU2FTokenRemovedType,
|
||||
user.UserV1MFAOTPRemovedType)
|
||||
user.HumanU2FTokenRemovedType)
|
||||
|
||||
}
|
||||
|
||||
@@ -190,17 +200,29 @@ func (wm *HumanPasswordlessTokensReadModel) AppendEvents(events ...eventstore.Ev
|
||||
func (wm *HumanPasswordlessTokensReadModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanWebAuthNAddedEvent:
|
||||
case *user.HumanPasswordlessAddedEvent:
|
||||
token := &HumanWebAuthNWriteModel{}
|
||||
token.appendAddedEvent(e)
|
||||
wm.WebAuthNTokens = append(wm.WebAuthNTokens, token)
|
||||
case *user.HumanWebAuthNVerifiedEvent:
|
||||
token.appendAddedEvent(&e.HumanWebAuthNAddedEvent)
|
||||
token.WriteModel = eventstore.WriteModel{
|
||||
AggregateID: e.AggregateID(),
|
||||
}
|
||||
replaced := false
|
||||
for i, existingTokens := range wm.WebAuthNTokens {
|
||||
if existingTokens.State == domain.MFAStateNotReady {
|
||||
wm.WebAuthNTokens[i] = token
|
||||
replaced = true
|
||||
}
|
||||
}
|
||||
if !replaced {
|
||||
wm.WebAuthNTokens = append(wm.WebAuthNTokens, token)
|
||||
}
|
||||
case *user.HumanPasswordlessVerifiedEvent:
|
||||
idx, token := wm.WebAuthNTokenByID(e.WebAuthNTokenID)
|
||||
if idx < 0 {
|
||||
continue
|
||||
}
|
||||
token.appendVerifiedEvent(e)
|
||||
case *user.HumanWebAuthNRemovedEvent:
|
||||
token.appendVerifiedEvent(&e.HumanWebAuthNVerifiedEvent)
|
||||
case *user.HumanPasswordlessRemovedEvent:
|
||||
idx, _ := wm.WebAuthNTokenByID(e.WebAuthNTokenID)
|
||||
if idx < 0 {
|
||||
continue
|
||||
@@ -222,8 +244,7 @@ func (rm *HumanPasswordlessTokensReadModel) Query() *eventstore.SearchQueryBuild
|
||||
EventTypes(
|
||||
user.HumanPasswordlessTokenAddedType,
|
||||
user.HumanPasswordlessTokenVerifiedType,
|
||||
user.HumanPasswordlessTokenRemovedType,
|
||||
user.UserV1MFAOTPRemovedType)
|
||||
user.HumanPasswordlessTokenRemovedType)
|
||||
|
||||
}
|
||||
|
||||
@@ -235,3 +256,114 @@ func (wm *HumanPasswordlessTokensReadModel) WebAuthNTokenByID(id string) (idx in
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
type HumanU2FLoginReadModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
AuthReqID string
|
||||
Challenge string
|
||||
State domain.UserState
|
||||
}
|
||||
|
||||
func NewHumanU2FLoginReadModel(userID, authReqID, resourceOwner string) *HumanU2FLoginReadModel {
|
||||
return &HumanU2FLoginReadModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
AuthReqID: authReqID,
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanU2FLoginReadModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanU2FBeginLoginEvent:
|
||||
if e.AuthRequestInfo.ID != wm.AuthReqID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *user.UserRemovedEvent:
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanU2FLoginReadModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanU2FBeginLoginEvent:
|
||||
wm.Challenge = e.Challenge
|
||||
wm.State = domain.UserStateActive
|
||||
case *user.UserRemovedEvent:
|
||||
wm.State = domain.UserStateDeleted
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (rm *HumanU2FLoginReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(rm.AggregateID).
|
||||
ResourceOwner(rm.ResourceOwner).
|
||||
EventTypes(
|
||||
user.HumanU2FTokenBeginLoginType,
|
||||
user.UserRemovedType,
|
||||
)
|
||||
}
|
||||
|
||||
type HumanPasswordlessLoginReadModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
AuthReqID string
|
||||
Challenge string
|
||||
State domain.UserState
|
||||
}
|
||||
|
||||
func NewHumanPasswordlessLoginReadModel(userID, authReqID, resourceOwner string) *HumanPasswordlessLoginReadModel {
|
||||
return &HumanPasswordlessLoginReadModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
AuthReqID: authReqID,
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanPasswordlessLoginReadModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanPasswordlessBeginLoginEvent:
|
||||
if e.AuthRequestInfo.ID != wm.AuthReqID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *user.UserRemovedEvent:
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanPasswordlessLoginReadModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanPasswordlessBeginLoginEvent:
|
||||
wm.Challenge = e.Challenge
|
||||
wm.State = domain.UserStateActive
|
||||
case *user.UserRemovedEvent:
|
||||
wm.State = domain.UserStateDeleted
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (rm *HumanPasswordlessLoginReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(rm.AggregateID).
|
||||
ResourceOwner(rm.ResourceOwner).
|
||||
EventTypes(
|
||||
user.HumanPasswordlessTokenBeginLoginType,
|
||||
user.UserRemovedType,
|
||||
)
|
||||
|
||||
}
|
||||
|
@@ -51,7 +51,7 @@ func (r *CommandSide) ChangeMachine(ctx context.Context, machine *domain.Machine
|
||||
|
||||
changedEvent, hasChanged := existingUser.NewChangedEvent(ctx, machine.Name, machine.Description)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.Email.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2n8vs", "Errors.User.Email.NotChanged")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||
userAgg.PushEvents(changedEvent)
|
||||
|
83
internal/v2/command/user_machine_key.go
Normal file
83
internal/v2/command/user_machine_key.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
yearLayout = "2006-01-02"
|
||||
defaultExpirationDate = "9999-01-01"
|
||||
)
|
||||
|
||||
func (r *CommandSide) AddUserMachineKey(ctx context.Context, machineKey *domain.MachineKey, resourceOwner string) (*domain.MachineKey, error) {
|
||||
err := r.checkUserExists(ctx, machineKey.AggregateID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyID, err := r.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyWriteModel := NewMachineKeyWriteModel(machineKey.AggregateID, keyID, resourceOwner)
|
||||
userAgg := UserAggregateFromWriteModel(&keyWriteModel.WriteModel)
|
||||
err = r.eventstore.FilterToQueryReducer(ctx, keyWriteModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if machineKey.ExpirationDate.IsZero() {
|
||||
machineKey.ExpirationDate, err = time.Parse(yearLayout, defaultExpirationDate)
|
||||
if err != nil {
|
||||
logging.Log("COMMAND9-v8jMi").WithError(err).Warn("unable to set default date")
|
||||
return nil, errors.ThrowInternal(err, "COMMAND-38jfus", "Errors.Internal")
|
||||
}
|
||||
}
|
||||
if machineKey.ExpirationDate.Before(time.Now()) {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "COMMAND-38vns", "Errors.MachineKey.ExpireBeforeNow")
|
||||
}
|
||||
|
||||
machineKey.GenerateNewMachineKeyPair(r.machineKeySize)
|
||||
|
||||
userAgg.PushEvents(user.NewMachineKeyAddedEvent(ctx, keyID, machineKey.Type, machineKey.ExpirationDate, machineKey.PublicKey))
|
||||
err = r.eventstore.PushAggregate(ctx, keyWriteModel, userAgg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := keyWriteModelToMachineKey(keyWriteModel)
|
||||
key.PrivateKey = machineKey.PrivateKey
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) RemoveUserMachineKey(ctx context.Context, userID, keyID, resourceOwner string) error {
|
||||
keyWriteModel, err := r.machineKeyWriteModelByID(ctx, userID, keyID, resourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if keyWriteModel.State == domain.MachineKeyStateUnspecified || keyWriteModel.State == domain.MachineKeyStateRemoved {
|
||||
return errors.ThrowNotFound(nil, "COMMAND-4m77G", "Errors.User.Machine.Key.NotFound")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&keyWriteModel.WriteModel)
|
||||
userAgg.PushEvents(user.NewMachineKeyRemovedEvent(ctx, keyID))
|
||||
return r.eventstore.PushAggregate(ctx, keyWriteModel, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) machineKeyWriteModelByID(ctx context.Context, userID, keyID, resourceOwner string) (writeModel *MachineKeyWriteModel, err error) {
|
||||
if userID == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "COMMAND-4n8vs", "Errors.User.UserIDMissing")
|
||||
}
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
writeModel = NewMachineKeyWriteModel(userID, keyID, resourceOwner)
|
||||
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModel, nil
|
||||
}
|
74
internal/v2/command/user_machine_key_model.go
Normal file
74
internal/v2/command/user_machine_key_model.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MachineKeyWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
KeyID string
|
||||
KeyType domain.MachineKeyType
|
||||
ExpirationDate time.Time
|
||||
|
||||
State domain.MachineKeyState
|
||||
}
|
||||
|
||||
func NewMachineKeyWriteModel(userID, keyID, resourceOwner string) *MachineKeyWriteModel {
|
||||
return &MachineKeyWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
KeyID: keyID,
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *MachineKeyWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *user.MachineKeyAddedEvent:
|
||||
if wm.KeyID != e.KeyID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *user.MachineKeyRemovedEvent:
|
||||
if wm.KeyID != e.KeyID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *user.UserRemovedEvent:
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *MachineKeyWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.MachineKeyAddedEvent:
|
||||
wm.KeyID = e.KeyID
|
||||
wm.KeyType = e.KeyType
|
||||
wm.ExpirationDate = e.ExpirationDate
|
||||
wm.State = domain.MachineKeyStateActive
|
||||
case *user.MachineKeyRemovedEvent:
|
||||
wm.State = domain.MachineKeyStateRemoved
|
||||
case *user.UserRemovedEvent:
|
||||
wm.State = domain.MachineKeyStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *MachineKeyWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
EventTypes(
|
||||
user.MachineKeyAddedEventType,
|
||||
user.MachineKeyRemovedEventType,
|
||||
user.UserRemovedType)
|
||||
}
|
@@ -39,6 +39,8 @@ func (wm *UserWriteModel) Reduce() error {
|
||||
case *user.HumanRegisteredEvent:
|
||||
wm.UserName = e.UserName
|
||||
wm.UserState = domain.UserStateInitial
|
||||
case *user.HumanInitializedCheckSucceededEvent:
|
||||
wm.UserState = domain.UserStateActive
|
||||
case *user.MachineAddedEvent:
|
||||
wm.UserName = e.UserName
|
||||
wm.UserState = domain.UserStateActive
|
||||
|
150
internal/v2/domain/auth_request.go
Normal file
150
internal/v2/domain/auth_request.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"golang.org/x/text/language"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AuthRequest struct {
|
||||
ID string
|
||||
AgentID string
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
BrowserInfo *BrowserInfo
|
||||
ApplicationID string
|
||||
CallbackURI string
|
||||
TransferState string
|
||||
Prompt Prompt
|
||||
PossibleLOAs []LevelOfAssurance
|
||||
UiLocales []string
|
||||
LoginHint string
|
||||
MaxAuthAge uint32
|
||||
Request Request
|
||||
|
||||
levelOfAssurance LevelOfAssurance
|
||||
UserID string
|
||||
LoginName string
|
||||
DisplayName string
|
||||
UserOrgID string
|
||||
RequestedOrgID string
|
||||
RequestedOrgName string
|
||||
SelectedIDPConfigID string
|
||||
LinkingUsers []*ExternalUser
|
||||
PossibleSteps []NextStep
|
||||
PasswordVerified bool
|
||||
MFAsVerified []MFAType
|
||||
Audience []string
|
||||
AuthTime time.Time
|
||||
Code string
|
||||
LoginPolicy *LoginPolicy
|
||||
AllowedExternalIDPs []*IDPProvider
|
||||
}
|
||||
|
||||
type ExternalUser struct {
|
||||
IDPConfigID string
|
||||
ExternalUserID string
|
||||
DisplayName string
|
||||
PreferredUsername string
|
||||
FirstName string
|
||||
LastName string
|
||||
NickName string
|
||||
Email string
|
||||
IsEmailVerified bool
|
||||
PreferredLanguage language.Tag
|
||||
Phone string
|
||||
IsPhoneVerified bool
|
||||
}
|
||||
|
||||
type Prompt int32
|
||||
|
||||
const (
|
||||
PromptUnspecified Prompt = iota
|
||||
PromptNone
|
||||
PromptLogin
|
||||
PromptConsent
|
||||
PromptSelectAccount
|
||||
)
|
||||
|
||||
type LevelOfAssurance int
|
||||
|
||||
const (
|
||||
LevelOfAssuranceNone LevelOfAssurance = iota
|
||||
)
|
||||
|
||||
type MFAType int
|
||||
|
||||
const (
|
||||
MFATypeOTP MFAType = iota
|
||||
MFATypeU2F
|
||||
MFATypeU2FUserVerification
|
||||
)
|
||||
|
||||
type MFALevel int
|
||||
|
||||
const (
|
||||
MFALevelNotSetUp MFALevel = iota
|
||||
MFALevelSecondFactor
|
||||
MFALevelMultiFactor
|
||||
MFALevelMultiFactorCertified
|
||||
)
|
||||
|
||||
func NewAuthRequestFromType(requestType AuthRequestType) (*AuthRequest, error) {
|
||||
request, ok := authRequestTypeMapping[requestType]
|
||||
if !ok {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "DOMAIN-ds2kl", "invalid request type")
|
||||
}
|
||||
return &AuthRequest{Request: request}, nil
|
||||
}
|
||||
|
||||
func (a *AuthRequest) WithCurrentInfo(info *BrowserInfo) *AuthRequest {
|
||||
a.BrowserInfo = info
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *AuthRequest) SetUserInfo(userID, loginName, displayName, userOrgID string) {
|
||||
a.UserID = userID
|
||||
a.LoginName = loginName
|
||||
a.DisplayName = displayName
|
||||
a.UserOrgID = userOrgID
|
||||
}
|
||||
|
||||
func (a *AuthRequest) MFALevel() MFALevel {
|
||||
return -1
|
||||
//PLANNED: check a.PossibleLOAs (and Prompt Login?)
|
||||
}
|
||||
|
||||
func (a *AuthRequest) AppendAudIfNotExisting(aud string) {
|
||||
for _, a := range a.Audience {
|
||||
if a == aud {
|
||||
return
|
||||
}
|
||||
}
|
||||
a.Audience = append(a.Audience, aud)
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetScopeProjectIDsForAud() []string {
|
||||
projectIDs := make([]string, 0)
|
||||
switch request := a.Request.(type) {
|
||||
case *AuthRequestOIDC:
|
||||
for _, scope := range request.Scopes {
|
||||
if strings.HasPrefix(scope, ProjectIDScope) && strings.HasSuffix(scope, AudSuffix) {
|
||||
projectIDs = append(projectIDs, strings.TrimSuffix(strings.TrimPrefix(scope, ProjectIDScope), AudSuffix))
|
||||
}
|
||||
}
|
||||
}
|
||||
return projectIDs
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetScopeOrgPrimaryDomain() string {
|
||||
switch request := a.Request.(type) {
|
||||
case *AuthRequestOIDC:
|
||||
for _, scope := range request.Scopes {
|
||||
if strings.HasPrefix(scope, OrgDomainPrimaryScope) {
|
||||
return strings.TrimPrefix(scope, OrgDomainPrimaryScope)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
22
internal/v2/domain/browser_info.go
Normal file
22
internal/v2/domain/browser_info.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"net"
|
||||
net_http "net/http"
|
||||
|
||||
http_util "github.com/caos/zitadel/internal/api/http"
|
||||
)
|
||||
|
||||
type BrowserInfo struct {
|
||||
UserAgent string
|
||||
AcceptLanguage string
|
||||
RemoteIP net.IP
|
||||
}
|
||||
|
||||
func BrowserInfoFromRequest(r *net_http.Request) *BrowserInfo {
|
||||
return &BrowserInfo{
|
||||
UserAgent: r.Header.Get(http_util.UserAgentHeader),
|
||||
AcceptLanguage: r.Header.Get(http_util.AcceptLanguage),
|
||||
RemoteIP: http_util.RemoteIPFromRequest(r),
|
||||
}
|
||||
}
|
@@ -60,15 +60,15 @@ func (u *Human) IsValid() bool {
|
||||
return u.Profile != nil && u.FirstName != "" && u.LastName != "" && u.Email != nil && u.Email.IsValid() && u.Phone == nil || (u.Phone != nil && u.Phone.PhoneNumber != "" && u.Phone.IsValid())
|
||||
}
|
||||
|
||||
func (u *Human) CheckOrgIAMPolicy(userName string, policy *OrgIAMPolicy) error {
|
||||
func (u *Human) CheckOrgIAMPolicy(policy *OrgIAMPolicy) error {
|
||||
if policy == nil {
|
||||
return caos_errors.ThrowPreconditionFailed(nil, "DOMAIN-zSH7j", "Errors.Users.OrgIamPolicyNil")
|
||||
}
|
||||
if policy.UserLoginMustBeDomain && strings.Contains(userName, "@") {
|
||||
if policy.UserLoginMustBeDomain && strings.Contains(u.Username, "@") {
|
||||
return caos_errors.ThrowPreconditionFailed(nil, "DOMAIN-se4sJ", "Errors.User.EmailAsUsernameNotAllowed")
|
||||
}
|
||||
if !policy.UserLoginMustBeDomain && u.Profile != nil && userName == "" && u.Email != nil {
|
||||
userName = u.EmailAddress
|
||||
if !policy.UserLoginMustBeDomain && u.Profile != nil && u.Username == "" && u.Email != nil {
|
||||
u.Username = u.EmailAddress
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -1,6 +1,9 @@
|
||||
package domain
|
||||
|
||||
import es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
import (
|
||||
"bytes"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
)
|
||||
|
||||
type WebAuthNToken struct {
|
||||
es_models.ObjectRoot
|
||||
@@ -55,3 +58,12 @@ func GetTokenToVerify(tokens []*WebAuthNToken) (int, *WebAuthNToken) {
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func GetTokenByKeyID(tokens []*WebAuthNToken, keyID []byte) (int, *WebAuthNToken) {
|
||||
for i, token := range tokens {
|
||||
if bytes.Compare(token.KeyID, keyID) == 0 {
|
||||
return i, token
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
@@ -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 ""
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
149
internal/v2/domain/next_step.go
Normal file
149
internal/v2/domain/next_step.go
Normal 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
|
||||
}
|
17
internal/v2/domain/oidc_code_challenge.go
Normal file
17
internal/v2/domain/oidc_code_challenge.go
Normal 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
|
||||
)
|
@@ -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
|
||||
}
|
||||
|
54
internal/v2/domain/request.go
Normal file
54
internal/v2/domain/request.go
Normal 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
|
||||
}
|
@@ -2,6 +2,7 @@ package domain
|
||||
|
||||
const (
|
||||
RoleOrgOwner = "ORG_OWNER"
|
||||
RoleOrgProjectCreator = "ORG_PROJECT_CREATOR"
|
||||
RoleIAMOwner = "IAM_OWNER"
|
||||
RoleProjectOwner = "PROJECT_OWNER"
|
||||
RoleProjectOwnerGlobal = "PROJECT_OWNER_GLOBAL"
|
||||
|
18
internal/v2/domain/token.go
Normal file
18
internal/v2/domain/token.go
Normal 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
|
||||
}
|
@@ -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
11
internal/v2/query/user.go
Normal 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)
|
||||
}
|
@@ -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)
|
||||
}
|
||||
|
@@ -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"`
|
||||
|
16
internal/v2/repository/user/auth_request_info.go
Normal file
16
internal/v2/repository/user/auth_request_info.go
Normal 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"`
|
||||
}
|
@@ -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).
|
||||
|
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
224
internal/v2/repository/user/human_mfa_passwordless.go
Normal file
224
internal/v2/repository/user/human_mfa_passwordless.go
Normal 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
|
||||
}
|
224
internal/v2/repository/user/human_mfa_u2f.go
Normal file
224
internal/v2/repository/user/human_mfa_u2f.go
Normal 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
|
||||
}
|
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user