fix: backend bugs (#1449)

* i18n of compliance problems

* fix: return iam member roles

* remove u2f/passwordless

* u2f/passwordless

* fix rest path GetMachineKeyByIDs

* fix rest path GetMachineKeyByIDs

* fix email mime-type

* fix: member preferred login name

* machine users in notify

* fix api key query

* fix: todos grpc api

* fix: handle user init state

* fix: tests

Co-authored-by: fabi <fabienne.gerschwiler@gmail.com>
This commit is contained in:
Livio Amstutz 2021-03-22 14:40:25 +01:00 committed by GitHub
parent 6b1f7ba333
commit bd1a3bb6d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 639 additions and 224 deletions

View File

@ -2,8 +2,14 @@ package handler
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1" "github.com/caos/zitadel/internal/eventstore/v1"
es_sdk "github.com/caos/zitadel/internal/eventstore/v1/sdk"
iam_view "github.com/caos/zitadel/internal/iam/repository/view"
org_model "github.com/caos/zitadel/internal/org/model"
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
org_view "github.com/caos/zitadel/internal/org/repository/view"
"github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view"
view_model "github.com/caos/zitadel/internal/user/repository/view/model" view_model "github.com/caos/zitadel/internal/user/repository/view/model"
@ -11,8 +17,10 @@ import (
es_models "github.com/caos/zitadel/internal/eventstore/v1/models" es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/eventstore/v1/query" "github.com/caos/zitadel/internal/eventstore/v1/query"
"github.com/caos/zitadel/internal/eventstore/v1/spooler" "github.com/caos/zitadel/internal/eventstore/v1/spooler"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model"
usr_model "github.com/caos/zitadel/internal/user/model" usr_model "github.com/caos/zitadel/internal/user/model"
usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
) )
@ -84,7 +92,7 @@ func (m *IAMMember) Reduce(event *es_models.Event) (err error) {
} }
func (m *IAMMember) processIAMMember(event *es_models.Event) (err error) { func (m *IAMMember) processIAMMember(event *es_models.Event) (err error) {
member := new(iam_model.IAMMemberView) member := new(iam_view_model.IAMMemberView)
switch event.Type { switch event.Type {
case model.IAMMemberAdded: case model.IAMMemberAdded:
err = member.AppendEvent(event) err = member.AppendEvent(event)
@ -146,17 +154,25 @@ func (m *IAMMember) processUser(event *es_models.Event) (err error) {
} }
} }
func (m *IAMMember) fillData(member *iam_model.IAMMemberView) (err error) { func (m *IAMMember) fillData(member *iam_view_model.IAMMemberView) (err error) {
user, err := m.getUserByID(member.UserID) user, err := m.getUserByID(member.UserID)
if err != nil { if err != nil {
return err return err
} }
m.fillUserData(member, user) return m.fillUserData(member, user)
return nil
} }
func (m *IAMMember) fillUserData(member *iam_model.IAMMemberView, user *view_model.UserView) { func (m *IAMMember) fillUserData(member *iam_view_model.IAMMemberView, user *view_model.UserView) error {
org, err := m.getOrgByID(context.Background(), user.ResourceOwner)
policy := org.OrgIamPolicy
if policy == nil {
policy, err = m.getDefaultOrgIAMPolicy(context.TODO())
if err != nil {
return err
}
}
member.UserName = user.UserName member.UserName = user.UserName
member.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain)
if user.HumanView != nil { if user.HumanView != nil {
member.FirstName = user.FirstName member.FirstName = user.FirstName
member.LastName = user.LastName member.LastName = user.LastName
@ -166,7 +182,9 @@ func (m *IAMMember) fillUserData(member *iam_model.IAMMemberView, user *view_mod
if user.MachineView != nil { if user.MachineView != nil {
member.DisplayName = user.MachineView.Name member.DisplayName = user.MachineView.Name
} }
return nil
} }
func (m *IAMMember) OnError(event *es_models.Event, err error) error { func (m *IAMMember) OnError(event *es_models.Event, err error) error {
logging.LogWithFields("SPOOL-Ld9ow", "id", event.AggregateID).WithError(err).Warn("something went wrong in iammember handler") logging.LogWithFields("SPOOL-Ld9ow", "id", event.AggregateID).WithError(err).Warn("something went wrong in iammember handler")
return spooler.HandleError(event, err, m.view.GetLatestIAMMemberFailedEvent, m.view.ProcessedIAMMemberFailedEvent, m.view.ProcessedIAMMemberSequence, m.errorCountUntilSkip) return spooler.HandleError(event, err, m.view.GetLatestIAMMemberFailedEvent, m.view.ProcessedIAMMemberFailedEvent, m.view.ProcessedIAMMemberSequence, m.errorCountUntilSkip)
@ -208,3 +226,53 @@ func (m *IAMMember) getUserEvents(userID string, sequence uint64) ([]*es_models.
return m.es.FilterEvents(context.Background(), query) return m.es.FilterEvents(context.Background(), query)
} }
func (u *IAMMember) getOrgByID(ctx context.Context, orgID string) (*org_model.Org, error) {
query, err := org_view.OrgByIDQuery(orgID, 0)
if err != nil {
return nil, err
}
esOrg := &org_es_model.Org{
ObjectRoot: es_models.ObjectRoot{
AggregateID: orgID,
},
}
err = es_sdk.Filter(ctx, u.Eventstore().FilterEvents, esOrg.AppendEvents, query)
if err != nil && !caos_errs.IsNotFound(err) {
return nil, err
}
if esOrg.Sequence == 0 {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-3nd7s", "Errors.Org.NotFound")
}
return org_es_model.OrgToModel(esOrg), nil
}
func (u *IAMMember) getDefaultOrgIAMPolicy(ctx context.Context) (*iam_model.OrgIAMPolicy, error) {
existingIAM, err := u.getIAMByID(ctx)
if err != nil {
return nil, err
}
if existingIAM.DefaultOrgIAMPolicy == nil {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-3Bf7s", "Errors.IAM.OrgIAMPolicy.NotExisting")
}
return existingIAM.DefaultOrgIAMPolicy, nil
}
func (u *IAMMember) getIAMByID(ctx context.Context) (*iam_model.IAM, error) {
query, err := iam_view.IAMByIDQuery(domain.IAMID, 0)
if err != nil {
return nil, err
}
iam := &iam_es_model.IAM{
ObjectRoot: es_models.ObjectRoot{
AggregateID: domain.IAMID,
},
}
err = es_sdk.Filter(ctx, u.Eventstore().FilterEvents, iam.AppendEvents, query)
if err != nil && caos_errs.IsNotFound(err) && iam.Sequence == 0 {
return nil, err
}
return iam_es_model.IAMToModel(iam), nil
}

View File

@ -12,6 +12,7 @@ import (
func (s *Server) ListIAMMemberRoles(ctx context.Context, req *admin_pb.ListIAMMemberRolesRequest) (*admin_pb.ListIAMMemberRolesResponse, error) { func (s *Server) ListIAMMemberRoles(ctx context.Context, req *admin_pb.ListIAMMemberRolesRequest) (*admin_pb.ListIAMMemberRolesResponse, error) {
roles := s.iam.GetIAMMemberRoles() roles := s.iam.GetIAMMemberRoles()
return &admin_pb.ListIAMMemberRolesResponse{ return &admin_pb.ListIAMMemberRolesResponse{
Roles: roles,
Details: object.ToListDetails(uint64(len(roles)), 0, time.Now()), Details: object.ToListDetails(uint64(len(roles)), 0, time.Now()),
}, nil }, nil
} }

View File

@ -67,7 +67,6 @@ func (s *Server) AddMyAuthFactorU2F(ctx context.Context, _ *auth_pb.AddMyAuthFac
} }
return &auth_pb.AddMyAuthFactorU2FResponse{ return &auth_pb.AddMyAuthFactorU2FResponse{
Key: &user_pb.WebAuthNKey{ Key: &user_pb.WebAuthNKey{
Id: u2f.WebAuthNTokenID,
PublicKey: u2f.CredentialCreationData, PublicKey: u2f.CredentialCreationData,
}, },
Details: object.AddToDetailsPb( Details: object.AddToDetailsPb(
@ -91,7 +90,7 @@ func (s *Server) VerifyMyAuthFactorU2F(ctx context.Context, req *auth_pb.VerifyM
func (s *Server) RemoveMyAuthFactorU2F(ctx context.Context, req *auth_pb.RemoveMyAuthFactorU2FRequest) (*auth_pb.RemoveMyAuthFactorU2FResponse, error) { func (s *Server) RemoveMyAuthFactorU2F(ctx context.Context, req *auth_pb.RemoveMyAuthFactorU2FRequest) (*auth_pb.RemoveMyAuthFactorU2FResponse, error) {
ctxData := authz.GetCtxData(ctx) ctxData := authz.GetCtxData(ctx)
objectDetails, err := s.command.HumanRemovePasswordless(ctx, ctxData.UserID, req.TokenId, ctxData.ResourceOwner) objectDetails, err := s.command.HumanRemoveU2F(ctx, ctxData.UserID, req.TokenId, ctxData.ResourceOwner)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -41,14 +41,13 @@ func (s *Server) SetMyPhone(ctx context.Context, req *auth_pb.SetMyPhoneRequest)
func (s *Server) VerifyMyPhone(ctx context.Context, req *auth_pb.VerifyMyPhoneRequest) (*auth_pb.VerifyMyPhoneResponse, error) { func (s *Server) VerifyMyPhone(ctx context.Context, req *auth_pb.VerifyMyPhoneRequest) (*auth_pb.VerifyMyPhoneResponse, error) {
ctxData := authz.GetCtxData(ctx) ctxData := authz.GetCtxData(ctx)
_, err := s.command.VerifyHumanPhone(ctx, ctxData.UserID, req.Code, ctxData.ResourceOwner) objectDetails, err := s.command.VerifyHumanPhone(ctx, ctxData.UserID, req.Code, ctxData.ResourceOwner)
if err != nil { if err != nil {
return nil, err return nil, err
} }
//TODO: response from business
return &auth_pb.VerifyMyPhoneResponse{ return &auth_pb.VerifyMyPhoneResponse{
//Details: object.DomainToChangeDetailsPb(objectDetails), Details: object.DomainToChangeDetailsPb(objectDetails),
}, nil }, nil
} }

View File

@ -245,7 +245,7 @@ func IDPProviderTypeModelFromPb(typ idp_pb.IDPOwnerType) iam_model.IDPProviderTy
func IDPIDQueryToModel(query *idp_pb.IDPIDQuery) *iam_model.IDPConfigSearchQuery { func IDPIDQueryToModel(query *idp_pb.IDPIDQuery) *iam_model.IDPConfigSearchQuery {
return &iam_model.IDPConfigSearchQuery{ return &iam_model.IDPConfigSearchQuery{
Key: iam_model.IDPConfigSearchKeyIdpConfigID, //TODO: whats the difference between idpconfigid and aggregateid search key? Key: iam_model.IDPConfigSearchKeyIdpConfigID,
Method: domain.SearchMethodEquals, Method: domain.SearchMethodEquals,
Value: query.Id, Value: query.Id,
} }

View File

@ -139,7 +139,6 @@ func (s *Server) GenerateOrgDomainValidation(ctx context.Context, req *mgmt_pb.G
return &mgmt_pb.GenerateOrgDomainValidationResponse{ return &mgmt_pb.GenerateOrgDomainValidationResponse{
Token: token, Token: token,
Url: url, Url: url,
//TODO: remove details from proto
}, nil }, nil
} }

View File

@ -121,7 +121,7 @@ func AddAPIClientKeyRequestToDomain(key *mgmt_pb.AddAppKeyRequest) *domain.Appli
func ListAPIClientKeysRequestToModel(req *mgmt_pb.ListAppKeysRequest) (*key_model.AuthNKeySearchRequest, error) { func ListAPIClientKeysRequestToModel(req *mgmt_pb.ListAppKeysRequest) (*key_model.AuthNKeySearchRequest, error) {
offset, limit, asc := object.ListQueryToModel(req.Query) offset, limit, asc := object.ListQueryToModel(req.Query)
queries := make([]*key_model.AuthNKeySearchQuery, 2) queries := make([]*key_model.AuthNKeySearchQuery, 0)
queries = append(queries, &key_model.AuthNKeySearchQuery{ queries = append(queries, &key_model.AuthNKeySearchQuery{
Key: key_model.AuthNKeyObjectID, Key: key_model.AuthNKeyObjectID,
Method: domain.SearchMethodEquals, Method: domain.SearchMethodEquals,

View File

@ -2,6 +2,7 @@ package management
import ( import (
"context" "context"
"time"
"github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/authz"
member_grpc "github.com/caos/zitadel/internal/api/grpc/member" member_grpc "github.com/caos/zitadel/internal/api/grpc/member"
@ -106,8 +107,8 @@ func (s *Server) RemoveProjectGrant(ctx context.Context, req *mgmt_pb.RemoveProj
func (s *Server) ListProjectGrantMemberRoles(ctx context.Context, req *mgmt_pb.ListProjectGrantMemberRolesRequest) (*mgmt_pb.ListProjectGrantMemberRolesResponse, error) { func (s *Server) ListProjectGrantMemberRoles(ctx context.Context, req *mgmt_pb.ListProjectGrantMemberRolesRequest) (*mgmt_pb.ListProjectGrantMemberRolesResponse, error) {
roles := s.project.GetProjectGrantMemberRoles() roles := s.project.GetProjectGrantMemberRoles()
return &mgmt_pb.ListProjectGrantMemberRolesResponse{ return &mgmt_pb.ListProjectGrantMemberRolesResponse{
Result: roles, Result: roles,
//TODO: metadata Details: object_grpc.ToListDetails(uint64(len(roles)), 0, time.Now()),
}, nil }, nil
} }

View File

@ -100,7 +100,5 @@ func (s *Server) BulkRemoveUserGrant(ctx context.Context, req *mgmt_pb.BulkRemov
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &mgmt_pb.BulkRemoveUserGrantResponse{ return &mgmt_pb.BulkRemoveUserGrantResponse{}, nil
//TODO: Do we need details here?
}, nil
} }

View File

@ -17,13 +17,13 @@ func IAMMembersToPb(members []*iam_model.IAMMemberView) []*member_pb.Member {
func IAMMemberToPb(m *iam_model.IAMMemberView) *member_pb.Member { func IAMMemberToPb(m *iam_model.IAMMemberView) *member_pb.Member {
return &member_pb.Member{ return &member_pb.Member{
UserId: m.UserID, UserId: m.UserID,
Roles: m.Roles, Roles: m.Roles,
// PreferredLoginName: //TODO: not implemented in be PreferredLoginName: m.PreferredLoginName,
Email: m.Email, Email: m.Email,
FirstName: m.FirstName, FirstName: m.FirstName,
LastName: m.LastName, LastName: m.LastName,
DisplayName: m.DisplayName, DisplayName: m.DisplayName,
Details: object.ToViewDetailsPb( Details: object.ToViewDetailsPb(
m.Sequence, m.Sequence,
m.CreationDate, m.CreationDate,

View File

@ -17,13 +17,13 @@ func OrgMembersToPb(members []*org_model.OrgMemberView) []*member_pb.Member {
func OrgMemberToPb(m *org_model.OrgMemberView) *member_pb.Member { func OrgMemberToPb(m *org_model.OrgMemberView) *member_pb.Member {
return &member_pb.Member{ return &member_pb.Member{
UserId: m.UserID, UserId: m.UserID,
Roles: m.Roles, Roles: m.Roles,
// PreferredLoginName: //TODO: not implemented in be PreferredLoginName: m.PreferredLoginName,
Email: m.Email, Email: m.Email,
FirstName: m.FirstName, FirstName: m.FirstName,
LastName: m.LastName, LastName: m.LastName,
DisplayName: m.DisplayName, DisplayName: m.DisplayName,
Details: object.ToViewDetailsPb( Details: object.ToViewDetailsPb(
m.Sequence, m.Sequence,
m.CreationDate, m.CreationDate,

View File

@ -17,13 +17,13 @@ func ProjectGrantMembersToPb(members []*proj_model.ProjectGrantMemberView) []*me
func ProjectGrantMemberToPb(m *proj_model.ProjectGrantMemberView) *member_pb.Member { func ProjectGrantMemberToPb(m *proj_model.ProjectGrantMemberView) *member_pb.Member {
return &member_pb.Member{ return &member_pb.Member{
UserId: m.UserID, UserId: m.UserID,
Roles: m.Roles, Roles: m.Roles,
// PreferredLoginName: //TODO: not implemented in be PreferredLoginName: m.PreferredLoginName,
Email: m.Email, Email: m.Email,
FirstName: m.FirstName, FirstName: m.FirstName,
LastName: m.LastName, LastName: m.LastName,
DisplayName: m.DisplayName, DisplayName: m.DisplayName,
Details: object.ToViewDetailsPb( Details: object.ToViewDetailsPb(
m.Sequence, m.Sequence,
m.CreationDate, m.CreationDate,

View File

@ -17,13 +17,13 @@ func ProjectMembersToPb(members []*proj_model.ProjectMemberView) []*member_pb.Me
func ProjectMemberToPb(m *proj_model.ProjectMemberView) *member_pb.Member { func ProjectMemberToPb(m *proj_model.ProjectMemberView) *member_pb.Member {
return &member_pb.Member{ return &member_pb.Member{
UserId: m.UserID, UserId: m.UserID,
Roles: m.Roles, Roles: m.Roles,
// PreferredLoginName: //TODO: not implemented in be PreferredLoginName: m.PreferredLoginName,
Email: m.Email, Email: m.Email,
FirstName: m.FirstName, FirstName: m.FirstName,
LastName: m.LastName, LastName: m.LastName,
DisplayName: m.DisplayName, DisplayName: m.DisplayName,
Details: object.ToViewDetailsPb( Details: object.ToViewDetailsPb(
m.Sequence, m.Sequence,
m.CreationDate, m.CreationDate,

View File

@ -29,8 +29,11 @@ func OrgQueryToModel(query *org_pb.OrgQuery) (*org_model.OrgSearchQuery, error)
Value: q.DomainQuery.Domain, Value: q.DomainQuery.Domain,
}, nil }, nil
case *org_pb.OrgQuery_NameQuery: case *org_pb.OrgQuery_NameQuery:
//TODO: implement name in backend return &org_model.OrgSearchQuery{
return nil, errors.ThrowUnimplemented(nil, "ADMIN-KGXnX", "name query not implemented") Key: org_model.OrgSearchKeyOrgName,
Method: object.TextMethodToModel(q.NameQuery.Method),
Value: q.NameQuery.Name,
}, nil
default: default:
return nil, errors.ThrowInvalidArgument(nil, "ADMIN-vR9nC", "List.Query.Invalid") return nil, errors.ThrowInvalidArgument(nil, "ADMIN-vR9nC", "List.Query.Invalid")
} }

View File

@ -7,7 +7,8 @@ import (
func MultiFactorTypeToDomain(multiFactorType policy_pb.MultiFactorType) domain.MultiFactorType { func MultiFactorTypeToDomain(multiFactorType policy_pb.MultiFactorType) domain.MultiFactorType {
switch multiFactorType { switch multiFactorType {
//TODO: gap between proto and backend case policy_pb.MultiFactorType_MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION:
return domain.MultiFactorTypeU2FWithPIN
default: default:
return domain.MultiFactorTypeUnspecified return domain.MultiFactorTypeUnspecified
} }

View File

@ -244,7 +244,6 @@ func WebAuthNTokenViewToPb(token *model.WebAuthNView) *user_pb.WebAuthNToken {
func WebAuthNTokenToWebAuthNKeyPb(token *domain.WebAuthNToken) *user_pb.WebAuthNKey { func WebAuthNTokenToWebAuthNKeyPb(token *domain.WebAuthNToken) *user_pb.WebAuthNKey {
return &user_pb.WebAuthNKey{ return &user_pb.WebAuthNKey{
Id: string(token.KeyID), //TODO: ask if it's the correct id?
PublicKey: token.PublicKey, PublicKey: token.PublicKey,
} }
} }

View File

@ -37,10 +37,14 @@ func (wm *HumanEmailWriteModel) Reduce() error {
switch e := event.(type) { switch e := event.(type) {
case *user.HumanAddedEvent: case *user.HumanAddedEvent:
wm.Email = e.EmailAddress wm.Email = e.EmailAddress
wm.UserState = domain.UserStateInitial wm.UserState = domain.UserStateActive
case *user.HumanRegisteredEvent: case *user.HumanRegisteredEvent:
wm.Email = e.EmailAddress wm.Email = e.EmailAddress
wm.UserState = domain.UserStateActive
case *user.HumanInitialCodeAddedEvent:
wm.UserState = domain.UserStateInitial wm.UserState = domain.UserStateInitial
case *user.HumanInitializedCheckSucceededEvent:
wm.UserState = domain.UserStateActive
case *user.HumanEmailChangedEvent: case *user.HumanEmailChangedEvent:
wm.Email = e.EmailAddress wm.Email = e.EmailAddress
wm.IsEmailVerified = false wm.IsEmailVerified = false
@ -52,9 +56,6 @@ func (wm *HumanEmailWriteModel) Reduce() error {
case *user.HumanEmailVerifiedEvent: case *user.HumanEmailVerifiedEvent:
wm.IsEmailVerified = true wm.IsEmailVerified = true
wm.Code = nil wm.Code = nil
if wm.UserState == domain.UserStateInitial {
wm.UserState = domain.UserStateActive
}
case *user.UserRemovedEvent: case *user.UserRemovedEvent:
wm.UserState = domain.UserStateDeleted wm.UserState = domain.UserStateDeleted
} }

View File

@ -572,6 +572,12 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
true, true,
), ),
), ),
eventFromEventPusher(
user.NewHumanInitialCodeAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
nil, time.Hour*1,
),
),
), ),
), ),
}, },

View File

@ -37,10 +37,10 @@ func (wm *HumanInitCodeWriteModel) Reduce() error {
switch e := event.(type) { switch e := event.(type) {
case *user.HumanAddedEvent: case *user.HumanAddedEvent:
wm.Email = e.EmailAddress wm.Email = e.EmailAddress
wm.UserState = domain.UserStateInitial wm.UserState = domain.UserStateActive
case *user.HumanRegisteredEvent: case *user.HumanRegisteredEvent:
wm.Email = e.EmailAddress wm.Email = e.EmailAddress
wm.UserState = domain.UserStateInitial wm.UserState = domain.UserStateActive
case *user.HumanEmailChangedEvent: case *user.HumanEmailChangedEvent:
wm.Email = e.EmailAddress wm.Email = e.EmailAddress
wm.IsEmailVerified = false wm.IsEmailVerified = false
@ -54,8 +54,10 @@ func (wm *HumanInitCodeWriteModel) Reduce() error {
wm.Code = e.Code wm.Code = e.Code
wm.CodeCreationDate = e.CreationDate() wm.CodeCreationDate = e.CreationDate()
wm.CodeExpiry = e.Expiry wm.CodeExpiry = e.Expiry
wm.UserState = domain.UserStateInitial
case *user.HumanInitializedCheckSucceededEvent: case *user.HumanInitializedCheckSucceededEvent:
wm.Code = nil wm.Code = nil
wm.UserState = domain.UserStateActive
case *user.UserRemovedEvent: case *user.UserRemovedEvent:
wm.UserState = domain.UserStateDeleted wm.UserState = domain.UserStateDeleted
} }

View File

@ -126,6 +126,12 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
true, true,
), ),
), ),
eventFromEventPusher(
user.NewHumanInitialCodeAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
nil, time.Hour*1,
),
),
), ),
expectPush( expectPush(
[]*repository.Event{ []*repository.Event{
@ -178,6 +184,12 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
true, true,
), ),
), ),
eventFromEventPusher(
user.NewHumanInitialCodeAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
nil, time.Hour*1,
),
),
), ),
expectPush( expectPush(
[]*repository.Event{ []*repository.Event{
@ -229,6 +241,12 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
true, true,
), ),
), ),
eventFromEventPusher(
user.NewHumanInitialCodeAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
nil, time.Hour*1,
),
),
), ),
expectPush( expectPush(
[]*repository.Event{ []*repository.Event{

View File

@ -54,6 +54,10 @@ func (wm *HumanWriteModel) Reduce() error {
wm.reduceHumanAddedEvent(e) wm.reduceHumanAddedEvent(e)
case *user.HumanRegisteredEvent: case *user.HumanRegisteredEvent:
wm.reduceHumanRegisteredEvent(e) wm.reduceHumanRegisteredEvent(e)
case *user.HumanInitialCodeAddedEvent:
wm.UserState = domain.UserStateInitial
case *user.HumanInitializedCheckSucceededEvent:
wm.UserState = domain.UserStateActive
case *user.UsernameChangedEvent: case *user.UsernameChangedEvent:
wm.UserName = e.UserName wm.UserName = e.UserName
case *user.HumanProfileChangedEvent: case *user.HumanProfileChangedEvent:
@ -128,7 +132,7 @@ func (wm *HumanWriteModel) reduceHumanAddedEvent(e *user.HumanAddedEvent) {
wm.StreetAddress = e.StreetAddress wm.StreetAddress = e.StreetAddress
wm.Secret = e.Secret wm.Secret = e.Secret
wm.SecretChangeRequired = e.ChangeRequired wm.SecretChangeRequired = e.ChangeRequired
wm.UserState = domain.UserStateInitial wm.UserState = domain.UserStateActive
} }
func (wm *HumanWriteModel) reduceHumanRegisteredEvent(e *user.HumanRegisteredEvent) { func (wm *HumanWriteModel) reduceHumanRegisteredEvent(e *user.HumanRegisteredEvent) {
@ -148,7 +152,7 @@ func (wm *HumanWriteModel) reduceHumanRegisteredEvent(e *user.HumanRegisteredEve
wm.StreetAddress = e.StreetAddress wm.StreetAddress = e.StreetAddress
wm.Secret = e.Secret wm.Secret = e.Secret
wm.SecretChangeRequired = e.ChangeRequired wm.SecretChangeRequired = e.ChangeRequired
wm.UserState = domain.UserStateInitial wm.UserState = domain.UserStateActive
} }
func (wm *HumanWriteModel) reduceHumanProfileChangedEvent(e *user.HumanProfileChangedEvent) { func (wm *HumanWriteModel) reduceHumanProfileChangedEvent(e *user.HumanProfileChangedEvent) {

View File

@ -37,11 +37,15 @@ func (wm *HumanPasswordWriteModel) Reduce() error {
case *user.HumanAddedEvent: case *user.HumanAddedEvent:
wm.Secret = e.Secret wm.Secret = e.Secret
wm.SecretChangeRequired = e.ChangeRequired wm.SecretChangeRequired = e.ChangeRequired
wm.UserState = domain.UserStateInitial wm.UserState = domain.UserStateActive
case *user.HumanRegisteredEvent: case *user.HumanRegisteredEvent:
wm.Secret = e.Secret wm.Secret = e.Secret
wm.SecretChangeRequired = e.ChangeRequired wm.SecretChangeRequired = e.ChangeRequired
wm.UserState = domain.UserStateActive wm.UserState = domain.UserStateActive
case *user.HumanInitialCodeAddedEvent:
wm.UserState = domain.UserStateInitial
case *user.HumanInitializedCheckSucceededEvent:
wm.UserState = domain.UserStateActive
case *user.HumanPasswordChangedEvent: case *user.HumanPasswordChangedEvent:
wm.Secret = e.Secret wm.Secret = e.Secret
wm.SecretChangeRequired = e.ChangeRequired wm.SecretChangeRequired = e.ChangeRequired

View File

@ -757,7 +757,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) {
}, },
}, },
{ {
name: "phone already verified, precondition error", name: "user initial, precondition error",
fields: fields{ fields: fields{
eventstore: eventstoreExpect( eventstore: eventstoreExpect(
t, t,
@ -776,6 +776,12 @@ func TestCommandSide_RequestSetPassword(t *testing.T) {
true, true,
), ),
), ),
eventFromEventPusher(
user.NewHumanInitialCodeAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
nil, time.Hour*1,
),
),
eventFromEventPusher( eventFromEventPusher(
user.NewHumanPhoneChangedEvent(context.Background(), user.NewHumanPhoneChangedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate, &user.NewAggregate("user1", "org1").Aggregate,

View File

@ -48,6 +48,10 @@ func (wm *HumanPhoneWriteModel) Reduce() error {
wm.State = domain.PhoneStateActive wm.State = domain.PhoneStateActive
} }
wm.UserState = domain.UserStateActive wm.UserState = domain.UserStateActive
case *user.HumanInitialCodeAddedEvent:
wm.UserState = domain.UserStateInitial
case *user.HumanInitializedCheckSucceededEvent:
wm.UserState = domain.UserStateActive
case *user.HumanPhoneChangedEvent: case *user.HumanPhoneChangedEvent:
wm.Phone = e.PhoneNumber wm.Phone = e.PhoneNumber
wm.IsPhoneVerified = false wm.IsPhoneVerified = false

View File

@ -474,7 +474,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
EmailAddress: "email@test.ch", EmailAddress: "email@test.ch",
IsEmailVerified: true, IsEmailVerified: true,
}, },
State: domain.UserStateInitial, State: domain.UserStateActive,
}, },
}, },
}, },
@ -1048,7 +1048,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
EmailAddress: "email@test.ch", EmailAddress: "email@test.ch",
IsEmailVerified: true, IsEmailVerified: true,
}, },
State: domain.UserStateInitial, State: domain.UserStateActive,
}, },
}, },
}, },

View File

@ -173,6 +173,7 @@ func (c *Commands) HumanVerifyU2FSetup(ctx context.Context, userID, resourceowne
webAuthN.PublicKey, webAuthN.PublicKey,
webAuthN.AAGUID, webAuthN.AAGUID,
webAuthN.SignCount, webAuthN.SignCount,
userAgentID,
), ),
) )
if err != nil { if err != nil {
@ -206,6 +207,7 @@ func (c *Commands) HumanHumanPasswordlessSetup(ctx context.Context, userID, reso
webAuthN.PublicKey, webAuthN.PublicKey,
webAuthN.AAGUID, webAuthN.AAGUID,
webAuthN.SignCount, webAuthN.SignCount,
userAgentID,
), ),
) )
if err != nil { if err != nil {
@ -436,7 +438,7 @@ func (c *Commands) removeHumanWebAuthN(ctx context.Context, userID, webAuthNID,
return nil, err return nil, err
} }
if existingWebAuthN.State == domain.MFAStateUnspecified || existingWebAuthN.State == domain.MFAStateRemoved { if existingWebAuthN.State == domain.MFAStateUnspecified || existingWebAuthN.State == domain.MFAStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-2M9ds", "Errors.User.ExternalIDP.NotFound") return nil, caos_errs.ThrowNotFound(nil, "COMMAND-DAfb2", "Errors.User.WebAuthN.NotFound")
} }
userAgg := UserAggregateFromWriteModel(&existingWebAuthN.WriteModel) userAgg := UserAggregateFromWriteModel(&existingWebAuthN.WriteModel)

View File

@ -39,18 +39,50 @@ func (wm *HumanWebAuthNWriteModel) AppendEvents(events ...eventstore.EventReader
if wm.WebauthNTokenID == e.WebAuthNTokenID { if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(e) wm.WriteModel.AppendEvents(e)
} }
case *user.HumanPasswordlessAddedEvent:
if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(&e.HumanWebAuthNAddedEvent)
}
case *user.HumanU2FAddedEvent:
if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(&e.HumanWebAuthNAddedEvent)
}
case *user.HumanWebAuthNVerifiedEvent: case *user.HumanWebAuthNVerifiedEvent:
if wm.WebauthNTokenID == e.WebAuthNTokenID { if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(e) wm.WriteModel.AppendEvents(e)
} }
case *user.HumanPasswordlessVerifiedEvent:
if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(e)
}
case *user.HumanU2FVerifiedEvent:
if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(e)
}
case *user.HumanWebAuthNSignCountChangedEvent: case *user.HumanWebAuthNSignCountChangedEvent:
if wm.WebauthNTokenID == e.WebAuthNTokenID { if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(e) wm.WriteModel.AppendEvents(e)
} }
case *user.HumanPasswordlessSignCountChangedEvent:
if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(e)
}
case *user.HumanU2FSignCountChangedEvent:
if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(e)
}
case *user.HumanWebAuthNRemovedEvent: case *user.HumanWebAuthNRemovedEvent:
if wm.WebauthNTokenID == e.WebAuthNTokenID { if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(e) wm.WriteModel.AppendEvents(e)
} }
case *user.HumanPasswordlessRemovedEvent:
if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(e)
}
case *user.HumanU2FRemovedEvent:
if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.WriteModel.AppendEvents(e)
}
case *user.UserRemovedEvent: case *user.UserRemovedEvent:
wm.WriteModel.AppendEvents(e) wm.WriteModel.AppendEvents(e)
} }

View File

@ -30,9 +30,11 @@ func (wm *UserWriteModel) Reduce() error {
switch e := event.(type) { switch e := event.(type) {
case *user.HumanAddedEvent: case *user.HumanAddedEvent:
wm.UserName = e.UserName wm.UserName = e.UserName
wm.UserState = domain.UserStateInitial wm.UserState = domain.UserStateActive
case *user.HumanRegisteredEvent: case *user.HumanRegisteredEvent:
wm.UserName = e.UserName wm.UserName = e.UserName
wm.UserState = domain.UserStateActive
case *user.HumanInitialCodeAddedEvent:
wm.UserState = domain.UserStateInitial wm.UserState = domain.UserStateInitial
case *user.HumanInitializedCheckSucceededEvent: case *user.HumanInitializedCheckSucceededEvent:
wm.UserState = domain.UserStateActive wm.UserState = domain.UserStateActive

View File

@ -6,17 +6,18 @@ import (
) )
type IAMMemberView struct { type IAMMemberView struct {
UserID string UserID string
IAMID string IAMID string
UserName string UserName string
Email string Email string
FirstName string FirstName string
LastName string LastName string
DisplayName string DisplayName string
Roles []string PreferredLoginName string
CreationDate time.Time Roles []string
ChangeDate time.Time CreationDate time.Time
Sequence uint64 ChangeDate time.Time
Sequence uint64
} }
type IAMMemberSearchRequest struct { type IAMMemberSearchRequest struct {

View File

@ -23,15 +23,16 @@ const (
) )
type IAMMemberView struct { type IAMMemberView struct {
UserID string `json:"userId" gorm:"column:user_id;primary_key"` UserID string `json:"userId" gorm:"column:user_id;primary_key"`
IAMID string `json:"-" gorm:"column:iam_id"` IAMID string `json:"-" gorm:"column:iam_id"`
UserName string `json:"-" gorm:"column:user_name"` UserName string `json:"-" gorm:"column:user_name"`
Email string `json:"-" gorm:"column:email_address"` Email string `json:"-" gorm:"column:email_address"`
FirstName string `json:"-" gorm:"column:first_name"` FirstName string `json:"-" gorm:"column:first_name"`
LastName string `json:"-" gorm:"column:last_name"` LastName string `json:"-" gorm:"column:last_name"`
DisplayName string `json:"-" gorm:"column:display_name"` DisplayName string `json:"-" gorm:"column:display_name"`
Roles pq.StringArray `json:"roles" gorm:"column:roles"` Roles pq.StringArray `json:"roles" gorm:"column:roles"`
Sequence uint64 `json:"-" gorm:"column:sequence"` Sequence uint64 `json:"-" gorm:"column:sequence"`
PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"` CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"` ChangeDate time.Time `json:"-" gorm:"column:change_date"`
@ -55,17 +56,18 @@ func IAMMemberViewFromModel(member *model.IAMMemberView) *IAMMemberView {
func IAMMemberToModel(member *IAMMemberView) *model.IAMMemberView { func IAMMemberToModel(member *IAMMemberView) *model.IAMMemberView {
return &model.IAMMemberView{ return &model.IAMMemberView{
UserID: member.UserID, UserID: member.UserID,
IAMID: member.IAMID, IAMID: member.IAMID,
UserName: member.UserName, UserName: member.UserName,
Email: member.Email, Email: member.Email,
FirstName: member.FirstName, FirstName: member.FirstName,
LastName: member.LastName, LastName: member.LastName,
DisplayName: member.DisplayName, DisplayName: member.DisplayName,
Roles: member.Roles, PreferredLoginName: member.PreferredLoginName,
Sequence: member.Sequence, Roles: member.Roles,
CreationDate: member.CreationDate, Sequence: member.Sequence,
ChangeDate: member.ChangeDate, CreationDate: member.CreationDate,
ChangeDate: member.ChangeDate,
} }
} }

View File

@ -2,16 +2,23 @@ package handler
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1" "github.com/caos/zitadel/internal/eventstore/v1"
es_sdk "github.com/caos/zitadel/internal/eventstore/v1/sdk"
iam_model "github.com/caos/zitadel/internal/iam/model"
iam_view "github.com/caos/zitadel/internal/iam/repository/view"
org_view "github.com/caos/zitadel/internal/org/repository/view"
"github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view"
"github.com/caos/logging" "github.com/caos/logging"
es_models "github.com/caos/zitadel/internal/eventstore/v1/models" es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/eventstore/v1/query" "github.com/caos/zitadel/internal/eventstore/v1/query"
"github.com/caos/zitadel/internal/eventstore/v1/spooler" "github.com/caos/zitadel/internal/eventstore/v1/spooler"
iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
org_model "github.com/caos/zitadel/internal/org/model"
"github.com/caos/zitadel/internal/org/repository/eventsourcing/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
org_model "github.com/caos/zitadel/internal/org/repository/view/model" org_view_model "github.com/caos/zitadel/internal/org/repository/view/model"
usr_model "github.com/caos/zitadel/internal/user/model" usr_model "github.com/caos/zitadel/internal/user/model"
usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
usr_view_model "github.com/caos/zitadel/internal/user/repository/view/model" usr_view_model "github.com/caos/zitadel/internal/user/repository/view/model"
@ -84,7 +91,7 @@ func (m *OrgMember) Reduce(event *es_models.Event) (err error) {
} }
func (m *OrgMember) processOrgMember(event *es_models.Event) (err error) { func (m *OrgMember) processOrgMember(event *es_models.Event) (err error) {
member := new(org_model.OrgMemberView) member := new(org_view_model.OrgMemberView)
switch event.Type { switch event.Type {
case model.OrgMemberAdded: case model.OrgMemberAdded:
err = member.AppendEvent(event) err = member.AppendEvent(event)
@ -146,17 +153,25 @@ func (m *OrgMember) processUser(event *es_models.Event) (err error) {
} }
} }
func (m *OrgMember) fillData(member *org_model.OrgMemberView) (err error) { func (m *OrgMember) fillData(member *org_view_model.OrgMemberView) (err error) {
user, err := m.getUserByID(member.UserID) user, err := m.getUserByID(member.UserID)
if err != nil { if err != nil {
return err return err
} }
m.fillUserData(member, user) return m.fillUserData(member, user)
return nil
} }
func (m *OrgMember) fillUserData(member *org_model.OrgMemberView, user *usr_view_model.UserView) { func (m *OrgMember) fillUserData(member *org_view_model.OrgMemberView, user *usr_view_model.UserView) error {
org, err := m.getOrgByID(context.Background(), user.ResourceOwner)
policy := org.OrgIamPolicy
if policy == nil {
policy, err = m.getDefaultOrgIAMPolicy(context.TODO())
if err != nil {
return err
}
}
member.UserName = user.UserName member.UserName = user.UserName
member.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain)
if user.HumanView != nil { if user.HumanView != nil {
member.FirstName = user.FirstName member.FirstName = user.FirstName
member.LastName = user.LastName member.LastName = user.LastName
@ -166,7 +181,9 @@ func (m *OrgMember) fillUserData(member *org_model.OrgMemberView, user *usr_view
if user.MachineView != nil { if user.MachineView != nil {
member.DisplayName = user.MachineView.Name member.DisplayName = user.MachineView.Name
} }
return nil
} }
func (m *OrgMember) OnError(event *es_models.Event, err error) error { func (m *OrgMember) OnError(event *es_models.Event, err error) error {
logging.LogWithFields("SPOOL-u73es", "id", event.AggregateID).WithError(err).Warn("something went wrong in orgmember handler") logging.LogWithFields("SPOOL-u73es", "id", event.AggregateID).WithError(err).Warn("something went wrong in orgmember handler")
return spooler.HandleError(event, err, m.view.GetLatestOrgMemberFailedEvent, m.view.ProcessedOrgMemberFailedEvent, m.view.ProcessedOrgMemberSequence, m.errorCountUntilSkip) return spooler.HandleError(event, err, m.view.GetLatestOrgMemberFailedEvent, m.view.ProcessedOrgMemberFailedEvent, m.view.ProcessedOrgMemberSequence, m.errorCountUntilSkip)
@ -208,3 +225,53 @@ func (u *OrgMember) getUserEvents(userID string, sequence uint64) ([]*es_models.
return u.es.FilterEvents(context.Background(), query) return u.es.FilterEvents(context.Background(), query)
} }
func (u *OrgMember) getOrgByID(ctx context.Context, orgID string) (*org_model.Org, error) {
query, err := org_view.OrgByIDQuery(orgID, 0)
if err != nil {
return nil, err
}
esOrg := &model.Org{
ObjectRoot: es_models.ObjectRoot{
AggregateID: orgID,
},
}
err = es_sdk.Filter(ctx, u.Eventstore().FilterEvents, esOrg.AppendEvents, query)
if err != nil && !caos_errs.IsNotFound(err) {
return nil, err
}
if esOrg.Sequence == 0 {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-kVLb2", "Errors.Org.NotFound")
}
return model.OrgToModel(esOrg), nil
}
func (u *OrgMember) getDefaultOrgIAMPolicy(ctx context.Context) (*iam_model.OrgIAMPolicy, error) {
existingIAM, err := u.getIAMByID(ctx)
if err != nil {
return nil, err
}
if existingIAM.DefaultOrgIAMPolicy == nil {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-2Fj8s", "Errors.IAM.OrgIAMPolicy.NotExisting")
}
return existingIAM.DefaultOrgIAMPolicy, nil
}
func (u *OrgMember) getIAMByID(ctx context.Context) (*iam_model.IAM, error) {
query, err := iam_view.IAMByIDQuery(domain.IAMID, 0)
if err != nil {
return nil, err
}
iam := &iam_es_model.IAM{
ObjectRoot: es_models.ObjectRoot{
AggregateID: domain.IAMID,
},
}
err = es_sdk.Filter(ctx, u.Eventstore().FilterEvents, iam.AppendEvents, query)
if err != nil && caos_errs.IsNotFound(err) && iam.Sequence == 0 {
return nil, err
}
return iam_es_model.IAMToModel(iam), nil
}

View File

@ -2,8 +2,16 @@ package handler
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1" "github.com/caos/zitadel/internal/eventstore/v1"
es_sdk "github.com/caos/zitadel/internal/eventstore/v1/sdk"
iam_model "github.com/caos/zitadel/internal/iam/model"
iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
iam_view "github.com/caos/zitadel/internal/iam/repository/view"
org_model "github.com/caos/zitadel/internal/org/model"
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
org_view "github.com/caos/zitadel/internal/org/repository/view"
"github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view"
usr_view_model "github.com/caos/zitadel/internal/user/repository/view/model" usr_view_model "github.com/caos/zitadel/internal/user/repository/view/model"
@ -160,8 +168,17 @@ func (p *ProjectGrantMember) fillData(member *view_model.ProjectGrantMemberView)
return nil return nil
} }
func (p *ProjectGrantMember) fillUserData(member *view_model.ProjectGrantMemberView, user *usr_view_model.UserView) { func (p *ProjectGrantMember) fillUserData(member *view_model.ProjectGrantMemberView, user *usr_view_model.UserView) error {
org, err := p.getOrgByID(context.Background(), user.ResourceOwner)
policy := org.OrgIamPolicy
if policy == nil {
policy, err = p.getDefaultOrgIAMPolicy(context.TODO())
if err != nil {
return err
}
}
member.UserName = user.UserName member.UserName = user.UserName
member.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain)
if user.HumanView != nil { if user.HumanView != nil {
member.FirstName = user.FirstName member.FirstName = user.FirstName
member.LastName = user.LastName member.LastName = user.LastName
@ -171,6 +188,7 @@ func (p *ProjectGrantMember) fillUserData(member *view_model.ProjectGrantMemberV
if user.MachineView != nil { if user.MachineView != nil {
member.DisplayName = user.MachineView.Name member.DisplayName = user.MachineView.Name
} }
return nil
} }
func (p *ProjectGrantMember) OnError(event *es_models.Event, err error) error { func (p *ProjectGrantMember) OnError(event *es_models.Event, err error) error {
@ -214,3 +232,53 @@ func (u *ProjectGrantMember) getUserEvents(userID string, sequence uint64) ([]*e
return u.es.FilterEvents(context.Background(), query) return u.es.FilterEvents(context.Background(), query)
} }
func (u *ProjectGrantMember) getOrgByID(ctx context.Context, orgID string) (*org_model.Org, error) {
query, err := org_view.OrgByIDQuery(orgID, 0)
if err != nil {
return nil, err
}
esOrg := &org_es_model.Org{
ObjectRoot: es_models.ObjectRoot{
AggregateID: orgID,
},
}
err = es_sdk.Filter(ctx, u.Eventstore().FilterEvents, esOrg.AppendEvents, query)
if err != nil && !caos_errs.IsNotFound(err) {
return nil, err
}
if esOrg.Sequence == 0 {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-3nd7s", "Errors.Org.NotFound")
}
return org_es_model.OrgToModel(esOrg), nil
}
func (u *ProjectGrantMember) getDefaultOrgIAMPolicy(ctx context.Context) (*iam_model.OrgIAMPolicy, error) {
existingIAM, err := u.getIAMByID(ctx)
if err != nil {
return nil, err
}
if existingIAM.DefaultOrgIAMPolicy == nil {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-3Bf7s", "Errors.IAM.OrgIAMPolicy.NotExisting")
}
return existingIAM.DefaultOrgIAMPolicy, nil
}
func (u *ProjectGrantMember) getIAMByID(ctx context.Context) (*iam_model.IAM, error) {
query, err := iam_view.IAMByIDQuery(domain.IAMID, 0)
if err != nil {
return nil, err
}
iam := &iam_es_model.IAM{
ObjectRoot: es_models.ObjectRoot{
AggregateID: domain.IAMID,
},
}
err = es_sdk.Filter(ctx, u.Eventstore().FilterEvents, iam.AppendEvents, query)
if err != nil && caos_errs.IsNotFound(err) && iam.Sequence == 0 {
return nil, err
}
return iam_es_model.IAMToModel(iam), nil
}

View File

@ -2,8 +2,15 @@ package handler
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1" "github.com/caos/zitadel/internal/eventstore/v1"
es_sdk "github.com/caos/zitadel/internal/eventstore/v1/sdk"
iam_model "github.com/caos/zitadel/internal/iam/model"
iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
iam_view "github.com/caos/zitadel/internal/iam/repository/view"
org_model "github.com/caos/zitadel/internal/org/model"
org_view "github.com/caos/zitadel/internal/org/repository/view"
"github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view"
"github.com/caos/logging" "github.com/caos/logging"
@ -11,6 +18,7 @@ import (
es_models "github.com/caos/zitadel/internal/eventstore/v1/models" es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/eventstore/v1/query" "github.com/caos/zitadel/internal/eventstore/v1/query"
"github.com/caos/zitadel/internal/eventstore/v1/spooler" "github.com/caos/zitadel/internal/eventstore/v1/spooler"
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
view_model "github.com/caos/zitadel/internal/project/repository/view/model" view_model "github.com/caos/zitadel/internal/project/repository/view/model"
usr_model "github.com/caos/zitadel/internal/user/model" usr_model "github.com/caos/zitadel/internal/user/model"
@ -153,12 +161,20 @@ func (p *ProjectMember) fillData(member *view_model.ProjectMemberView) (err erro
if err != nil { if err != nil {
return err return err
} }
p.fillUserData(member, user) return p.fillUserData(member, user)
return nil
} }
func (p *ProjectMember) fillUserData(member *view_model.ProjectMemberView, user *usr_view_model.UserView) { func (p *ProjectMember) fillUserData(member *view_model.ProjectMemberView, user *usr_view_model.UserView) error {
org, err := p.getOrgByID(context.Background(), user.ResourceOwner)
policy := org.OrgIamPolicy
if policy == nil {
policy, err = p.getDefaultOrgIAMPolicy(context.TODO())
if err != nil {
return err
}
}
member.UserName = user.UserName member.UserName = user.UserName
member.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain)
if user.HumanView != nil { if user.HumanView != nil {
member.FirstName = user.FirstName member.FirstName = user.FirstName
member.LastName = user.LastName member.LastName = user.LastName
@ -168,7 +184,9 @@ func (p *ProjectMember) fillUserData(member *view_model.ProjectMemberView, user
if user.MachineView != nil { if user.MachineView != nil {
member.DisplayName = user.MachineView.Name member.DisplayName = user.MachineView.Name
} }
return nil
} }
func (p *ProjectMember) OnError(event *es_models.Event, err error) error { func (p *ProjectMember) OnError(event *es_models.Event, err error) error {
logging.LogWithFields("SPOOL-u73es", "id", event.AggregateID).WithError(err).Warn("something went wrong in projectmember handler") logging.LogWithFields("SPOOL-u73es", "id", event.AggregateID).WithError(err).Warn("something went wrong in projectmember handler")
return spooler.HandleError(event, err, p.view.GetLatestProjectMemberFailedEvent, p.view.ProcessedProjectMemberFailedEvent, p.view.ProcessedProjectMemberSequence, p.errorCountUntilSkip) return spooler.HandleError(event, err, p.view.GetLatestProjectMemberFailedEvent, p.view.ProcessedProjectMemberFailedEvent, p.view.ProcessedProjectMemberSequence, p.errorCountUntilSkip)
@ -210,3 +228,53 @@ func (u *ProjectMember) getUserEvents(userID string, sequence uint64) ([]*es_mod
return u.es.FilterEvents(context.Background(), query) return u.es.FilterEvents(context.Background(), query)
} }
func (u *ProjectMember) getOrgByID(ctx context.Context, orgID string) (*org_model.Org, error) {
query, err := org_view.OrgByIDQuery(orgID, 0)
if err != nil {
return nil, err
}
esOrg := &org_es_model.Org{
ObjectRoot: es_models.ObjectRoot{
AggregateID: orgID,
},
}
err = es_sdk.Filter(ctx, u.Eventstore().FilterEvents, esOrg.AppendEvents, query)
if err != nil && !caos_errs.IsNotFound(err) {
return nil, err
}
if esOrg.Sequence == 0 {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-3N8fs", "Errors.Org.NotFound")
}
return org_es_model.OrgToModel(esOrg), nil
}
func (u *ProjectMember) getDefaultOrgIAMPolicy(ctx context.Context) (*iam_model.OrgIAMPolicy, error) {
existingIAM, err := u.getIAMByID(ctx)
if err != nil {
return nil, err
}
if existingIAM.DefaultOrgIAMPolicy == nil {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-5M9sd", "Errors.IAM.OrgIAMPolicy.NotExisting")
}
return existingIAM.DefaultOrgIAMPolicy, nil
}
func (u *ProjectMember) getIAMByID(ctx context.Context) (*iam_model.IAM, error) {
query, err := iam_view.IAMByIDQuery(domain.IAMID, 0)
if err != nil {
return nil, err
}
iam := &iam_es_model.IAM{
ObjectRoot: es_models.ObjectRoot{
AggregateID: domain.IAMID,
},
}
err = es_sdk.Filter(ctx, u.Eventstore().FilterEvents, iam.AppendEvents, query)
if err != nil && caos_errs.IsNotFound(err) && iam.Sequence == 0 {
return nil, err
}
return iam_es_model.IAMToModel(iam), nil
}

View File

@ -32,9 +32,9 @@ func (msg *EmailMessage) GetContent() string {
} }
//default mime-type is html //default mime-type is html
mime := "MIME-version: 1.0;" + lineBreak + "Content-KeyType: text/html; charset=\"UTF-8\";" + lineBreak + lineBreak mime := "MIME-version: 1.0;" + lineBreak + "Content-Type: text/html; charset=\"UTF-8\";" + lineBreak + lineBreak
if !isHTML(msg.Content) { if !isHTML(msg.Content) {
mime = "MIME-version: 1.0;" + lineBreak + "Content-KeyType: text/plain; charset=\"UTF-8\";" + lineBreak + lineBreak mime = "MIME-version: 1.0;" + lineBreak + "Content-Type: text/plain; charset=\"UTF-8\";" + lineBreak + lineBreak
} }
subject := "Subject: " + msg.Subject + lineBreak subject := "Subject: " + msg.Subject + lineBreak
message += subject + mime + lineBreak + msg.Content message += subject + mime + lineBreak + msg.Content

View File

@ -2,6 +2,7 @@ package handler
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1" "github.com/caos/zitadel/internal/eventstore/v1"
@ -12,6 +13,7 @@ import (
org_view "github.com/caos/zitadel/internal/org/repository/view" org_view "github.com/caos/zitadel/internal/org/repository/view"
"github.com/caos/logging" "github.com/caos/logging"
es_models "github.com/caos/zitadel/internal/eventstore/v1/models" es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/eventstore/v1/query" "github.com/caos/zitadel/internal/eventstore/v1/query"
"github.com/caos/zitadel/internal/eventstore/v1/spooler" "github.com/caos/zitadel/internal/eventstore/v1/spooler"
@ -97,9 +99,13 @@ func (u *NotifyUser) ProcessUser(event *es_models.Event) (err error) {
case es_model.UserAdded, case es_model.UserAdded,
es_model.UserRegistered, es_model.UserRegistered,
es_model.HumanRegistered, es_model.HumanRegistered,
es_model.HumanAdded: es_model.HumanAdded,
user.AppendEvent(event) es_model.MachineAdded:
u.fillLoginNames(user) err := user.AppendEvent(event)
if err != nil {
return err
}
err = u.fillLoginNames(user)
case es_model.UserProfileChanged, case es_model.UserProfileChanged,
es_model.UserEmailChanged, es_model.UserEmailChanged,
es_model.UserEmailVerified, es_model.UserEmailVerified,
@ -128,7 +134,7 @@ func (u *NotifyUser) ProcessUser(event *es_models.Event) (err error) {
if err != nil { if err != nil {
return err return err
} }
u.fillLoginNames(user) err = u.fillLoginNames(user)
case es_model.UserRemoved: case es_model.UserRemoved:
return u.view.DeleteNotifyUser(event.AggregateID, event) return u.view.DeleteNotifyUser(event.AggregateID, event)
default: default:

View File

@ -1,22 +1,24 @@
package model package model
import ( import (
"github.com/caos/zitadel/internal/domain"
"time" "time"
"github.com/caos/zitadel/internal/domain"
) )
type OrgMemberView struct { type OrgMemberView struct {
UserID string UserID string
OrgID string OrgID string
UserName string UserName string
Email string Email string
FirstName string FirstName string
LastName string LastName string
DisplayName string DisplayName string
Roles []string PreferredLoginName string
CreationDate time.Time Roles []string
ChangeDate time.Time CreationDate time.Time
Sequence uint64 ChangeDate time.Time
Sequence uint64
} }
type OrgMemberSearchRequest struct { type OrgMemberSearchRequest struct {

View File

@ -23,15 +23,16 @@ const (
) )
type OrgMemberView struct { type OrgMemberView struct {
UserID string `json:"userId" gorm:"column:user_id;primary_key"` UserID string `json:"userId" gorm:"column:user_id;primary_key"`
OrgID string `json:"-" gorm:"column:org_id;primary_key"` OrgID string `json:"-" gorm:"column:org_id;primary_key"`
UserName string `json:"-" gorm:"column:user_name"` UserName string `json:"-" gorm:"column:user_name"`
Email string `json:"-" gorm:"column:email_address"` Email string `json:"-" gorm:"column:email_address"`
FirstName string `json:"-" gorm:"column:first_name"` FirstName string `json:"-" gorm:"column:first_name"`
LastName string `json:"-" gorm:"column:last_name"` LastName string `json:"-" gorm:"column:last_name"`
DisplayName string `json:"-" gorm:"column:display_name"` DisplayName string `json:"-" gorm:"column:display_name"`
Roles pq.StringArray `json:"roles" gorm:"column:roles"` Roles pq.StringArray `json:"roles" gorm:"column:roles"`
Sequence uint64 `json:"-" gorm:"column:sequence"` Sequence uint64 `json:"-" gorm:"column:sequence"`
PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"` CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"` ChangeDate time.Time `json:"-" gorm:"column:change_date"`
@ -39,17 +40,18 @@ type OrgMemberView struct {
func OrgMemberToModel(member *OrgMemberView) *model.OrgMemberView { func OrgMemberToModel(member *OrgMemberView) *model.OrgMemberView {
return &model.OrgMemberView{ return &model.OrgMemberView{
UserID: member.UserID, UserID: member.UserID,
OrgID: member.OrgID, OrgID: member.OrgID,
UserName: member.UserName, UserName: member.UserName,
Email: member.Email, Email: member.Email,
FirstName: member.FirstName, FirstName: member.FirstName,
LastName: member.LastName, LastName: member.LastName,
DisplayName: member.DisplayName, DisplayName: member.DisplayName,
Roles: member.Roles, PreferredLoginName: member.PreferredLoginName,
Sequence: member.Sequence, Roles: member.Roles,
CreationDate: member.CreationDate, Sequence: member.Sequence,
ChangeDate: member.ChangeDate, CreationDate: member.CreationDate,
ChangeDate: member.ChangeDate,
} }
} }

View File

@ -6,18 +6,19 @@ import (
) )
type ProjectGrantMemberView struct { type ProjectGrantMemberView struct {
UserID string UserID string
GrantID string GrantID string
ProjectID string ProjectID string
UserName string UserName string
Email string Email string
FirstName string FirstName string
LastName string LastName string
DisplayName string DisplayName string
Roles []string PreferredLoginName string
CreationDate time.Time Roles []string
ChangeDate time.Time CreationDate time.Time
Sequence uint64 ChangeDate time.Time
Sequence uint64
} }
type ProjectGrantMemberSearchRequest struct { type ProjectGrantMemberSearchRequest struct {

View File

@ -6,17 +6,18 @@ import (
) )
type ProjectMemberView struct { type ProjectMemberView struct {
UserID string UserID string
ProjectID string ProjectID string
UserName string UserName string
Email string Email string
FirstName string FirstName string
LastName string LastName string
DisplayName string DisplayName string
Roles []string PreferredLoginName string
CreationDate time.Time Roles []string
ChangeDate time.Time CreationDate time.Time
Sequence uint64 ChangeDate time.Time
Sequence uint64
} }
type ProjectMemberSearchRequest struct { type ProjectMemberSearchRequest struct {

View File

@ -5,11 +5,12 @@ import (
"time" "time"
"github.com/caos/logging" "github.com/caos/logging"
"github.com/lib/pq"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/model"
es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
"github.com/lib/pq"
) )
const ( const (
@ -23,16 +24,17 @@ const (
) )
type ProjectGrantMemberView struct { type ProjectGrantMemberView struct {
UserID string `json:"userId" gorm:"column:user_id;primary_key"` UserID string `json:"userId" gorm:"column:user_id;primary_key"`
GrantID string `json:"grantId" gorm:"column:grant_id;primary_key"` GrantID string `json:"grantId" gorm:"column:grant_id;primary_key"`
ProjectID string `json:"-" gorm:"column:project_id"` ProjectID string `json:"-" gorm:"column:project_id"`
UserName string `json:"-" gorm:"column:user_name"` UserName string `json:"-" gorm:"column:user_name"`
Email string `json:"-" gorm:"column:email_address"` Email string `json:"-" gorm:"column:email_address"`
FirstName string `json:"-" gorm:"column:first_name"` FirstName string `json:"-" gorm:"column:first_name"`
LastName string `json:"-" gorm:"column:last_name"` LastName string `json:"-" gorm:"column:last_name"`
DisplayName string `json:"-" gorm:"column:display_name"` DisplayName string `json:"-" gorm:"column:display_name"`
Roles pq.StringArray `json:"roles" gorm:"column:roles"` Roles pq.StringArray `json:"roles" gorm:"column:roles"`
Sequence uint64 `json:"-" gorm:"column:sequence"` Sequence uint64 `json:"-" gorm:"column:sequence"`
PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"` CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"` ChangeDate time.Time `json:"-" gorm:"column:change_date"`
@ -40,18 +42,19 @@ type ProjectGrantMemberView struct {
func ProjectGrantMemberToModel(member *ProjectGrantMemberView) *model.ProjectGrantMemberView { func ProjectGrantMemberToModel(member *ProjectGrantMemberView) *model.ProjectGrantMemberView {
return &model.ProjectGrantMemberView{ return &model.ProjectGrantMemberView{
UserID: member.UserID, UserID: member.UserID,
GrantID: member.GrantID, GrantID: member.GrantID,
ProjectID: member.ProjectID, ProjectID: member.ProjectID,
UserName: member.UserName, UserName: member.UserName,
Email: member.Email, Email: member.Email,
FirstName: member.FirstName, FirstName: member.FirstName,
LastName: member.LastName, LastName: member.LastName,
DisplayName: member.DisplayName, DisplayName: member.DisplayName,
Roles: member.Roles, PreferredLoginName: member.PreferredLoginName,
Sequence: member.Sequence, Roles: member.Roles,
CreationDate: member.CreationDate, Sequence: member.Sequence,
ChangeDate: member.ChangeDate, CreationDate: member.CreationDate,
ChangeDate: member.ChangeDate,
} }
} }

View File

@ -22,15 +22,16 @@ const (
) )
type ProjectMemberView struct { type ProjectMemberView struct {
UserID string `json:"userId" gorm:"column:user_id;primary_key"` UserID string `json:"userId" gorm:"column:user_id;primary_key"`
ProjectID string `json:"-" gorm:"column:project_id;primary_key"` ProjectID string `json:"-" gorm:"column:project_id;primary_key"`
UserName string `json:"-" gorm:"column:user_name"` UserName string `json:"-" gorm:"column:user_name"`
Email string `json:"-" gorm:"column:email_address"` Email string `json:"-" gorm:"column:email_address"`
FirstName string `json:"-" gorm:"column:first_name"` FirstName string `json:"-" gorm:"column:first_name"`
LastName string `json:"-" gorm:"column:last_name"` LastName string `json:"-" gorm:"column:last_name"`
DisplayName string `json:"-" gorm:"column:display_name"` DisplayName string `json:"-" gorm:"column:display_name"`
Roles pq.StringArray `json:"roles" gorm:"column:roles"` Roles pq.StringArray `json:"roles" gorm:"column:roles"`
Sequence uint64 `json:"-" gorm:"column:sequence"` Sequence uint64 `json:"-" gorm:"column:sequence"`
PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"` CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"` ChangeDate time.Time `json:"-" gorm:"column:change_date"`

View File

@ -64,6 +64,7 @@ func NewHumanPasswordlessVerifiedEvent(
publicKey, publicKey,
aaguid []byte, aaguid []byte,
signCount uint32, signCount uint32,
userAgentID string,
) *HumanPasswordlessVerifiedEvent { ) *HumanPasswordlessVerifiedEvent {
return &HumanPasswordlessVerifiedEvent{ return &HumanPasswordlessVerifiedEvent{
HumanWebAuthNVerifiedEvent: *NewHumanWebAuthNVerifiedEvent( HumanWebAuthNVerifiedEvent: *NewHumanWebAuthNVerifiedEvent(
@ -79,6 +80,7 @@ func NewHumanPasswordlessVerifiedEvent(
publicKey, publicKey,
aaguid, aaguid,
signCount, signCount,
userAgentID,
), ),
} }
} }

View File

@ -64,6 +64,7 @@ func NewHumanU2FVerifiedEvent(
publicKey, publicKey,
aaguid []byte, aaguid []byte,
signCount uint32, signCount uint32,
userAgentID string,
) *HumanU2FVerifiedEvent { ) *HumanU2FVerifiedEvent {
return &HumanU2FVerifiedEvent{ return &HumanU2FVerifiedEvent{
HumanWebAuthNVerifiedEvent: *NewHumanWebAuthNVerifiedEvent( HumanWebAuthNVerifiedEvent: *NewHumanWebAuthNVerifiedEvent(
@ -79,6 +80,7 @@ func NewHumanU2FVerifiedEvent(
publicKey, publicKey,
aaguid, aaguid,
signCount, signCount,
userAgentID,
), ),
} }
} }

View File

@ -2,6 +2,7 @@ package user
import ( import (
"encoding/json" "encoding/json"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
@ -56,6 +57,7 @@ type HumanWebAuthNVerifiedEvent struct {
AAGUID []byte `json:"aaguid"` AAGUID []byte `json:"aaguid"`
SignCount uint32 `json:"signCount"` SignCount uint32 `json:"signCount"`
WebAuthNTokenName string `json:"webAuthNTokenName"` WebAuthNTokenName string `json:"webAuthNTokenName"`
UserAgentID string `json:"userAgentID,omitempty"`
} }
func (e *HumanWebAuthNVerifiedEvent) Data() interface{} { func (e *HumanWebAuthNVerifiedEvent) Data() interface{} {
@ -75,6 +77,7 @@ func NewHumanWebAuthNVerifiedEvent(
publicKey, publicKey,
aaguid []byte, aaguid []byte,
signCount uint32, signCount uint32,
userAgentID string,
) *HumanWebAuthNVerifiedEvent { ) *HumanWebAuthNVerifiedEvent {
return &HumanWebAuthNVerifiedEvent{ return &HumanWebAuthNVerifiedEvent{
BaseEvent: *base, BaseEvent: *base,
@ -85,6 +88,7 @@ func NewHumanWebAuthNVerifiedEvent(
AAGUID: aaguid, AAGUID: aaguid,
SignCount: signCount, SignCount: signCount,
WebAuthNTokenName: webAuthNTokenName, WebAuthNTokenName: webAuthNTokenName,
UserAgentID: userAgentID,
} }
} }

View File

@ -69,8 +69,6 @@ func MachineAddedEventMapper(event *repository.Event) (eventstore.EventReader, e
type MachineChangedEvent struct { type MachineChangedEvent struct {
eventstore.BaseEvent `json:"-"` eventstore.BaseEvent `json:"-"`
UserName string `json:"userName"`
Name *string `json:"name,omitempty"` Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"` Description *string `json:"description,omitempty"`
} }

View File

@ -2,16 +2,18 @@ package model
import ( import (
"encoding/json" "encoding/json"
iam_model "github.com/caos/zitadel/internal/iam/model"
"time" "time"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/logging" "github.com/caos/logging"
"github.com/lib/pq"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
org_model "github.com/caos/zitadel/internal/org/model" org_model "github.com/caos/zitadel/internal/org/model"
"github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/model"
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
"github.com/lib/pq"
) )
const ( const (
@ -117,7 +119,8 @@ func (u *NotifyUser) AppendEvent(event *models.Event) (err error) {
case es_model.UserAdded, case es_model.UserAdded,
es_model.UserRegistered, es_model.UserRegistered,
es_model.HumanRegistered, es_model.HumanRegistered,
es_model.HumanAdded: es_model.HumanAdded,
es_model.MachineAdded:
u.CreationDate = event.CreationDate u.CreationDate = event.CreationDate
u.setRootData(event) u.setRootData(event)
err = u.setData(event) err = u.setData(event)
@ -130,7 +133,8 @@ func (u *NotifyUser) AppendEvent(event *models.Event) (err error) {
es_model.UserPhoneChanged, es_model.UserPhoneChanged,
es_model.HumanProfileChanged, es_model.HumanProfileChanged,
es_model.HumanEmailChanged, es_model.HumanEmailChanged,
es_model.HumanPhoneChanged: es_model.HumanPhoneChanged,
es_model.UserUserNameChanged:
err = u.setData(event) err = u.setData(event)
case es_model.UserEmailVerified, case es_model.UserEmailVerified,
es_model.HumanEmailVerified: es_model.HumanEmailVerified:

View File

@ -0,0 +1,5 @@
ALTER TABLE management.org_members ADD COLUMN preferred_login_name TEXT;
ALTER TABLE management.project_members ADD COLUMN preferred_login_name TEXT;
ALTER TABLE management.project_grant_members ADD COLUMN preferred_login_name TEXT;
ALTER TABLE adminapi.iam_members ADD COLUMN preferred_login_name TEXT;

View File

@ -11,16 +11,24 @@ func (a *App) Localizers() []middleware.Localizer {
switch configType := a.Config.(type) { switch configType := a.Config.(type) {
case *App_OidcConfig: case *App_OidcConfig:
if !configType.OidcConfig.NoneCompliant { return configType.ComplianceLocalizers()
return nil
}
localizers := make([]middleware.Localizer, len(configType.OidcConfig.ComplianceProblems))
for i, problem := range configType.OidcConfig.ComplianceProblems {
localizers[i] = problem
}
return localizers
} }
return nil return nil
} }
func (o *App_OidcConfig) ComplianceLocalizers() []middleware.Localizer {
if o.OidcConfig == nil {
return nil
}
if !o.OidcConfig.NoneCompliant {
return nil
}
localizers := make([]middleware.Localizer, len(o.OidcConfig.ComplianceProblems))
for i, problem := range o.OidcConfig.ComplianceProblems {
localizers[i] = problem
}
return localizers
}
type AppConfig = isApp_Config type AppConfig = isApp_Config

View File

@ -1,6 +1,28 @@
package management package management
import "github.com/caos/zitadel/internal/api/grpc/server/middleware" import (
"github.com/caos/zitadel/internal/api/grpc/server/middleware"
)
func (a *ListAppsResponse) Localizers() []middleware.Localizer {
if a == nil {
return nil
}
localizers := make([]middleware.Localizer, 0)
for _, a := range a.Result {
localizers = append(localizers, a.Localizers()...)
}
return localizers
}
func (a *GetAppByIDResponse) Localizers() []middleware.Localizer {
if a == nil || (a != nil && a.App == nil) {
return nil
}
return a.App.Localizers()
}
func (a *AddOIDCAppResponse) Localizers() []middleware.Localizer { func (a *AddOIDCAppResponse) Localizers() []middleware.Localizer {
if a == nil { if a == nil {

View File

@ -515,7 +515,9 @@ message VerifyMyPhoneRequest {
string code = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; string code = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
} }
message VerifyMyPhoneResponse {} message VerifyMyPhoneResponse {
zitadel.v1.ObjectDetails details = 1;
}
message ResendMyPhoneVerificationRequest {} message ResendMyPhoneVerificationRequest {}

View File

@ -388,7 +388,7 @@ service ManagementService {
rpc RemoveHumanAuthFactorU2F(RemoveHumanAuthFactorU2FRequest) returns (RemoveHumanAuthFactorU2FResponse) { rpc RemoveHumanAuthFactorU2F(RemoveHumanAuthFactorU2FRequest) returns (RemoveHumanAuthFactorU2FResponse) {
option (google.api.http) = { option (google.api.http) = {
delete: "/users/{user_id}/auth_factors/u2f" delete: "/users/{user_id}/auth_factors/u2f/{token_id}"
}; };
option (zitadel.v1.auth_option) = { option (zitadel.v1.auth_option) = {
@ -408,7 +408,7 @@ service ManagementService {
rpc RemoveHumanPasswordless(RemoveHumanPasswordlessRequest) returns (RemoveHumanPasswordlessResponse) { rpc RemoveHumanPasswordless(RemoveHumanPasswordlessRequest) returns (RemoveHumanPasswordlessResponse) {
option (google.api.http) = { option (google.api.http) = {
delete: "/users/{user_id}/passwordless" delete: "/users/{user_id}/passwordless/{token_id}"
}; };
option (zitadel.v1.auth_option) = { option (zitadel.v1.auth_option) = {
@ -429,8 +429,7 @@ service ManagementService {
rpc GetMachineKeyByIDs(GetMachineKeyByIDsRequest) returns (GetMachineKeyByIDsResponse) { rpc GetMachineKeyByIDs(GetMachineKeyByIDsRequest) returns (GetMachineKeyByIDsResponse) {
option (google.api.http) = { option (google.api.http) = {
post: "/users/{user_id}/keys/{key_id}" get: "/users/{user_id}/keys/{key_id}"
body: "*"
}; };
option (zitadel.v1.auth_option) = { option (zitadel.v1.auth_option) = {
@ -2206,9 +2205,8 @@ message GenerateOrgDomainValidationRequest {
} }
message GenerateOrgDomainValidationResponse { message GenerateOrgDomainValidationResponse {
zitadel.v1.ObjectDetails details = 1; string token = 1;
string token = 2; string url = 2;
string url = 3;
} }
message ValidateOrgDomainRequest { message ValidateOrgDomainRequest {

View File

@ -172,8 +172,7 @@ message AuthFactorU2F {
} }
message WebAuthNKey { message WebAuthNKey {
string id = 1; bytes public_key = 1;
bytes public_key = 2;
} }
message WebAuthNVerification { message WebAuthNVerification {