mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-01 13:53:08 +00:00
feat: new es testing2 (#1428)
* fix: org tests * fix: org tests * fix: user grant test * fix: user grant test * fix: project and project role test * fix: project grant test * fix: project grant test * fix: project member, grant member, app changed tests * fix: application tests * fix: application tests * fix: add oidc app test * fix: add oidc app test * fix: add api keys test * fix: iam policies * fix: iam and org member tests * fix: idp config tests * fix: iam tests * fix: user tests * fix: user tests * fix: user tests * fix: user tests * fix: user tests * fix: user tests * fix: user tests * fix: user tests * fix: user tests * fix: user tests * fix: org domain test * fix: org tests * fix: org tests * fix: implement org idps * fix: pr requests * fix: email tests * fix: fix idp check * fix: fix user profile
This commit is contained in:
@@ -170,7 +170,7 @@ func (i *IDPProvider) getOrgIDPConfig(ctx context.Context, aggregateID, idpConfi
|
|||||||
if _, i := existing.GetIDP(idpConfigID); i != nil {
|
if _, i := existing.GetIDP(idpConfigID); i != nil {
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
return nil, errors.ThrowNotFound(nil, "EVENT-4m0fs", "Errors.Org.IdpNotExisting")
|
return nil, errors.ThrowNotFound(nil, "EVENT-4m0fs", "Errors.IDP.NotExisting")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IDPProvider) getOrgByID(ctx context.Context, orgID string) (*org_model.Org, error) {
|
func (i *IDPProvider) getOrgByID(ctx context.Context, orgID string) (*org_model.Org, error) {
|
||||||
@@ -220,5 +220,5 @@ func (u *IDPProvider) getDefaultIDPConfig(ctx context.Context, idpConfigID strin
|
|||||||
if _, existingIDP := existing.GetIDP(idpConfigID); existingIDP != nil {
|
if _, existingIDP := existing.GetIDP(idpConfigID); existingIDP != nil {
|
||||||
return existingIDP, nil
|
return existingIDP, nil
|
||||||
}
|
}
|
||||||
return nil, errors.ThrowNotFound(nil, "EVENT-4M=Fs", "Errors.IAM.IdpNotExisting")
|
return nil, errors.ThrowNotFound(nil, "EVENT-4M=Fs", "Errors.IDP.NotExisting")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ func (i *ExternalIDP) getOrgIDPConfig(ctx context.Context, aggregateID, idpConfi
|
|||||||
if _, i := existing.GetIDP(idpConfigID); i != nil {
|
if _, i := existing.GetIDP(idpConfigID); i != nil {
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "EVENT-2n8Fh", "Errors.Org.IdpNotExisting")
|
return nil, caos_errs.ThrowNotFound(nil, "EVENT-2n8Fh", "Errors.IDP.NotExisting")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ExternalIDP) getOrgByID(ctx context.Context, orgID string) (*org_model.Org, error) {
|
func (i *ExternalIDP) getOrgByID(ctx context.Context, orgID string) (*org_model.Org, error) {
|
||||||
@@ -231,5 +231,5 @@ func (u *ExternalIDP) getDefaultIDPConfig(ctx context.Context, idpConfigID strin
|
|||||||
if _, existingIDP := existing.GetIDP(idpConfigID); existingIDP != nil {
|
if _, existingIDP := existing.GetIDP(idpConfigID); existingIDP != nil {
|
||||||
return existingIDP, nil
|
return existingIDP, nil
|
||||||
}
|
}
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "EVENT-49O0f", "Errors.IAM.IdpNotExisting")
|
return nil, caos_errs.ThrowNotFound(nil, "EVENT-49O0f", "Errors.IDP.NotExisting")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -221,6 +221,17 @@ func ModelIDPProviderTypeToPb(typ iam_model.IDPProviderType) idp_pb.IDPOwnerType
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IDPProviderTypeFromPb(typ idp_pb.IDPOwnerType) domain.IdentityProviderType {
|
||||||
|
switch typ {
|
||||||
|
case idp_pb.IDPOwnerType_IDP_OWNER_TYPE_ORG:
|
||||||
|
return domain.IdentityProviderTypeOrg
|
||||||
|
case idp_pb.IDPOwnerType_IDP_OWNER_TYPE_SYSTEM:
|
||||||
|
return domain.IdentityProviderTypeSystem
|
||||||
|
default:
|
||||||
|
return domain.IdentityProviderTypeOrg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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, //TODO: whats the difference between idpconfigid and aggregateid search key?
|
||||||
|
|||||||
@@ -6,29 +6,84 @@ import (
|
|||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/api/authz"
|
||||||
|
idp_grpc "github.com/caos/zitadel/internal/api/grpc/idp"
|
||||||
|
object_pb "github.com/caos/zitadel/internal/api/grpc/object"
|
||||||
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
|
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) GetOrgIDPByID(ctx context.Context, req *mgmt_pb.GetOrgIDPByIDRequest) (*mgmt_pb.GetOrgIDPByIDResponse, error) {
|
func (s *Server) GetOrgIDPByID(ctx context.Context, req *mgmt_pb.GetOrgIDPByIDRequest) (*mgmt_pb.GetOrgIDPByIDResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetOrgIDPByID not implemented")
|
idp, err := s.org.IDPConfigByID(ctx, req.Id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &mgmt_pb.GetOrgIDPByIDResponse{Idp: idp_grpc.ModelIDPViewToPb(idp)}, nil
|
||||||
}
|
}
|
||||||
func (s *Server) ListOrgIDPs(ctx context.Context, req *mgmt_pb.ListOrgIDPsRequest) (*mgmt_pb.ListOrgIDPsResponse, error) {
|
func (s *Server) ListOrgIDPs(ctx context.Context, req *mgmt_pb.ListOrgIDPsRequest) (*mgmt_pb.ListOrgIDPsResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method ListOrgIDPs not implemented")
|
resp, err := s.org.SearchIDPConfigs(ctx, listIDPsToModel(req))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &mgmt_pb.ListOrgIDPsResponse{
|
||||||
|
Result: idp_grpc.IDPViewsToPb(resp.Result),
|
||||||
|
Details: object_pb.ToListDetails(resp.TotalResult, resp.Sequence, resp.Timestamp),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
func (s *Server) AddOrgOIDCIDP(ctx context.Context, req *mgmt_pb.AddOrgOIDCIDPRequest) (*mgmt_pb.AddOrgOIDCIDPResponse, error) {
|
func (s *Server) AddOrgOIDCIDP(ctx context.Context, req *mgmt_pb.AddOrgOIDCIDPRequest) (*mgmt_pb.AddOrgOIDCIDPResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method AddOrgOIDCIDP not implemented")
|
config, err := s.command.AddDefaultIDPConfig(ctx, addOIDCIDPRequestToDomain(req))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &mgmt_pb.AddOrgOIDCIDPResponse{
|
||||||
|
IdpId: config.AggregateID,
|
||||||
|
Details: object_pb.ToDetailsPb(
|
||||||
|
config.Sequence,
|
||||||
|
config.ChangeDate,
|
||||||
|
config.ResourceOwner,
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
func (s *Server) DeactivateOrgIDP(ctx context.Context, req *mgmt_pb.DeactivateOrgIDPRequest) (*mgmt_pb.DeactivateOrgIDPResponse, error) {
|
func (s *Server) DeactivateOrgIDP(ctx context.Context, req *mgmt_pb.DeactivateOrgIDPRequest) (*mgmt_pb.DeactivateOrgIDPResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method DeactivateOrgIDP not implemented")
|
objectDetails, err := s.command.DeactivateDefaultIDPConfig(ctx, req.IdpId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &mgmt_pb.DeactivateOrgIDPResponse{Details: object_pb.DomainToDetailsPb(objectDetails)}, nil
|
||||||
}
|
}
|
||||||
func (s *Server) ReactivateOrgIDP(ctx context.Context, req *mgmt_pb.ReactivateOrgIDPRequest) (*mgmt_pb.ReactivateOrgIDPResponse, error) {
|
func (s *Server) ReactivateOrgIDP(ctx context.Context, req *mgmt_pb.ReactivateOrgIDPRequest) (*mgmt_pb.ReactivateOrgIDPResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method ReactivateOrgIDP not implemented")
|
objectDetails, err := s.command.ReactivateDefaultIDPConfig(ctx, req.IdpId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &mgmt_pb.ReactivateOrgIDPResponse{Details: object_pb.DomainToDetailsPb(objectDetails)}, nil
|
||||||
}
|
}
|
||||||
func (s *Server) RemoveOrgIDP(ctx context.Context, req *mgmt_pb.RemoveOrgIDPRequest) (*mgmt_pb.RemoveOrgIDPResponse, error) {
|
func (s *Server) RemoveOrgIDP(ctx context.Context, req *mgmt_pb.RemoveOrgIDPRequest) (*mgmt_pb.RemoveOrgIDPResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method RemoveOrgIDP not implemented")
|
idpProviders, err := s.org.GetIDPProvidersByIDPConfigID(ctx, authz.GetCtxData(ctx).OrgID, req.IdpId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
externalIDPs, err := s.user.ExternalIDPsByIDPConfigID(ctx, req.IdpId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = s.command.RemoveDefaultIDPConfig(ctx, req.IdpId, idpProviderViewsToDomain(idpProviders), externalIDPViewsToDomain(externalIDPs)...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &mgmt_pb.RemoveOrgIDPResponse{}, nil
|
||||||
}
|
}
|
||||||
func (s *Server) UpdateOrgIDP(ctx context.Context, req *mgmt_pb.UpdateOrgIDPRequest) (*mgmt_pb.UpdateOrgIDPResponse, error) {
|
func (s *Server) UpdateOrgIDP(ctx context.Context, req *mgmt_pb.UpdateOrgIDPRequest) (*mgmt_pb.UpdateOrgIDPResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method UpdateOrgIDP not implemented")
|
config, err := s.command.ChangeDefaultIDPConfig(ctx, updateIDPToDomain(req))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &mgmt_pb.UpdateOrgIDPResponse{
|
||||||
|
Details: object_pb.ToDetailsPb(
|
||||||
|
config.Sequence,
|
||||||
|
config.ChangeDate,
|
||||||
|
config.ResourceOwner,
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
func (s *Server) UpdateOrgIDPOIDCConfig(ctx context.Context, req *mgmt_pb.UpdateOrgIDPOIDCConfigRequest) (*mgmt_pb.UpdateOrgIDPOIDCConfigResponse, error) {
|
func (s *Server) UpdateOrgIDPOIDCConfig(ctx context.Context, req *mgmt_pb.UpdateOrgIDPOIDCConfigRequest) (*mgmt_pb.UpdateOrgIDPOIDCConfigResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method UpdateOrgIDPOIDCConfig not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method UpdateOrgIDPOIDCConfig not implemented")
|
||||||
|
|||||||
121
internal/api/grpc/management/idp_converter.go
Normal file
121
internal/api/grpc/management/idp_converter.go
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
package management
|
||||||
|
|
||||||
|
import (
|
||||||
|
idp_grpc "github.com/caos/zitadel/internal/api/grpc/idp"
|
||||||
|
"github.com/caos/zitadel/internal/api/grpc/object"
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||||
|
user_model "github.com/caos/zitadel/internal/user/model"
|
||||||
|
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
|
||||||
|
)
|
||||||
|
|
||||||
|
func addOIDCIDPRequestToDomain(req *mgmt_pb.AddOrgOIDCIDPRequest) *domain.IDPConfig {
|
||||||
|
return &domain.IDPConfig{
|
||||||
|
Name: req.Name,
|
||||||
|
OIDCConfig: addOIDCIDPRequestToDomainOIDCIDPConfig(req),
|
||||||
|
StylingType: idp_grpc.IDPStylingTypeToDomain(req.StylingType),
|
||||||
|
Type: domain.IDPConfigTypeOIDC,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addOIDCIDPRequestToDomainOIDCIDPConfig(req *mgmt_pb.AddOrgOIDCIDPRequest) *domain.OIDCIDPConfig {
|
||||||
|
return &domain.OIDCIDPConfig{
|
||||||
|
ClientID: req.ClientId,
|
||||||
|
ClientSecretString: req.ClientSecret,
|
||||||
|
Issuer: req.Issuer,
|
||||||
|
Scopes: req.Scopes,
|
||||||
|
IDPDisplayNameMapping: idp_grpc.MappingFieldToDomain(req.DisplayNameMapping),
|
||||||
|
UsernameMapping: idp_grpc.MappingFieldToDomain(req.UsernameMapping),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateIDPToDomain(req *mgmt_pb.UpdateOrgIDPRequest) *domain.IDPConfig {
|
||||||
|
return &domain.IDPConfig{
|
||||||
|
IDPConfigID: req.IdpId,
|
||||||
|
Name: req.Name,
|
||||||
|
StylingType: idp_grpc.IDPStylingTypeToDomain(req.StylingType),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateOIDCConfigToDomain(req *mgmt_pb.UpdateOrgIDPOIDCConfigRequest) *domain.OIDCIDPConfig {
|
||||||
|
return &domain.OIDCIDPConfig{
|
||||||
|
IDPConfigID: req.IdpId,
|
||||||
|
ClientID: req.ClientId,
|
||||||
|
ClientSecretString: req.ClientSecret,
|
||||||
|
Issuer: req.Issuer,
|
||||||
|
Scopes: req.Scopes,
|
||||||
|
IDPDisplayNameMapping: idp_grpc.MappingFieldToDomain(req.DisplayNameMapping),
|
||||||
|
UsernameMapping: idp_grpc.MappingFieldToDomain(req.UsernameMapping),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func listIDPsToModel(req *mgmt_pb.ListOrgIDPsRequest) *iam_model.IDPConfigSearchRequest {
|
||||||
|
offset, limit, asc := object.ListQueryToModel(req.Query)
|
||||||
|
return &iam_model.IDPConfigSearchRequest{
|
||||||
|
Offset: offset,
|
||||||
|
Limit: limit,
|
||||||
|
Asc: asc,
|
||||||
|
SortingColumn: idp_grpc.FieldNameToModel(req.SortingColumn),
|
||||||
|
Queries: idpQueriesToModel(req.Queries),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func idpQueriesToModel(queries []*mgmt_pb.IDPQuery) []*iam_model.IDPConfigSearchQuery {
|
||||||
|
q := make([]*iam_model.IDPConfigSearchQuery, len(queries))
|
||||||
|
for i, query := range queries {
|
||||||
|
q[i] = idpQueryToModel(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
|
||||||
|
func idpQueryToModel(query *mgmt_pb.IDPQuery) *iam_model.IDPConfigSearchQuery {
|
||||||
|
switch q := query.Query.(type) {
|
||||||
|
case *mgmt_pb.IDPQuery_IdpNameQuery:
|
||||||
|
return idp_grpc.IDPNameQueryToModel(q.IdpNameQuery)
|
||||||
|
case *mgmt_pb.IDPQuery_IdpIdQuery:
|
||||||
|
return idp_grpc.IDPIDQueryToModel(q.IdpIdQuery)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func idpProviderViewsToDomain(idps []*iam_model.IDPProviderView) []*domain.IDPProvider {
|
||||||
|
idpProvider := make([]*domain.IDPProvider, len(idps))
|
||||||
|
for i, idp := range idps {
|
||||||
|
idpProvider[i] = &domain.IDPProvider{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: idp.AggregateID,
|
||||||
|
},
|
||||||
|
IDPConfigID: idp.IDPConfigID,
|
||||||
|
Type: idpConfigTypeToDomain(idp.IDPProviderType),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return idpProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func idpConfigTypeToDomain(idpType iam_model.IDPProviderType) domain.IdentityProviderType {
|
||||||
|
switch idpType {
|
||||||
|
case iam_model.IDPProviderTypeOrg:
|
||||||
|
return domain.IdentityProviderTypeOrg
|
||||||
|
default:
|
||||||
|
return domain.IdentityProviderTypeSystem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func externalIDPViewsToDomain(idps []*user_model.ExternalIDPView) []*domain.ExternalIDP {
|
||||||
|
externalIDPs := make([]*domain.ExternalIDP, len(idps))
|
||||||
|
for i, idp := range idps {
|
||||||
|
externalIDPs[i] = &domain.ExternalIDP{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: idp.UserID,
|
||||||
|
ResourceOwner: idp.ResourceOwner,
|
||||||
|
},
|
||||||
|
IDPConfigID: idp.IDPConfigID,
|
||||||
|
ExternalUserID: idp.ExternalUserID,
|
||||||
|
DisplayName: idp.UserDisplayName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return externalIDPs
|
||||||
|
}
|
||||||
149
internal/api/grpc/management/idp_converter_test.go
Normal file
149
internal/api/grpc/management/idp_converter_test.go
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
package management
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/test"
|
||||||
|
"github.com/caos/zitadel/pkg/grpc/idp"
|
||||||
|
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_addOIDCIDPRequestToDomain(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
req *mgmt_pb.AddOrgOIDCIDPRequest
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "all fields filled",
|
||||||
|
args: args{
|
||||||
|
req: &mgmt_pb.AddOrgOIDCIDPRequest{
|
||||||
|
Name: "ZITADEL",
|
||||||
|
StylingType: idp.IDPStylingType_STYLING_TYPE_GOOGLE,
|
||||||
|
ClientId: "test1234",
|
||||||
|
ClientSecret: "test4321",
|
||||||
|
Issuer: "zitadel.ch",
|
||||||
|
Scopes: []string{"email", "profile"},
|
||||||
|
DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL,
|
||||||
|
UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_PREFERRED_USERNAME,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := addOIDCIDPRequestToDomain(tt.args.req)
|
||||||
|
test.AssertFieldsMapped(t, got,
|
||||||
|
"ObjectRoot",
|
||||||
|
"OIDCConfig.ClientSecret",
|
||||||
|
"OIDCConfig.ObjectRoot",
|
||||||
|
"OIDCConfig.IDPConfigID",
|
||||||
|
"IDPConfigID",
|
||||||
|
"State",
|
||||||
|
"Type", //TODO: default (0) is oidc
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_addOIDCIDPRequestToDomainOIDCIDPConfig(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
req *mgmt_pb.AddOrgOIDCIDPRequest
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "all fields filled",
|
||||||
|
args: args{
|
||||||
|
req: &mgmt_pb.AddOrgOIDCIDPRequest{
|
||||||
|
ClientId: "test1234",
|
||||||
|
ClientSecret: "test4321",
|
||||||
|
Issuer: "zitadel.ch",
|
||||||
|
Scopes: []string{"email", "profile"},
|
||||||
|
DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL,
|
||||||
|
UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_PREFERRED_USERNAME,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := addOIDCIDPRequestToDomainOIDCIDPConfig(tt.args.req)
|
||||||
|
test.AssertFieldsMapped(t, got,
|
||||||
|
"ObjectRoot",
|
||||||
|
"ClientSecret", //TODO: is client secret string enough for backend?
|
||||||
|
"IDPConfigID",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_updateIDPToDomain(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
req *mgmt_pb.UpdateOrgIDPRequest
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "all fields filled",
|
||||||
|
args: args{
|
||||||
|
req: &mgmt_pb.UpdateOrgIDPRequest{
|
||||||
|
IdpId: "13523",
|
||||||
|
Name: "new name",
|
||||||
|
StylingType: idp.IDPStylingType_STYLING_TYPE_GOOGLE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := updateIDPToDomain(tt.args.req)
|
||||||
|
test.AssertFieldsMapped(t, got,
|
||||||
|
"ObjectRoot",
|
||||||
|
"OIDCConfig",
|
||||||
|
"State",
|
||||||
|
"Type", //TODO: type should not be changeable
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_updateOIDCConfigToDomain(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
req *mgmt_pb.UpdateOrgIDPOIDCConfigRequest
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "all fields filled",
|
||||||
|
args: args{
|
||||||
|
req: &mgmt_pb.UpdateOrgIDPOIDCConfigRequest{
|
||||||
|
IdpId: "4208",
|
||||||
|
Issuer: "zitadel.ch",
|
||||||
|
ClientId: "ZITEADEL",
|
||||||
|
ClientSecret: "i'm so secret",
|
||||||
|
Scopes: []string{"profile"},
|
||||||
|
DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL,
|
||||||
|
UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_PREFERRED_USERNAME,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := updateOIDCConfigToDomain(tt.args.req)
|
||||||
|
test.AssertFieldsMapped(t, got,
|
||||||
|
"ObjectRoot",
|
||||||
|
"ClientSecret",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -79,7 +79,7 @@ func (s *Server) ListLoginPolicyIDPs(ctx context.Context, req *mgmt_pb.ListLogin
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) AddIDPToLoginPolicy(ctx context.Context, req *mgmt_pb.AddIDPToLoginPolicyRequest) (*mgmt_pb.AddIDPToLoginPolicyResponse, error) {
|
func (s *Server) AddIDPToLoginPolicy(ctx context.Context, req *mgmt_pb.AddIDPToLoginPolicyRequest) (*mgmt_pb.AddIDPToLoginPolicyResponse, error) {
|
||||||
idp, err := s.command.AddIDPProviderToLoginPolicy(ctx, authz.GetCtxData(ctx).OrgID, &domain.IDPProvider{IDPConfigID: req.IdpId}) //TODO: old way was to also add type but this doesnt make sense in my point of view
|
idp, err := s.command.AddIDPProviderToLoginPolicy(ctx, authz.GetCtxData(ctx).OrgID, &domain.IDPProvider{IDPConfigID: req.IdpId, Type: idp.IDPProviderTypeFromPb(req.OwnerType)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ func (i *IDPProvider) getOrgIDPConfig(ctx context.Context, aggregateID, idpConfi
|
|||||||
if _, i := existing.GetIDP(idpConfigID); i != nil {
|
if _, i := existing.GetIDP(idpConfigID); i != nil {
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
return nil, errors.ThrowNotFound(nil, "EVENT-2m9fS", "Errors.Org.IdpNotExisting")
|
return nil, errors.ThrowNotFound(nil, "EVENT-2m9fS", "Errors.IDP.NotExisting")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IDPProvider) getOrgByID(ctx context.Context, orgID string) (*org_model.Org, error) {
|
func (i *IDPProvider) getOrgByID(ctx context.Context, orgID string) (*org_model.Org, error) {
|
||||||
@@ -228,5 +228,5 @@ func (u *IDPProvider) getDefaultIDPConfig(ctx context.Context, idpConfigID strin
|
|||||||
if _, existingIDP := existing.GetIDP(idpConfigID); existingIDP != nil {
|
if _, existingIDP := existing.GetIDP(idpConfigID); existingIDP != nil {
|
||||||
return existingIDP, nil
|
return existingIDP, nil
|
||||||
}
|
}
|
||||||
return nil, errors.ThrowNotFound(nil, "EVENT-49O0f", "Errors.IAM.IdpNotExisting")
|
return nil, errors.ThrowNotFound(nil, "EVENT-49O0f", "Errors.IDP.NotExisting")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ type Commands struct {
|
|||||||
iamDomain string
|
iamDomain string
|
||||||
zitadelRoles []authz.RoleMapping
|
zitadelRoles []authz.RoleMapping
|
||||||
|
|
||||||
idpConfigSecretCrypto crypto.Crypto
|
idpConfigSecretCrypto crypto.EncryptionAlgorithm
|
||||||
|
|
||||||
userPasswordAlg crypto.HashAlgorithm
|
userPasswordAlg crypto.HashAlgorithm
|
||||||
initializeUserCode crypto.Generator
|
initializeUserCode crypto.Generator
|
||||||
@@ -39,7 +39,7 @@ type Commands struct {
|
|||||||
machineKeySize int
|
machineKeySize int
|
||||||
applicationKeySize int
|
applicationKeySize int
|
||||||
applicationSecretGenerator crypto.Generator
|
applicationSecretGenerator crypto.Generator
|
||||||
domainVerificationAlg *crypto.AESCrypto
|
domainVerificationAlg crypto.EncryptionAlgorithm
|
||||||
domainVerificationGenerator crypto.Generator
|
domainVerificationGenerator crypto.Generator
|
||||||
domainVerificationValidator func(domain, token, verifier string, checkType http.CheckType) error
|
domainVerificationValidator func(domain, token, verifier string, checkType http.CheckType) error
|
||||||
multifactors domain.MultifactorConfigs
|
multifactors domain.MultifactorConfigs
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ func writeModelToMailText(wm *MailTextWriteModel) *domain.MailText {
|
|||||||
Greeting: wm.Greeting,
|
Greeting: wm.Greeting,
|
||||||
Text: wm.Text,
|
Text: wm.Text,
|
||||||
ButtonText: wm.ButtonText,
|
ButtonText: wm.ButtonText,
|
||||||
|
State: wm.State,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func (c *Commands) AddDefaultIDPConfig(ctx context.Context, config *domain.IDPCo
|
|||||||
}
|
}
|
||||||
addedConfig := NewIAMIDPConfigWriteModel(idpConfigID)
|
addedConfig := NewIAMIDPConfigWriteModel(idpConfigID)
|
||||||
|
|
||||||
clientSecret, err := crypto.Crypt([]byte(config.OIDCConfig.ClientSecretString), c.idpConfigSecretCrypto)
|
clientSecret, err := crypto.Encrypt([]byte(config.OIDCConfig.ClientSecretString), c.idpConfigSecretCrypto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -63,12 +63,15 @@ func (c *Commands) AddDefaultIDPConfig(ctx context.Context, config *domain.IDPCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) ChangeDefaultIDPConfig(ctx context.Context, config *domain.IDPConfig) (*domain.IDPConfig, error) {
|
func (c *Commands) ChangeDefaultIDPConfig(ctx context.Context, config *domain.IDPConfig) (*domain.IDPConfig, error) {
|
||||||
|
if config.IDPConfigID == "" {
|
||||||
|
return nil, errors.ThrowInvalidArgument(nil, "IAM-4m9gs", "Errors.IDMissing")
|
||||||
|
}
|
||||||
existingIDP, err := c.iamIDPConfigWriteModelByID(ctx, config.IDPConfigID)
|
existingIDP, err := c.iamIDPConfigWriteModelByID(ctx, config.IDPConfigID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if existingIDP.State == domain.IDPConfigStateRemoved || existingIDP.State == domain.IDPConfigStateUnspecified {
|
if existingIDP.State == domain.IDPConfigStateRemoved || existingIDP.State == domain.IDPConfigStateUnspecified {
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-4M9so", "Errors.IAM.IDPConfig.NotExisting")
|
return nil, caos_errs.ThrowNotFound(nil, "IAM-4M9so", "Errors.IDPConfig.NotExisting")
|
||||||
}
|
}
|
||||||
|
|
||||||
iamAgg := IAMAggregateFromWriteModel(&existingIDP.WriteModel)
|
iamAgg := IAMAggregateFromWriteModel(&existingIDP.WriteModel)
|
||||||
@@ -133,7 +136,7 @@ func (c *Commands) RemoveDefaultIDPConfig(ctx context.Context, idpID string, idp
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if existingIDP.State == domain.IDPConfigStateRemoved || existingIDP.State == domain.IDPConfigStateUnspecified {
|
if existingIDP.State == domain.IDPConfigStateRemoved || existingIDP.State == domain.IDPConfigStateUnspecified {
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-4M0xy", "Errors.IAM.IDPConfig.NotExisting")
|
return nil, caos_errs.ThrowNotFound(nil, "IAM-4M0xy", "Errors.IDPConfig.NotExisting")
|
||||||
}
|
}
|
||||||
|
|
||||||
iamAgg := IAMAggregateFromWriteModel(&existingIDP.WriteModel)
|
iamAgg := IAMAggregateFromWriteModel(&existingIDP.WriteModel)
|
||||||
@@ -168,7 +171,7 @@ func (c *Commands) getIAMIDPConfigByID(ctx context.Context, idpID string) (*doma
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !config.State.Exists() {
|
if !config.State.Exists() {
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-4M9so", "Errors.IAM.IDPConfig.NotExisting")
|
return nil, caos_errs.ThrowNotFound(nil, "IAM-4M9so", "Errors.IDPConfig.NotExisting")
|
||||||
}
|
}
|
||||||
return writeModelToIDPConfig(&config.IDPConfigWriteModel), nil
|
return writeModelToIDPConfig(&config.IDPConfigWriteModel), nil
|
||||||
}
|
}
|
||||||
|
|||||||
294
internal/command/iam_idp_config_test.go
Normal file
294
internal/command/iam_idp_config_test.go
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/caos/zitadel/internal/id"
|
||||||
|
id_mock "github.com/caos/zitadel/internal/id/mock"
|
||||||
|
"github.com/caos/zitadel/internal/repository/iam"
|
||||||
|
"github.com/caos/zitadel/internal/repository/idpconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_AddDefaultIDPConfig(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
idGenerator id.Generator
|
||||||
|
secretCrypto crypto.EncryptionAlgorithm
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
config *domain.IDPConfig
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.IDPConfig
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "invalid config, error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.IDPConfig{},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "idp config oidc add, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIDPConfigAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name1",
|
||||||
|
domain.IDPConfigTypeOIDC,
|
||||||
|
domain.IDPConfigStylingTypeGoogle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIDPOIDCConfigAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"clientid1",
|
||||||
|
"config1",
|
||||||
|
"issuer",
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("secret"),
|
||||||
|
},
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
"scope",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
uniqueConstraintsFromEventConstraint(idpconfig.NewAddIDPConfigNameUniqueConstraint("name1", "IAM")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "config1"),
|
||||||
|
secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.IDPConfig{
|
||||||
|
Name: "name1",
|
||||||
|
StylingType: domain.IDPConfigStylingTypeGoogle,
|
||||||
|
OIDCConfig: &domain.OIDCIDPConfig{
|
||||||
|
ClientID: "clientid1",
|
||||||
|
Issuer: "issuer",
|
||||||
|
ClientSecretString: "secret",
|
||||||
|
Scopes: []string{"scope"},
|
||||||
|
IDPDisplayNameMapping: domain.OIDCMappingFieldEmail,
|
||||||
|
UsernameMapping: domain.OIDCMappingFieldEmail,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.IDPConfig{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "IAM",
|
||||||
|
ResourceOwner: "IAM",
|
||||||
|
},
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
Name: "name1",
|
||||||
|
StylingType: domain.IDPConfigStylingTypeGoogle,
|
||||||
|
State: domain.IDPConfigStateActive,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
idGenerator: tt.fields.idGenerator,
|
||||||
|
idpConfigSecretCrypto: tt.fields.secretCrypto,
|
||||||
|
}
|
||||||
|
got, err := r.AddDefaultIDPConfig(tt.args.ctx, tt.args.config)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_ChangeDefaultIDPConfig(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
config *domain.IDPConfig
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.IDPConfig
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "invalid config, error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.IDPConfig{},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "config not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.IDPConfig{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "idp config change, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIDPConfigAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name1",
|
||||||
|
domain.IDPConfigTypeOIDC,
|
||||||
|
domain.IDPConfigStylingTypeGoogle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIDPOIDCConfigAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"clientid1",
|
||||||
|
"config1",
|
||||||
|
"issuer",
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
"scope",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
newDefaultIDPConfigChangedEvent(context.Background(), "config1", "name1", "name2", domain.IDPConfigStylingTypeUnspecified),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
uniqueConstraintsFromEventConstraint(idpconfig.NewRemoveIDPConfigNameUniqueConstraint("name1", "IAM")),
|
||||||
|
uniqueConstraintsFromEventConstraint(idpconfig.NewAddIDPConfigNameUniqueConstraint("name2", "IAM")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.IDPConfig{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
Name: "name2",
|
||||||
|
StylingType: domain.IDPConfigStylingTypeUnspecified,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.IDPConfig{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "IAM",
|
||||||
|
ResourceOwner: "IAM",
|
||||||
|
},
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
Name: "name2",
|
||||||
|
StylingType: domain.IDPConfigStylingTypeUnspecified,
|
||||||
|
State: domain.IDPConfigStateActive,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.ChangeDefaultIDPConfig(tt.args.ctx, tt.args.config)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDefaultIDPConfigChangedEvent(ctx context.Context, configID, oldName, newName string, stylingType domain.IDPConfigStylingType) *iam.IDPConfigChangedEvent {
|
||||||
|
event, _ := iam.NewIDPConfigChangedEvent(ctx,
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
configID,
|
||||||
|
oldName,
|
||||||
|
[]idpconfig.IDPConfigChanges{
|
||||||
|
idpconfig.ChangeName(newName),
|
||||||
|
idpconfig.ChangeStyleType(stylingType),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return event
|
||||||
|
}
|
||||||
@@ -7,6 +7,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *Commands) ChangeDefaultIDPOIDCConfig(ctx context.Context, config *domain.OIDCIDPConfig) (*domain.OIDCIDPConfig, error) {
|
func (c *Commands) ChangeDefaultIDPOIDCConfig(ctx context.Context, config *domain.OIDCIDPConfig) (*domain.OIDCIDPConfig, error) {
|
||||||
|
if config.IDPConfigID == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-9djf8", "Errors.IDMissing")
|
||||||
|
}
|
||||||
existingConfig := NewIAMIDPOIDCConfigWriteModel(config.IDPConfigID)
|
existingConfig := NewIAMIDPOIDCConfigWriteModel(config.IDPConfigID)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, existingConfig)
|
err := c.eventstore.FilterToQueryReducer(ctx, existingConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -14,7 +17,7 @@ func (c *Commands) ChangeDefaultIDPOIDCConfig(ctx context.Context, config *domai
|
|||||||
}
|
}
|
||||||
|
|
||||||
if existingConfig.State == domain.IDPConfigStateRemoved || existingConfig.State == domain.IDPConfigStateUnspecified {
|
if existingConfig.State == domain.IDPConfigStateRemoved || existingConfig.State == domain.IDPConfigStateUnspecified {
|
||||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-67J9d", "Errors.IAM.IDPConfig.AlreadyExists")
|
return nil, caos_errs.ThrowNotFound(nil, "IAM-67J9d", "Errors.IAM.IDPConfig.AlreadyExists")
|
||||||
}
|
}
|
||||||
|
|
||||||
iamAgg := IAMAggregateFromWriteModel(&existingConfig.WriteModel)
|
iamAgg := IAMAggregateFromWriteModel(&existingConfig.WriteModel)
|
||||||
|
|||||||
295
internal/command/iam_idp_oidc_config_test.go
Normal file
295
internal/command/iam_idp_oidc_config_test.go
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/caos/zitadel/internal/repository/iam"
|
||||||
|
"github.com/caos/zitadel/internal/repository/idpconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_ChangeDefaultIDPOIDCConfig(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
secretCrypto crypto.EncryptionAlgorithm
|
||||||
|
}
|
||||||
|
type (
|
||||||
|
args struct {
|
||||||
|
ctx context.Context
|
||||||
|
config *domain.OIDCIDPConfig
|
||||||
|
}
|
||||||
|
)
|
||||||
|
type res struct {
|
||||||
|
want *domain.OIDCIDPConfig
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "invalid config, error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.OIDCIDPConfig{},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "idp config not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.OIDCIDPConfig{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "idp config removed, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIDPConfigAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name1",
|
||||||
|
domain.IDPConfigTypeOIDC,
|
||||||
|
domain.IDPConfigStylingTypeGoogle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIDPOIDCConfigAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"clientid1",
|
||||||
|
"config1",
|
||||||
|
"issuer",
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
"scope",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIDPConfigRemovedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.OIDCIDPConfig{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no changes, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIDPConfigAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name1",
|
||||||
|
domain.IDPConfigTypeOIDC,
|
||||||
|
domain.IDPConfigStylingTypeGoogle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIDPOIDCConfigAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"clientid1",
|
||||||
|
"config1",
|
||||||
|
"issuer",
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("secret"),
|
||||||
|
},
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
"scope",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.OIDCIDPConfig{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
ClientID: "clientid1",
|
||||||
|
Issuer: "issuer",
|
||||||
|
Scopes: []string{"scope"},
|
||||||
|
IDPDisplayNameMapping: domain.OIDCMappingFieldEmail,
|
||||||
|
UsernameMapping: domain.OIDCMappingFieldEmail,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "idp config oidc add, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIDPConfigAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name1",
|
||||||
|
domain.IDPConfigTypeOIDC,
|
||||||
|
domain.IDPConfigStylingTypeGoogle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIDPOIDCConfigAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"clientid1",
|
||||||
|
"config1",
|
||||||
|
"issuer",
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("secret"),
|
||||||
|
},
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
"scope",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
newDefaultIDPOIDCConfigChangedEvent(context.Background(),
|
||||||
|
"config1",
|
||||||
|
"clientid-changed",
|
||||||
|
"issuer-changed",
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("secret-changed"),
|
||||||
|
},
|
||||||
|
domain.OIDCMappingFieldPreferredLoginName,
|
||||||
|
domain.OIDCMappingFieldPreferredLoginName,
|
||||||
|
[]string{"scope", "scope2"},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.OIDCIDPConfig{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
ClientID: "clientid-changed",
|
||||||
|
Issuer: "issuer-changed",
|
||||||
|
ClientSecretString: "secret-changed",
|
||||||
|
Scopes: []string{"scope", "scope2"},
|
||||||
|
IDPDisplayNameMapping: domain.OIDCMappingFieldPreferredLoginName,
|
||||||
|
UsernameMapping: domain.OIDCMappingFieldPreferredLoginName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.OIDCIDPConfig{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "IAM",
|
||||||
|
ResourceOwner: "IAM",
|
||||||
|
},
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
ClientID: "clientid-changed",
|
||||||
|
Issuer: "issuer-changed",
|
||||||
|
Scopes: []string{"scope", "scope2"},
|
||||||
|
IDPDisplayNameMapping: domain.OIDCMappingFieldPreferredLoginName,
|
||||||
|
UsernameMapping: domain.OIDCMappingFieldPreferredLoginName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
idpConfigSecretCrypto: tt.fields.secretCrypto,
|
||||||
|
}
|
||||||
|
got, err := r.ChangeDefaultIDPOIDCConfig(tt.args.ctx, tt.args.config)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDefaultIDPOIDCConfigChangedEvent(ctx context.Context, configID, clientID, issuer string, secret *crypto.CryptoValue, displayMapping, usernameMapping domain.OIDCMappingField, scopes []string) *iam.IDPOIDCConfigChangedEvent {
|
||||||
|
event, _ := iam.NewIDPOIDCConfigChangedEvent(ctx,
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
configID,
|
||||||
|
[]idpconfig.OIDCConfigChanges{
|
||||||
|
idpconfig.ChangeClientID(clientID),
|
||||||
|
idpconfig.ChangeIssuer(issuer),
|
||||||
|
idpconfig.ChangeClientSecret(secret),
|
||||||
|
idpconfig.ChangeIDPDisplayNameMapping(displayMapping),
|
||||||
|
idpconfig.ChangeUserNameMapping(usernameMapping),
|
||||||
|
idpconfig.ChangeScopes(scopes),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return event
|
||||||
|
}
|
||||||
@@ -85,8 +85,15 @@ func (c *Commands) changeDefaultLoginPolicy(ctx context.Context, iamAgg *eventst
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) AddIDPProviderToDefaultLoginPolicy(ctx context.Context, idpProvider *domain.IDPProvider) (*domain.IDPProvider, error) {
|
func (c *Commands) AddIDPProviderToDefaultLoginPolicy(ctx context.Context, idpProvider *domain.IDPProvider) (*domain.IDPProvider, error) {
|
||||||
|
if !idpProvider.IsValid() {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-9nf88", "Errors.IAM.LoginPolicy.IDP.Invalid")
|
||||||
|
}
|
||||||
|
_, err := c.getIAMIDPConfigByID(ctx, idpProvider.IDPConfigID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, caos_errs.ThrowPreconditionFailed(err, "IAM-m8fsd", "Errors.IDPConfig.NotExisting")
|
||||||
|
}
|
||||||
idpModel := NewIAMIdentityProviderWriteModel(idpProvider.IDPConfigID)
|
idpModel := NewIAMIdentityProviderWriteModel(idpProvider.IDPConfigID)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, idpModel)
|
err = c.eventstore.FilterToQueryReducer(ctx, idpModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -95,7 +102,7 @@ func (c *Commands) AddIDPProviderToDefaultLoginPolicy(ctx context.Context, idpPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
iamAgg := IAMAggregateFromWriteModel(&idpModel.WriteModel)
|
iamAgg := IAMAggregateFromWriteModel(&idpModel.WriteModel)
|
||||||
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewIdentityProviderAddedEvent(ctx, iamAgg, idpProvider.IDPConfigID, idpProvider.Type))
|
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewIdentityProviderAddedEvent(ctx, iamAgg, idpProvider.IDPConfigID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -107,6 +114,9 @@ func (c *Commands) AddIDPProviderToDefaultLoginPolicy(ctx context.Context, idpPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) RemoveIDPProviderFromDefaultLoginPolicy(ctx context.Context, idpProvider *domain.IDPProvider, cascadeExternalIDPs ...*domain.ExternalIDP) (*domain.ObjectDetails, error) {
|
func (c *Commands) RemoveIDPProviderFromDefaultLoginPolicy(ctx context.Context, idpProvider *domain.IDPProvider, cascadeExternalIDPs ...*domain.ExternalIDP) (*domain.ObjectDetails, error) {
|
||||||
|
if !idpProvider.IsValid() {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-66m9s", "Errors.IAM.LoginPolicy.IDP.Invalid")
|
||||||
|
}
|
||||||
idpModel := NewIAMIdentityProviderWriteModel(idpProvider.IDPConfigID)
|
idpModel := NewIAMIdentityProviderWriteModel(idpProvider.IDPConfigID)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, idpModel)
|
err := c.eventstore.FilterToQueryReducer(ctx, idpModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -117,12 +127,7 @@ func (c *Commands) RemoveIDPProviderFromDefaultLoginPolicy(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
iamAgg := IAMAggregateFromWriteModel(&idpModel.IdentityProviderWriteModel.WriteModel)
|
iamAgg := IAMAggregateFromWriteModel(&idpModel.IdentityProviderWriteModel.WriteModel)
|
||||||
events := []eventstore.EventPusher{
|
events := c.removeIDPProviderFromDefaultLoginPolicy(ctx, iamAgg, idpProvider, false, cascadeExternalIDPs...)
|
||||||
iam_repo.NewIdentityProviderRemovedEvent(ctx, iamAgg, idpProvider.IDPConfigID),
|
|
||||||
}
|
|
||||||
|
|
||||||
userEvents := c.removeIDPProviderFromDefaultLoginPolicy(ctx, iamAgg, idpProvider, false, cascadeExternalIDPs...)
|
|
||||||
events = append(events, userEvents...)
|
|
||||||
pushedEvents, err := c.eventstore.PushEvents(ctx, events...)
|
pushedEvents, err := c.eventstore.PushEvents(ctx, events...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -154,7 +159,10 @@ func (c *Commands) removeIDPProviderFromDefaultLoginPolicy(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) AddSecondFactorToDefaultLoginPolicy(ctx context.Context, secondFactor domain.SecondFactorType) (domain.SecondFactorType, *domain.ObjectDetails, error) {
|
func (c *Commands) AddSecondFactorToDefaultLoginPolicy(ctx context.Context, secondFactor domain.SecondFactorType) (domain.SecondFactorType, *domain.ObjectDetails, error) {
|
||||||
secondFactorModel := NewIAMSecondFactorWriteModel()
|
if !secondFactor.Valid() {
|
||||||
|
return domain.SecondFactorTypeUnspecified, nil, caos_errs.ThrowInvalidArgument(nil, "IAM-5m9fs", "Errors.IAM.LoginPolicy.MFA.Unspecified")
|
||||||
|
}
|
||||||
|
secondFactorModel := NewIAMSecondFactorWriteModel(secondFactor)
|
||||||
iamAgg := IAMAggregateFromWriteModel(&secondFactorModel.SecondFactorWriteModel.WriteModel)
|
iamAgg := IAMAggregateFromWriteModel(&secondFactorModel.SecondFactorWriteModel.WriteModel)
|
||||||
event, err := c.addSecondFactorToDefaultLoginPolicy(ctx, iamAgg, secondFactorModel, secondFactor)
|
event, err := c.addSecondFactorToDefaultLoginPolicy(ctx, iamAgg, secondFactorModel, secondFactor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -185,7 +193,10 @@ func (c *Commands) addSecondFactorToDefaultLoginPolicy(ctx context.Context, iamA
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) RemoveSecondFactorFromDefaultLoginPolicy(ctx context.Context, secondFactor domain.SecondFactorType) (*domain.ObjectDetails, error) {
|
func (c *Commands) RemoveSecondFactorFromDefaultLoginPolicy(ctx context.Context, secondFactor domain.SecondFactorType) (*domain.ObjectDetails, error) {
|
||||||
secondFactorModel := NewIAMSecondFactorWriteModel()
|
if !secondFactor.Valid() {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-55n8s", "Errors.IAM.LoginPolicy.MFA.Unspecified")
|
||||||
|
}
|
||||||
|
secondFactorModel := NewIAMSecondFactorWriteModel(secondFactor)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, secondFactorModel)
|
err := c.eventstore.FilterToQueryReducer(ctx, secondFactorModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -206,7 +217,10 @@ func (c *Commands) RemoveSecondFactorFromDefaultLoginPolicy(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) AddMultiFactorToDefaultLoginPolicy(ctx context.Context, multiFactor domain.MultiFactorType) (domain.MultiFactorType, *domain.ObjectDetails, error) {
|
func (c *Commands) AddMultiFactorToDefaultLoginPolicy(ctx context.Context, multiFactor domain.MultiFactorType) (domain.MultiFactorType, *domain.ObjectDetails, error) {
|
||||||
multiFactorModel := NewIAMMultiFactorWriteModel()
|
if !multiFactor.Valid() {
|
||||||
|
return domain.MultiFactorTypeUnspecified, nil, caos_errs.ThrowInvalidArgument(nil, "IAM-5m9fs", "Errors.IAM.LoginPolicy.MFA.Unspecified")
|
||||||
|
}
|
||||||
|
multiFactorModel := NewIAMMultiFactorWriteModel(multiFactor)
|
||||||
iamAgg := IAMAggregateFromWriteModel(&multiFactorModel.MultiFactoryWriteModel.WriteModel)
|
iamAgg := IAMAggregateFromWriteModel(&multiFactorModel.MultiFactoryWriteModel.WriteModel)
|
||||||
event, err := c.addMultiFactorToDefaultLoginPolicy(ctx, iamAgg, multiFactorModel, multiFactor)
|
event, err := c.addMultiFactorToDefaultLoginPolicy(ctx, iamAgg, multiFactorModel, multiFactor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -237,7 +251,10 @@ func (c *Commands) addMultiFactorToDefaultLoginPolicy(ctx context.Context, iamAg
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) RemoveMultiFactorFromDefaultLoginPolicy(ctx context.Context, multiFactor domain.MultiFactorType) (*domain.ObjectDetails, error) {
|
func (c *Commands) RemoveMultiFactorFromDefaultLoginPolicy(ctx context.Context, multiFactor domain.MultiFactorType) (*domain.ObjectDetails, error) {
|
||||||
multiFactorModel := NewIAMMultiFactorWriteModel()
|
if !multiFactor.Valid() {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-33m9F", "Errors.IAM.LoginPolicy.MFA.Unspecified")
|
||||||
|
}
|
||||||
|
multiFactorModel := NewIAMMultiFactorWriteModel(multiFactor)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, multiFactorModel)
|
err := c.eventstore.FilterToQueryReducer(ctx, multiFactorModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -10,13 +10,14 @@ type IAMSecondFactorWriteModel struct {
|
|||||||
SecondFactorWriteModel
|
SecondFactorWriteModel
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIAMSecondFactorWriteModel() *IAMSecondFactorWriteModel {
|
func NewIAMSecondFactorWriteModel(factorType domain.SecondFactorType) *IAMSecondFactorWriteModel {
|
||||||
return &IAMSecondFactorWriteModel{
|
return &IAMSecondFactorWriteModel{
|
||||||
SecondFactorWriteModel{
|
SecondFactorWriteModel{
|
||||||
WriteModel: eventstore.WriteModel{
|
WriteModel: eventstore.WriteModel{
|
||||||
AggregateID: domain.IAMID,
|
AggregateID: domain.IAMID,
|
||||||
ResourceOwner: domain.IAMID,
|
ResourceOwner: domain.IAMID,
|
||||||
},
|
},
|
||||||
|
MFAType: factorType,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,15 +26,19 @@ func (wm *IAMSecondFactorWriteModel) AppendEvents(events ...eventstore.EventRead
|
|||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
switch e := event.(type) {
|
switch e := event.(type) {
|
||||||
case *iam.LoginPolicySecondFactorAddedEvent:
|
case *iam.LoginPolicySecondFactorAddedEvent:
|
||||||
wm.WriteModel.AppendEvents(&e.SecondFactorAddedEvent)
|
if wm.MFAType == e.MFAType {
|
||||||
|
wm.WriteModel.AppendEvents(&e.SecondFactorAddedEvent)
|
||||||
|
}
|
||||||
case *iam.LoginPolicySecondFactorRemovedEvent:
|
case *iam.LoginPolicySecondFactorRemovedEvent:
|
||||||
wm.WriteModel.AppendEvents(&e.SecondFactorRemovedEvent)
|
if wm.MFAType == e.MFAType {
|
||||||
|
wm.WriteModel.AppendEvents(&e.SecondFactorRemovedEvent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wm *IAMSecondFactorWriteModel) Reduce() error {
|
func (wm *IAMSecondFactorWriteModel) Reduce() error {
|
||||||
return wm.WriteModel.Reduce()
|
return wm.SecondFactorWriteModel.Reduce()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wm *IAMSecondFactorWriteModel) Query() *eventstore.SearchQueryBuilder {
|
func (wm *IAMSecondFactorWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||||
@@ -49,13 +54,14 @@ type IAMMultiFactorWriteModel struct {
|
|||||||
MultiFactoryWriteModel
|
MultiFactoryWriteModel
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIAMMultiFactorWriteModel() *IAMMultiFactorWriteModel {
|
func NewIAMMultiFactorWriteModel(factorType domain.MultiFactorType) *IAMMultiFactorWriteModel {
|
||||||
return &IAMMultiFactorWriteModel{
|
return &IAMMultiFactorWriteModel{
|
||||||
MultiFactoryWriteModel{
|
MultiFactoryWriteModel{
|
||||||
WriteModel: eventstore.WriteModel{
|
WriteModel: eventstore.WriteModel{
|
||||||
AggregateID: domain.IAMID,
|
AggregateID: domain.IAMID,
|
||||||
ResourceOwner: domain.IAMID,
|
ResourceOwner: domain.IAMID,
|
||||||
},
|
},
|
||||||
|
MFAType: factorType,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,15 +70,19 @@ func (wm *IAMMultiFactorWriteModel) AppendEvents(events ...eventstore.EventReade
|
|||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
switch e := event.(type) {
|
switch e := event.(type) {
|
||||||
case *iam.LoginPolicyMultiFactorAddedEvent:
|
case *iam.LoginPolicyMultiFactorAddedEvent:
|
||||||
wm.WriteModel.AppendEvents(&e.MultiFactorAddedEvent)
|
if wm.MFAType == e.MFAType {
|
||||||
|
wm.WriteModel.AppendEvents(&e.MultiFactorAddedEvent)
|
||||||
|
}
|
||||||
case *iam.LoginPolicyMultiFactorRemovedEvent:
|
case *iam.LoginPolicyMultiFactorRemovedEvent:
|
||||||
wm.WriteModel.AppendEvents(&e.MultiFactorRemovedEvent)
|
if wm.MFAType == e.MFAType {
|
||||||
|
wm.WriteModel.AppendEvents(&e.MultiFactorRemovedEvent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wm *IAMMultiFactorWriteModel) Reduce() error {
|
func (wm *IAMMultiFactorWriteModel) Reduce() error {
|
||||||
return wm.WriteModel.Reduce()
|
return wm.MultiFactoryWriteModel.Reduce()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wm *IAMMultiFactorWriteModel) Query() *eventstore.SearchQueryBuilder {
|
func (wm *IAMMultiFactorWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import (
|
|||||||
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
"github.com/caos/zitadel/internal/repository/iam"
|
"github.com/caos/zitadel/internal/repository/iam"
|
||||||
"github.com/caos/zitadel/internal/repository/policy"
|
"github.com/caos/zitadel/internal/repository/policy"
|
||||||
|
"github.com/caos/zitadel/internal/repository/user"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@@ -268,6 +270,917 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
provider *domain.IDPProvider
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.IDPProvider
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "provider invalid, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
provider: &domain.IDPProvider{},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "config not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
provider: &domain.IDPProvider{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "provider already exists, already exists error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIDPConfigAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name",
|
||||||
|
domain.IDPConfigTypeOIDC,
|
||||||
|
domain.IDPConfigStylingTypeUnspecified,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewLoginPolicyAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
domain.PasswordlessTypeAllowed,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIdentityProviderAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
provider: &domain.IDPProvider{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorAlreadyExists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add provider, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIDPConfigAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name",
|
||||||
|
domain.IDPConfigTypeOIDC,
|
||||||
|
domain.IDPConfigStylingTypeUnspecified,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectFilter(),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIdentityProviderAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
provider: &domain.IDPProvider{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.IDPProvider{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "IAM",
|
||||||
|
ResourceOwner: "IAM",
|
||||||
|
},
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.AddIDPProviderToDefaultLoginPolicy(tt.args.ctx, tt.args.provider)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
provider *domain.IDPProvider
|
||||||
|
cascadeExternalIDPs []*domain.ExternalIDP
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "provider invalid, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
provider: &domain.IDPProvider{},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "provider not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
provider: &domain.IDPProvider{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "provider removed, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewLoginPolicyAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
domain.PasswordlessTypeAllowed,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIdentityProviderAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIdentityProviderRemovedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
provider: &domain.IDPProvider{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove provider, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewLoginPolicyAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
domain.PasswordlessTypeAllowed,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIdentityProviderAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIdentityProviderRemovedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
provider: &domain.IDPProvider{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "IAM",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove provider external idp not found, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewLoginPolicyAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
domain.PasswordlessTypeAllowed,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIdentityProviderAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIdentityProviderRemovedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
provider: &domain.IDPProvider{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
cascadeExternalIDPs: []*domain.ExternalIDP{
|
||||||
|
{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "IAM",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove provider with external idps, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewLoginPolicyAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
domain.PasswordlessTypeAllowed,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIdentityProviderAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanExternalIDPAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"config1", "", "externaluser1"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIdentityProviderRemovedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
"config1"),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanExternalIDPCascadeRemovedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"config1", "externaluser1")),
|
||||||
|
},
|
||||||
|
uniqueConstraintsFromEventConstraint(user.NewRemoveExternalIDPUniqueConstraint("config1", "externaluser1")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
provider: &domain.IDPProvider{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
cascadeExternalIDPs: []*domain.ExternalIDP{
|
||||||
|
{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
ExternalUserID: "externaluser1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "IAM",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.RemoveIDPProviderFromDefaultLoginPolicy(tt.args.ctx, tt.args.provider, tt.args.cascadeExternalIDPs...)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_AddSecondFactorDefaultLoginPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
factor domain.SecondFactorType
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "factor invalid, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
factor: domain.SecondFactorTypeUnspecified,
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "factor already exists, already exists error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewLoginPolicySecondFactorAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
domain.SecondFactorTypeOTP,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
factor: domain.SecondFactorTypeOTP,
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorAlreadyExists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add factor, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewLoginPolicySecondFactorAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
domain.SecondFactorTypeOTP),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
factor: domain.SecondFactorTypeOTP,
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "IAM",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
_, got, err := r.AddSecondFactorToDefaultLoginPolicy(tt.args.ctx, tt.args.factor)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_RemoveSecondFactorDefaultLoginPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
factor domain.SecondFactorType
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "factor invalid, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
factor: domain.SecondFactorTypeUnspecified,
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "factor not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
factor: domain.SecondFactorTypeOTP,
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "factor removed, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewLoginPolicySecondFactorAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
domain.SecondFactorTypeOTP,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewLoginPolicySecondFactorRemovedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
domain.SecondFactorTypeOTP,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
factor: domain.SecondFactorTypeOTP,
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add factor, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewLoginPolicySecondFactorAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
domain.SecondFactorTypeOTP,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewLoginPolicySecondFactorRemovedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
domain.SecondFactorTypeOTP),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
factor: domain.SecondFactorTypeOTP,
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "IAM",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.RemoveSecondFactorFromDefaultLoginPolicy(tt.args.ctx, tt.args.factor)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_AddMultiFactorDefaultLoginPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
factor domain.MultiFactorType
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "factor invalid, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
factor: domain.MultiFactorTypeUnspecified,
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "factor already exists, already exists error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewLoginPolicyMultiFactorAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
domain.MultiFactorTypeU2FWithPIN,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
factor: domain.MultiFactorTypeU2FWithPIN,
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorAlreadyExists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add factor, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewLoginPolicyMultiFactorAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
domain.MultiFactorTypeU2FWithPIN),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
factor: domain.MultiFactorTypeU2FWithPIN,
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "IAM",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
_, got, err := r.AddMultiFactorToDefaultLoginPolicy(tt.args.ctx, tt.args.factor)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_RemoveMultiFactorDefaultLoginPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
factor domain.MultiFactorType
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "factor invalid, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
factor: domain.MultiFactorTypeUnspecified,
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "factor not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
factor: domain.MultiFactorTypeU2FWithPIN,
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "factor removed, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewLoginPolicyMultiFactorAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
domain.MultiFactorTypeU2FWithPIN,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewLoginPolicyMultiFactorRemovedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
domain.MultiFactorTypeU2FWithPIN,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
factor: domain.MultiFactorTypeU2FWithPIN,
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add factor, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewLoginPolicyMultiFactorAddedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
domain.MultiFactorTypeU2FWithPIN,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewLoginPolicyMultiFactorRemovedEvent(context.Background(),
|
||||||
|
&iam.NewAggregate().Aggregate,
|
||||||
|
domain.MultiFactorTypeU2FWithPIN),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
factor: domain.MultiFactorTypeU2FWithPIN,
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "IAM",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.RemoveMultiFactorFromDefaultLoginPolicy(tt.args.ctx, tt.args.factor)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func newDefaultLoginPolicyChangedEvent(ctx context.Context, allowRegister, allowUsernamePassword, allowExternalIDP, forceMFA bool, passwordlessType domain.PasswordlessType) *iam.LoginPolicyChangedEvent {
|
func newDefaultLoginPolicyChangedEvent(ctx context.Context, allowRegister, allowUsernamePassword, allowExternalIDP, forceMFA bool, passwordlessType domain.PasswordlessType) *iam.LoginPolicyChangedEvent {
|
||||||
event, _ := iam.NewLoginPolicyChangedEvent(ctx,
|
event, _ := iam.NewLoginPolicyChangedEvent(ctx,
|
||||||
&iam.NewAggregate().Aggregate,
|
&iam.NewAggregate().Aggregate,
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func (c *Commands) ChangeDefaultOrgIAMPolicy(ctx context.Context, policy *domain
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
if !existingPolicy.State.Exists() {
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-0Pl0d", "Errors.IAM.OrgIAMPolicy.NotFound")
|
return nil, caos_errs.ThrowNotFound(nil, "IAM-0Pl0d", "Errors.IAM.OrgIAMPolicy.NotFound")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +70,9 @@ func (c *Commands) getDefaultOrgIAMPolicy(ctx context.Context) (*domain.OrgIAMPo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if !policyWriteModel.State.Exists() {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-3n8fs", "Errors.IAM.PasswordComplexityPolicy.NotFound")
|
||||||
|
}
|
||||||
policy := writeModelToOrgIAMPolicy(policyWriteModel)
|
policy := writeModelToOrgIAMPolicy(policyWriteModel)
|
||||||
policy.Default = true
|
policy.Default = true
|
||||||
return policy, nil
|
return policy, nil
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ func (c *Commands) getDefaultPasswordComplexityPolicy(ctx context.Context) (*dom
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if !policyWriteModel.State.Exists() {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-M0gsf", "Errors.IAM.OrgIAMPolicy.NotFound")
|
||||||
|
}
|
||||||
policy := writeModelToPasswordComplexityPolicy(&policyWriteModel.PasswordComplexityPolicyWriteModel)
|
policy := writeModelToPasswordComplexityPolicy(&policyWriteModel.PasswordComplexityPolicyWriteModel)
|
||||||
policy.Default = true
|
policy.Default = true
|
||||||
return policy, nil
|
return policy, nil
|
||||||
|
|||||||
@@ -147,6 +147,12 @@ func eventFromEventPusher(event eventstore.EventPusher) *repository.Event {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func eventFromEventPusherWithCreationDateNow(event eventstore.EventPusher) *repository.Event {
|
||||||
|
e := eventFromEventPusher(event)
|
||||||
|
e.CreationDate = time.Now()
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
func uniqueConstraintsFromEventConstraint(constraint *eventstore.EventUniqueConstraint) *repository.UniqueConstraint {
|
func uniqueConstraintsFromEventConstraint(constraint *eventstore.EventUniqueConstraint) *repository.UniqueConstraint {
|
||||||
return &repository.UniqueConstraint{
|
return &repository.UniqueConstraint{
|
||||||
UniqueType: constraint.UniqueType,
|
UniqueType: constraint.UniqueType,
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *Commands) AddOrgDomain(ctx context.Context, orgDomain *domain.OrgDomain) (*domain.OrgDomain, error) {
|
func (c *Commands) AddOrgDomain(ctx context.Context, orgDomain *domain.OrgDomain) (*domain.OrgDomain, error) {
|
||||||
|
if !orgDomain.IsValid() {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-R24hb", "Errors.Org.InvalidDomain")
|
||||||
|
}
|
||||||
domainWriteModel := NewOrgDomainWriteModel(orgDomain.AggregateID, orgDomain.Domain)
|
domainWriteModel := NewOrgDomainWriteModel(orgDomain.AggregateID, orgDomain.Domain)
|
||||||
orgAgg := OrgAggregateFromWriteModel(&domainWriteModel.WriteModel)
|
orgAgg := OrgAggregateFromWriteModel(&domainWriteModel.WriteModel)
|
||||||
events, err := c.addOrgDomain(ctx, orgAgg, domainWriteModel, orgDomain)
|
events, err := c.addOrgDomain(ctx, orgAgg, domainWriteModel, orgDomain)
|
||||||
@@ -31,19 +34,19 @@ func (c *Commands) AddOrgDomain(ctx context.Context, orgDomain *domain.OrgDomain
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) GenerateOrgDomainValidation(ctx context.Context, orgDomain *domain.OrgDomain) (token, url string, err error) {
|
func (c *Commands) GenerateOrgDomainValidation(ctx context.Context, orgDomain *domain.OrgDomain) (token, url string, err error) {
|
||||||
if orgDomain == nil || !orgDomain.IsValid() {
|
if orgDomain == nil || !orgDomain.IsValid() || orgDomain.AggregateID == "" {
|
||||||
return "", "", caos_errs.ThrowPreconditionFailed(nil, "ORG-R24hb", "Errors.Org.InvalidDomain")
|
return "", "", caos_errs.ThrowInvalidArgument(nil, "ORG-R24hb", "Errors.Org.InvalidDomain")
|
||||||
}
|
}
|
||||||
checkType, ok := orgDomain.ValidationType.CheckType()
|
checkType, ok := orgDomain.ValidationType.CheckType()
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", "", caos_errs.ThrowPreconditionFailed(nil, "ORG-Gsw31", "Errors.Org.DomainVerificationTypeInvalid")
|
return "", "", caos_errs.ThrowInvalidArgument(nil, "ORG-Gsw31", "Errors.Org.DomainVerificationTypeInvalid")
|
||||||
}
|
}
|
||||||
domainWriteModel, err := c.getOrgDomainWriteModel(ctx, orgDomain.AggregateID, orgDomain.Domain)
|
domainWriteModel, err := c.getOrgDomainWriteModel(ctx, orgDomain.AggregateID, orgDomain.Domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
if domainWriteModel.State != domain.OrgDomainStateActive {
|
if domainWriteModel.State != domain.OrgDomainStateActive {
|
||||||
return "", "", caos_errs.ThrowPreconditionFailed(nil, "ORG-AGD31", "Errors.Org.DomainNotOnOrg")
|
return "", "", caos_errs.ThrowNotFound(nil, "ORG-AGD31", "Errors.Org.DomainNotOnOrg")
|
||||||
}
|
}
|
||||||
if domainWriteModel.Verified {
|
if domainWriteModel.Verified {
|
||||||
return "", "", caos_errs.ThrowPreconditionFailed(nil, "ORG-HGw21", "Errors.Org.DomainAlreadyVerified")
|
return "", "", caos_errs.ThrowPreconditionFailed(nil, "ORG-HGw21", "Errors.Org.DomainAlreadyVerified")
|
||||||
@@ -69,15 +72,15 @@ func (c *Commands) GenerateOrgDomainValidation(ctx context.Context, orgDomain *d
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) ValidateOrgDomain(ctx context.Context, orgDomain *domain.OrgDomain, claimedUserIDs ...string) (*domain.ObjectDetails, error) {
|
func (c *Commands) ValidateOrgDomain(ctx context.Context, orgDomain *domain.OrgDomain, claimedUserIDs ...string) (*domain.ObjectDetails, error) {
|
||||||
if orgDomain == nil || !orgDomain.IsValid() {
|
if orgDomain == nil || !orgDomain.IsValid() || orgDomain.AggregateID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-R24hb", "Errors.Org.InvalidDomain")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-R24hb", "Errors.Org.InvalidDomain")
|
||||||
}
|
}
|
||||||
domainWriteModel, err := c.getOrgDomainWriteModel(ctx, orgDomain.AggregateID, orgDomain.Domain)
|
domainWriteModel, err := c.getOrgDomainWriteModel(ctx, orgDomain.AggregateID, orgDomain.Domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if domainWriteModel.State != domain.OrgDomainStateActive {
|
if domainWriteModel.State != domain.OrgDomainStateActive {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-Sjdi3", "Errors.Org.DomainNotOnOrg")
|
return nil, caos_errs.ThrowNotFound(nil, "ORG-Sjdi3", "Errors.Org.DomainNotOnOrg")
|
||||||
}
|
}
|
||||||
if domainWriteModel.Verified {
|
if domainWriteModel.Verified {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-HGw21", "Errors.Org.DomainAlreadyVerified")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-HGw21", "Errors.Org.DomainAlreadyVerified")
|
||||||
@@ -122,15 +125,15 @@ func (c *Commands) ValidateOrgDomain(ctx context.Context, orgDomain *domain.OrgD
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) SetPrimaryOrgDomain(ctx context.Context, orgDomain *domain.OrgDomain) (*domain.ObjectDetails, error) {
|
func (c *Commands) SetPrimaryOrgDomain(ctx context.Context, orgDomain *domain.OrgDomain) (*domain.ObjectDetails, error) {
|
||||||
if orgDomain == nil || !orgDomain.IsValid() {
|
if orgDomain == nil || !orgDomain.IsValid() || orgDomain.AggregateID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-SsDG2", "Errors.Org.InvalidDomain")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-SsDG2", "Errors.Org.InvalidDomain")
|
||||||
}
|
}
|
||||||
domainWriteModel, err := c.getOrgDomainWriteModel(ctx, orgDomain.AggregateID, orgDomain.Domain)
|
domainWriteModel, err := c.getOrgDomainWriteModel(ctx, orgDomain.AggregateID, orgDomain.Domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if domainWriteModel.State != domain.OrgDomainStateActive {
|
if domainWriteModel.State != domain.OrgDomainStateActive {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-GDfA3", "Errors.Org.DomainNotOnOrg")
|
return nil, caos_errs.ThrowNotFound(nil, "ORG-GDfA3", "Errors.Org.DomainNotOnOrg")
|
||||||
}
|
}
|
||||||
if !domainWriteModel.Verified {
|
if !domainWriteModel.Verified {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-Ggd32", "Errors.Org.DomainNotVerified")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-Ggd32", "Errors.Org.DomainNotVerified")
|
||||||
@@ -148,21 +151,21 @@ func (c *Commands) SetPrimaryOrgDomain(ctx context.Context, orgDomain *domain.Or
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) RemoveOrgDomain(ctx context.Context, orgDomain *domain.OrgDomain) (*domain.ObjectDetails, error) {
|
func (c *Commands) RemoveOrgDomain(ctx context.Context, orgDomain *domain.OrgDomain) (*domain.ObjectDetails, error) {
|
||||||
if orgDomain == nil || !orgDomain.IsValid() {
|
if orgDomain == nil || !orgDomain.IsValid() || orgDomain.AggregateID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-SJsK3", "Errors.Org.InvalidDomain")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-SJsK3", "Errors.Org.InvalidDomain")
|
||||||
}
|
}
|
||||||
domainWriteModel, err := c.getOrgDomainWriteModel(ctx, orgDomain.AggregateID, orgDomain.Domain)
|
domainWriteModel, err := c.getOrgDomainWriteModel(ctx, orgDomain.AggregateID, orgDomain.Domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if domainWriteModel.State != domain.OrgDomainStateActive {
|
if domainWriteModel.State != domain.OrgDomainStateActive {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-GDfA3", "Errors.Org.DomainNotOnOrg")
|
return nil, caos_errs.ThrowNotFound(nil, "ORG-GDfA3", "Errors.Org.DomainNotOnOrg")
|
||||||
}
|
}
|
||||||
if domainWriteModel.Primary {
|
if domainWriteModel.Primary {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-Sjdi3", "Errors.Org.PrimaryDomainNotDeletable")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-Sjdi3", "Errors.Org.PrimaryDomainNotDeletable")
|
||||||
}
|
}
|
||||||
orgAgg := OrgAggregateFromWriteModel(&domainWriteModel.WriteModel)
|
orgAgg := OrgAggregateFromWriteModel(&domainWriteModel.WriteModel)
|
||||||
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewDomainRemovedEvent(ctx, orgAgg, orgDomain.Domain))
|
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewDomainRemovedEvent(ctx, orgAgg, orgDomain.Domain, domainWriteModel.Verified))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
1318
internal/command/org_domain_test.go
Normal file
1318
internal/command/org_domain_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,10 @@ import (
|
|||||||
org_repo "github.com/caos/zitadel/internal/repository/org"
|
org_repo "github.com/caos/zitadel/internal/repository/org"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Commands) AddIDPConfig(ctx context.Context, config *domain.IDPConfig) (*domain.IDPConfig, error) {
|
func (c *Commands) AddIDPConfig(ctx context.Context, config *domain.IDPConfig, resourceOwner string) (*domain.IDPConfig, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-0j8gs", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
if config.OIDCConfig == nil {
|
if config.OIDCConfig == nil {
|
||||||
return nil, errors.ThrowInvalidArgument(nil, "Org-eUpQU", "Errors.idp.config.notset")
|
return nil, errors.ThrowInvalidArgument(nil, "Org-eUpQU", "Errors.idp.config.notset")
|
||||||
}
|
}
|
||||||
@@ -21,7 +24,7 @@ func (c *Commands) AddIDPConfig(ctx context.Context, config *domain.IDPConfig) (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
addedConfig := NewOrgIDPConfigWriteModel(idpConfigID, config.AggregateID)
|
addedConfig := NewOrgIDPConfigWriteModel(idpConfigID, resourceOwner)
|
||||||
|
|
||||||
clientSecret, err := crypto.Crypt([]byte(config.OIDCConfig.ClientSecretString), c.idpConfigSecretCrypto)
|
clientSecret, err := crypto.Crypt([]byte(config.OIDCConfig.ClientSecretString), c.idpConfigSecretCrypto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -60,7 +63,10 @@ func (c *Commands) AddIDPConfig(ctx context.Context, config *domain.IDPConfig) (
|
|||||||
return writeModelToIDPConfig(&addedConfig.IDPConfigWriteModel), nil
|
return writeModelToIDPConfig(&addedConfig.IDPConfigWriteModel), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) ChangeIDPConfig(ctx context.Context, config *domain.IDPConfig) (*domain.IDPConfig, error) {
|
func (c *Commands) ChangeIDPConfig(ctx context.Context, config *domain.IDPConfig, resourceOwner string) (*domain.IDPConfig, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Gh8ds", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
existingIDP, err := c.orgIDPConfigWriteModelByID(ctx, config.IDPConfigID, config.AggregateID)
|
existingIDP, err := c.orgIDPConfigWriteModelByID(ctx, config.IDPConfigID, config.AggregateID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -149,7 +155,7 @@ func (c *Commands) getOrgIDPConfigByID(ctx context.Context, idpID, orgID string)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !config.State.Exists() {
|
if !config.State.Exists() {
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-4M9so", "Errors.Org.IDPConfig.NotExisting")
|
return nil, caos_errs.ThrowNotFound(nil, "ORG-4M9so", "Errors.Org.IDPConfig.NotExisting")
|
||||||
}
|
}
|
||||||
return writeModelToIDPConfig(&config.IDPConfigWriteModel), nil
|
return writeModelToIDPConfig(&config.IDPConfigWriteModel), nil
|
||||||
}
|
}
|
||||||
|
|||||||
343
internal/command/org_idp_config_test.go
Normal file
343
internal/command/org_idp_config_test.go
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/caos/zitadel/internal/id"
|
||||||
|
id_mock "github.com/caos/zitadel/internal/id/mock"
|
||||||
|
"github.com/caos/zitadel/internal/repository/idpconfig"
|
||||||
|
"github.com/caos/zitadel/internal/repository/org"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_AddIDPConfig(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
idGenerator id.Generator
|
||||||
|
secretCrypto crypto.EncryptionAlgorithm
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
config *domain.IDPConfig
|
||||||
|
resourceOwner string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.IDPConfig
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "resourceowner missing, error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.IDPConfig{
|
||||||
|
Name: "name1",
|
||||||
|
StylingType: domain.IDPConfigStylingTypeGoogle,
|
||||||
|
OIDCConfig: &domain.OIDCIDPConfig{
|
||||||
|
ClientID: "clientid1",
|
||||||
|
Issuer: "issuer",
|
||||||
|
ClientSecretString: "secret",
|
||||||
|
Scopes: []string{"scope"},
|
||||||
|
IDPDisplayNameMapping: domain.OIDCMappingFieldEmail,
|
||||||
|
UsernameMapping: domain.OIDCMappingFieldEmail,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid config, error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
resourceOwner: "org1",
|
||||||
|
config: &domain.IDPConfig{},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "idp config oidc add, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewIDPConfigAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name1",
|
||||||
|
domain.IDPConfigTypeOIDC,
|
||||||
|
domain.IDPConfigStylingTypeGoogle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewIDPOIDCConfigAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"clientid1",
|
||||||
|
"config1",
|
||||||
|
"issuer",
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("secret"),
|
||||||
|
},
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
"scope",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
uniqueConstraintsFromEventConstraint(idpconfig.NewAddIDPConfigNameUniqueConstraint("name1", "org1")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "config1"),
|
||||||
|
secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
resourceOwner: "org1",
|
||||||
|
config: &domain.IDPConfig{
|
||||||
|
Name: "name1",
|
||||||
|
StylingType: domain.IDPConfigStylingTypeGoogle,
|
||||||
|
OIDCConfig: &domain.OIDCIDPConfig{
|
||||||
|
ClientID: "clientid1",
|
||||||
|
Issuer: "issuer",
|
||||||
|
ClientSecretString: "secret",
|
||||||
|
Scopes: []string{"scope"},
|
||||||
|
IDPDisplayNameMapping: domain.OIDCMappingFieldEmail,
|
||||||
|
UsernameMapping: domain.OIDCMappingFieldEmail,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.IDPConfig{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
Name: "name1",
|
||||||
|
StylingType: domain.IDPConfigStylingTypeGoogle,
|
||||||
|
State: domain.IDPConfigStateActive,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
idGenerator: tt.fields.idGenerator,
|
||||||
|
idpConfigSecretCrypto: tt.fields.secretCrypto,
|
||||||
|
}
|
||||||
|
got, err := r.AddIDPConfig(tt.args.ctx, tt.args.config, tt.args.resourceOwner)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_ChangeIDPConfig(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
resourceOwner string
|
||||||
|
config *domain.IDPConfig
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.IDPConfig
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "missing resourceowner, error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.IDPConfig{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid config, error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.IDPConfig{},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "config not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
resourceOwner: "org1",
|
||||||
|
config: &domain.IDPConfig{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "idp config change, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewIDPConfigAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name1",
|
||||||
|
domain.IDPConfigTypeOIDC,
|
||||||
|
domain.IDPConfigStylingTypeGoogle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewIDPOIDCConfigAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"clientid1",
|
||||||
|
"config1",
|
||||||
|
"issuer",
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
"scope",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
newIDPConfigChangedEvent(context.Background(), "org1", "config1", "name1", "name2", domain.IDPConfigStylingTypeUnspecified),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
uniqueConstraintsFromEventConstraint(idpconfig.NewRemoveIDPConfigNameUniqueConstraint("name1", "org1")),
|
||||||
|
uniqueConstraintsFromEventConstraint(idpconfig.NewAddIDPConfigNameUniqueConstraint("name2", "org1")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
resourceOwner: "org1",
|
||||||
|
config: &domain.IDPConfig{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
Name: "name2",
|
||||||
|
StylingType: domain.IDPConfigStylingTypeUnspecified,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.IDPConfig{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
Name: "name2",
|
||||||
|
StylingType: domain.IDPConfigStylingTypeUnspecified,
|
||||||
|
State: domain.IDPConfigStateActive,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.ChangeIDPConfig(tt.args.ctx, tt.args.config, tt.args.resourceOwner)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIDPConfigChangedEvent(ctx context.Context, orgID, configID, oldName, newName string, stylingType domain.IDPConfigStylingType) *org.IDPConfigChangedEvent {
|
||||||
|
event, _ := org.NewIDPConfigChangedEvent(ctx,
|
||||||
|
&org.NewAggregate(orgID, orgID).Aggregate,
|
||||||
|
configID,
|
||||||
|
oldName,
|
||||||
|
[]idpconfig.IDPConfigChanges{
|
||||||
|
idpconfig.ChangeName(newName),
|
||||||
|
idpconfig.ChangeStyleType(stylingType),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return event
|
||||||
|
}
|
||||||
@@ -6,15 +6,21 @@ import (
|
|||||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Commands) ChangeIDPOIDCConfig(ctx context.Context, config *domain.OIDCIDPConfig) (*domain.OIDCIDPConfig, error) {
|
func (c *Commands) ChangeIDPOIDCConfig(ctx context.Context, config *domain.OIDCIDPConfig, resourceOwner string) (*domain.OIDCIDPConfig, error) {
|
||||||
existingConfig := NewOrgIDPOIDCConfigWriteModel(config.IDPConfigID, config.AggregateID)
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-4n8f2", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
|
if config.IDPConfigID == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-66Qwj", "Errors.IDMissing")
|
||||||
|
}
|
||||||
|
existingConfig := NewOrgIDPOIDCConfigWriteModel(config.IDPConfigID, resourceOwner)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, existingConfig)
|
err := c.eventstore.FilterToQueryReducer(ctx, existingConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if existingConfig.State == domain.IDPConfigStateRemoved || existingConfig.State == domain.IDPConfigStateUnspecified {
|
if existingConfig.State == domain.IDPConfigStateRemoved || existingConfig.State == domain.IDPConfigStateUnspecified {
|
||||||
return nil, caos_errs.ThrowAlreadyExists(nil, "Org-67J9d", "Errors.Org.IDPConfig.AlreadyExists")
|
return nil, caos_errs.ThrowNotFound(nil, "Org-67J9d", "Errors.Org.IDPConfig.AlreadyExists")
|
||||||
}
|
}
|
||||||
|
|
||||||
orgAgg := OrgAggregateFromWriteModel(&existingConfig.WriteModel)
|
orgAgg := OrgAggregateFromWriteModel(&existingConfig.WriteModel)
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ func (wm *IDPOIDCConfigWriteModel) NewChangedEvent(
|
|||||||
if userNameMapping.Valid() && wm.UserNameMapping != userNameMapping {
|
if userNameMapping.Valid() && wm.UserNameMapping != userNameMapping {
|
||||||
changes = append(changes, idpconfig.ChangeUserNameMapping(userNameMapping))
|
changes = append(changes, idpconfig.ChangeUserNameMapping(userNameMapping))
|
||||||
}
|
}
|
||||||
if reflect.DeepEqual(wm.Scopes, scopes) {
|
if !reflect.DeepEqual(wm.Scopes, scopes) {
|
||||||
changes = append(changes, idpconfig.ChangeScopes(scopes))
|
changes = append(changes, idpconfig.ChangeScopes(scopes))
|
||||||
}
|
}
|
||||||
if len(changes) == 0 {
|
if len(changes) == 0 {
|
||||||
|
|||||||
319
internal/command/org_idp_oidc_config_test.go
Normal file
319
internal/command/org_idp_oidc_config_test.go
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/caos/zitadel/internal/repository/idpconfig"
|
||||||
|
"github.com/caos/zitadel/internal/repository/org"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_ChangeIDPOIDCConfig(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
secretCrypto crypto.EncryptionAlgorithm
|
||||||
|
}
|
||||||
|
type (
|
||||||
|
args struct {
|
||||||
|
ctx context.Context
|
||||||
|
config *domain.OIDCIDPConfig
|
||||||
|
resourceOwner string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
type res struct {
|
||||||
|
want *domain.OIDCIDPConfig
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "resourceowner missing, error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.OIDCIDPConfig{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid config, error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.OIDCIDPConfig{},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "idp config not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.OIDCIDPConfig{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "idp config removed, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewIDPConfigAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name1",
|
||||||
|
domain.IDPConfigTypeOIDC,
|
||||||
|
domain.IDPConfigStylingTypeGoogle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewIDPOIDCConfigAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"clientid1",
|
||||||
|
"config1",
|
||||||
|
"issuer",
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
"scope",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewIDPConfigRemovedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.OIDCIDPConfig{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no changes, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewIDPConfigAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name1",
|
||||||
|
domain.IDPConfigTypeOIDC,
|
||||||
|
domain.IDPConfigStylingTypeGoogle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewIDPOIDCConfigAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"clientid1",
|
||||||
|
"config1",
|
||||||
|
"issuer",
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("secret"),
|
||||||
|
},
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
"scope",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.OIDCIDPConfig{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
ClientID: "clientid1",
|
||||||
|
Issuer: "issuer",
|
||||||
|
Scopes: []string{"scope"},
|
||||||
|
IDPDisplayNameMapping: domain.OIDCMappingFieldEmail,
|
||||||
|
UsernameMapping: domain.OIDCMappingFieldEmail,
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "idp config oidc add, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewIDPConfigAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name1",
|
||||||
|
domain.IDPConfigTypeOIDC,
|
||||||
|
domain.IDPConfigStylingTypeGoogle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewIDPOIDCConfigAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"clientid1",
|
||||||
|
"config1",
|
||||||
|
"issuer",
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("secret"),
|
||||||
|
},
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
domain.OIDCMappingFieldEmail,
|
||||||
|
"scope",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
newIDPOIDCConfigChangedEvent(context.Background(),
|
||||||
|
"org1",
|
||||||
|
"config1",
|
||||||
|
"clientid-changed",
|
||||||
|
"issuer-changed",
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("secret-changed"),
|
||||||
|
},
|
||||||
|
domain.OIDCMappingFieldPreferredLoginName,
|
||||||
|
domain.OIDCMappingFieldPreferredLoginName,
|
||||||
|
[]string{"scope", "scope2"},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: &domain.OIDCIDPConfig{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
ClientID: "clientid-changed",
|
||||||
|
Issuer: "issuer-changed",
|
||||||
|
ClientSecretString: "secret-changed",
|
||||||
|
Scopes: []string{"scope", "scope2"},
|
||||||
|
IDPDisplayNameMapping: domain.OIDCMappingFieldPreferredLoginName,
|
||||||
|
UsernameMapping: domain.OIDCMappingFieldPreferredLoginName,
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.OIDCIDPConfig{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
ClientID: "clientid-changed",
|
||||||
|
Issuer: "issuer-changed",
|
||||||
|
Scopes: []string{"scope", "scope2"},
|
||||||
|
IDPDisplayNameMapping: domain.OIDCMappingFieldPreferredLoginName,
|
||||||
|
UsernameMapping: domain.OIDCMappingFieldPreferredLoginName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
idpConfigSecretCrypto: tt.fields.secretCrypto,
|
||||||
|
}
|
||||||
|
got, err := r.ChangeIDPOIDCConfig(tt.args.ctx, tt.args.config, tt.args.resourceOwner)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIDPOIDCConfigChangedEvent(ctx context.Context, orgID, configID, clientID, issuer string, secret *crypto.CryptoValue, displayMapping, usernameMapping domain.OIDCMappingField, scopes []string) *org.IDPOIDCConfigChangedEvent {
|
||||||
|
event, _ := org.NewIDPOIDCConfigChangedEvent(ctx,
|
||||||
|
&org.NewAggregate(orgID, orgID).Aggregate,
|
||||||
|
configID,
|
||||||
|
[]idpconfig.OIDCConfigChanges{
|
||||||
|
idpconfig.ChangeClientID(clientID),
|
||||||
|
idpconfig.ChangeIssuer(issuer),
|
||||||
|
idpconfig.ChangeClientSecret(secret),
|
||||||
|
idpconfig.ChangeIDPDisplayNameMapping(displayMapping),
|
||||||
|
idpconfig.ChangeUserNameMapping(usernameMapping),
|
||||||
|
idpconfig.ChangeScopes(scopes),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return event
|
||||||
|
}
|
||||||
@@ -9,6 +9,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *Commands) AddLabelPolicy(ctx context.Context, resourceOwner string, policy *domain.LabelPolicy) (*domain.LabelPolicy, error) {
|
func (c *Commands) AddLabelPolicy(ctx context.Context, resourceOwner string, policy *domain.LabelPolicy) (*domain.LabelPolicy, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Fn8ds", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
|
if !policy.IsValid() {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Md9sf", "Errors.Org.LabelPolicy.Invalid")
|
||||||
|
}
|
||||||
addedPolicy := NewOrgLabelPolicyWriteModel(resourceOwner)
|
addedPolicy := NewOrgLabelPolicyWriteModel(resourceOwner)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -31,6 +37,12 @@ func (c *Commands) AddLabelPolicy(ctx context.Context, resourceOwner string, pol
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) ChangeLabelPolicy(ctx context.Context, resourceOwner string, policy *domain.LabelPolicy) (*domain.LabelPolicy, error) {
|
func (c *Commands) ChangeLabelPolicy(ctx context.Context, resourceOwner string, policy *domain.LabelPolicy) (*domain.LabelPolicy, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-3N9fs", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
|
if !policy.IsValid() {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-dM9fs", "Errors.Org.LabelPolicy.Invalid")
|
||||||
|
}
|
||||||
existingPolicy := NewOrgLabelPolicyWriteModel(resourceOwner)
|
existingPolicy := NewOrgLabelPolicyWriteModel(resourceOwner)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -58,6 +70,9 @@ func (c *Commands) ChangeLabelPolicy(ctx context.Context, resourceOwner string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) RemoveLabelPolicy(ctx context.Context, orgID string) error {
|
func (c *Commands) RemoveLabelPolicy(ctx context.Context, orgID string) error {
|
||||||
|
if orgID == "" {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "Org-Mf9sf", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
existingPolicy := NewOrgLabelPolicyWriteModel(orgID)
|
existingPolicy := NewOrgLabelPolicyWriteModel(orgID)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
429
internal/command/org_policy_label_test.go
Normal file
429
internal/command/org_policy_label_test.go
Normal file
@@ -0,0 +1,429 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/caos/zitadel/internal/repository/org"
|
||||||
|
"github.com/caos/zitadel/internal/repository/policy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_AddLabelPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
policy *domain.LabelPolicy
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.LabelPolicy
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
policy: &domain.LabelPolicy{
|
||||||
|
PrimaryColor: "",
|
||||||
|
SecondaryColor: "secondary-color",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "labelpolicy invalid, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.LabelPolicy{
|
||||||
|
PrimaryColor: "",
|
||||||
|
SecondaryColor: "secondary-color",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "labelpolicy already existing, already exists error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewLabelPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"primary-color",
|
||||||
|
"secondary-color",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.LabelPolicy{
|
||||||
|
PrimaryColor: "primary-color",
|
||||||
|
SecondaryColor: "secondary-color",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorAlreadyExists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add policy,ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewLabelPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"primary-color",
|
||||||
|
"secondary-color",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.LabelPolicy{
|
||||||
|
PrimaryColor: "primary-color",
|
||||||
|
SecondaryColor: "secondary-color",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.LabelPolicy{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
PrimaryColor: "primary-color",
|
||||||
|
SecondaryColor: "secondary-color",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.AddLabelPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_ChangeLabelPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
policy *domain.LabelPolicy
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.LabelPolicy
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
policy: &domain.LabelPolicy{
|
||||||
|
PrimaryColor: "primary-color",
|
||||||
|
SecondaryColor: "secondary-color",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "labelpolicy invalid, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.LabelPolicy{
|
||||||
|
PrimaryColor: "",
|
||||||
|
SecondaryColor: "secondary-color",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "labelpolicy not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.LabelPolicy{
|
||||||
|
PrimaryColor: "primary-color",
|
||||||
|
SecondaryColor: "secondary-color",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no changes, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewLabelPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"primary-color",
|
||||||
|
"secondary-color",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.LabelPolicy{
|
||||||
|
PrimaryColor: "primary-color",
|
||||||
|
SecondaryColor: "secondary-color",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "change, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewLabelPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"primary-color",
|
||||||
|
"secondary-color",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
newLabelPolicyChangedEvent(context.Background(), "org1", "primary-color-change", "secondary-color-change"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.LabelPolicy{
|
||||||
|
PrimaryColor: "primary-color-change",
|
||||||
|
SecondaryColor: "secondary-color-change",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.LabelPolicy{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
PrimaryColor: "primary-color-change",
|
||||||
|
SecondaryColor: "secondary-color-change",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.ChangeLabelPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_RemoveLabelPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "labelpolicy not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewLabelPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"primary-color",
|
||||||
|
"secondary-color",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewLabelPolicyRemovedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
},
|
||||||
|
res: res{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
err := r.RemoveLabelPolicy(tt.args.ctx, tt.args.orgID)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLabelPolicyChangedEvent(ctx context.Context, orgID, primaryColor, secondaryColor string) *org.LabelPolicyChangedEvent {
|
||||||
|
event, _ := org.NewLabelPolicyChangedEvent(ctx,
|
||||||
|
&org.NewAggregate(orgID, orgID).Aggregate,
|
||||||
|
[]policy.LabelPolicyChanges{
|
||||||
|
policy.ChangePrimaryColor(primaryColor),
|
||||||
|
policy.ChangeSecondaryColor(secondaryColor),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return event
|
||||||
|
}
|
||||||
@@ -10,6 +10,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, policy *domain.LoginPolicy) (*domain.LoginPolicy, error) {
|
func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, policy *domain.LoginPolicy) (*domain.LoginPolicy, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Fn8ds", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
addedPolicy := NewOrgLoginPolicyWriteModel(resourceOwner)
|
addedPolicy := NewOrgLoginPolicyWriteModel(resourceOwner)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -41,6 +44,9 @@ func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, pol
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) ChangeLoginPolicy(ctx context.Context, resourceOwner string, policy *domain.LoginPolicy) (*domain.LoginPolicy, error) {
|
func (c *Commands) ChangeLoginPolicy(ctx context.Context, resourceOwner string, policy *domain.LoginPolicy) (*domain.LoginPolicy, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Mf9sf", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
existingPolicy := NewOrgLoginPolicyWriteModel(resourceOwner)
|
existingPolicy := NewOrgLoginPolicyWriteModel(resourceOwner)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -67,6 +73,9 @@ func (c *Commands) ChangeLoginPolicy(ctx context.Context, resourceOwner string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) RemoveLoginPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
|
func (c *Commands) RemoveLoginPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
|
||||||
|
if orgID == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-55Mg9", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
existingPolicy := NewOrgLoginPolicyWriteModel(orgID)
|
existingPolicy := NewOrgLoginPolicyWriteModel(orgID)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -88,8 +97,23 @@ func (c *Commands) RemoveLoginPolicy(ctx context.Context, orgID string) (*domain
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) AddIDPProviderToLoginPolicy(ctx context.Context, resourceOwner string, idpProvider *domain.IDPProvider) (*domain.IDPProvider, error) {
|
func (c *Commands) AddIDPProviderToLoginPolicy(ctx context.Context, resourceOwner string, idpProvider *domain.IDPProvider) (*domain.IDPProvider, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-M0fs9", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
|
if !idpProvider.IsValid() {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-9nf88", "Errors.Org.LoginPolicy.IDP.")
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
if idpProvider.Type == domain.IdentityProviderTypeOrg {
|
||||||
|
_, err = c.getOrgIDPConfigByID(ctx, idpProvider.IDPConfigID, resourceOwner)
|
||||||
|
} else {
|
||||||
|
_, err = c.getIAMIDPConfigByID(ctx, idpProvider.IDPConfigID)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, caos_errs.ThrowPreconditionFailed(err, "Org-3N9fs", "Errors.IDPConfig.NotExisting")
|
||||||
|
}
|
||||||
idpModel := NewOrgIdentityProviderWriteModel(resourceOwner, idpProvider.IDPConfigID)
|
idpModel := NewOrgIdentityProviderWriteModel(resourceOwner, idpProvider.IDPConfigID)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, idpModel)
|
err = c.eventstore.FilterToQueryReducer(ctx, idpModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -110,6 +134,12 @@ func (c *Commands) AddIDPProviderToLoginPolicy(ctx context.Context, resourceOwne
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) RemoveIDPProviderFromLoginPolicy(ctx context.Context, resourceOwner string, idpProvider *domain.IDPProvider, cascadeExternalIDPs ...*domain.ExternalIDP) (*domain.ObjectDetails, error) {
|
func (c *Commands) RemoveIDPProviderFromLoginPolicy(ctx context.Context, resourceOwner string, idpProvider *domain.IDPProvider, cascadeExternalIDPs ...*domain.ExternalIDP) (*domain.ObjectDetails, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-M0fs9", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
|
if !idpProvider.IsValid() {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-66m9s", "Errors.Org.LoginPolicy.IDP.Invalid")
|
||||||
|
}
|
||||||
idpModel := NewOrgIdentityProviderWriteModel(resourceOwner, idpProvider.IDPConfigID)
|
idpModel := NewOrgIdentityProviderWriteModel(resourceOwner, idpProvider.IDPConfigID)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, idpModel)
|
err := c.eventstore.FilterToQueryReducer(ctx, idpModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -153,7 +183,13 @@ func (c *Commands) removeIDPProviderFromLoginPolicy(ctx context.Context, orgAgg
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) AddSecondFactorToLoginPolicy(ctx context.Context, secondFactor domain.SecondFactorType, orgID string) (domain.SecondFactorType, error) {
|
func (c *Commands) AddSecondFactorToLoginPolicy(ctx context.Context, secondFactor domain.SecondFactorType, orgID string) (domain.SecondFactorType, error) {
|
||||||
secondFactorModel := NewOrgSecondFactorWriteModel(orgID)
|
if orgID == "" {
|
||||||
|
return domain.SecondFactorTypeUnspecified, caos_errs.ThrowInvalidArgument(nil, "Org-M0fs9", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
|
if !secondFactor.Valid() {
|
||||||
|
return domain.SecondFactorTypeUnspecified, caos_errs.ThrowInvalidArgument(nil, "Org-5m9fs", "Errors.Org.LoginPolicy.MFA.Unspecified")
|
||||||
|
}
|
||||||
|
secondFactorModel := NewOrgSecondFactorWriteModel(orgID, secondFactor)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, secondFactorModel)
|
err := c.eventstore.FilterToQueryReducer(ctx, secondFactorModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return domain.SecondFactorTypeUnspecified, err
|
return domain.SecondFactorTypeUnspecified, err
|
||||||
@@ -173,7 +209,13 @@ func (c *Commands) AddSecondFactorToLoginPolicy(ctx context.Context, secondFacto
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) RemoveSecondFactorFromLoginPolicy(ctx context.Context, secondFactor domain.SecondFactorType, orgID string) error {
|
func (c *Commands) RemoveSecondFactorFromLoginPolicy(ctx context.Context, secondFactor domain.SecondFactorType, orgID string) error {
|
||||||
secondFactorModel := NewOrgSecondFactorWriteModel(orgID)
|
if orgID == "" {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "Org-fM0gs", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
|
if !secondFactor.Valid() {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "Org-55n8s", "Errors.Org.LoginPolicy.MFA.Unspecified")
|
||||||
|
}
|
||||||
|
secondFactorModel := NewOrgSecondFactorWriteModel(orgID, secondFactor)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, secondFactorModel)
|
err := c.eventstore.FilterToQueryReducer(ctx, secondFactorModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -188,7 +230,13 @@ func (c *Commands) RemoveSecondFactorFromLoginPolicy(ctx context.Context, second
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) AddMultiFactorToLoginPolicy(ctx context.Context, multiFactor domain.MultiFactorType, orgID string) (domain.MultiFactorType, error) {
|
func (c *Commands) AddMultiFactorToLoginPolicy(ctx context.Context, multiFactor domain.MultiFactorType, orgID string) (domain.MultiFactorType, error) {
|
||||||
multiFactorModel := NewOrgMultiFactorWriteModel(orgID)
|
if orgID == "" {
|
||||||
|
return domain.MultiFactorTypeUnspecified, caos_errs.ThrowInvalidArgument(nil, "Org-M0fsf", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
|
if !multiFactor.Valid() {
|
||||||
|
return domain.MultiFactorTypeUnspecified, caos_errs.ThrowInvalidArgument(nil, "Org-5m9fs", "Errors.Org.LoginPolicy.MFA.Unspecified")
|
||||||
|
}
|
||||||
|
multiFactorModel := NewOrgMultiFactorWriteModel(orgID, multiFactor)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, multiFactorModel)
|
err := c.eventstore.FilterToQueryReducer(ctx, multiFactorModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return domain.MultiFactorTypeUnspecified, err
|
return domain.MultiFactorTypeUnspecified, err
|
||||||
@@ -207,7 +255,13 @@ func (c *Commands) AddMultiFactorToLoginPolicy(ctx context.Context, multiFactor
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) RemoveMultiFactorFromLoginPolicy(ctx context.Context, multiFactor domain.MultiFactorType, orgID string) error {
|
func (c *Commands) RemoveMultiFactorFromLoginPolicy(ctx context.Context, multiFactor domain.MultiFactorType, orgID string) error {
|
||||||
multiFactorModel := NewOrgMultiFactorWriteModel(orgID)
|
if orgID == "" {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "Org-M0fsf", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
|
if !multiFactor.Valid() {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "Org-5m9fs", "Errors.Org.LoginPolicy.MFA.Unspecified")
|
||||||
|
}
|
||||||
|
multiFactorModel := NewOrgMultiFactorWriteModel(orgID, multiFactor)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, multiFactorModel)
|
err := c.eventstore.FilterToQueryReducer(ctx, multiFactorModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
"github.com/caos/zitadel/internal/eventstore"
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
"github.com/caos/zitadel/internal/repository/org"
|
"github.com/caos/zitadel/internal/repository/org"
|
||||||
)
|
)
|
||||||
@@ -9,13 +10,14 @@ type OrgSecondFactorWriteModel struct {
|
|||||||
SecondFactorWriteModel
|
SecondFactorWriteModel
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOrgSecondFactorWriteModel(orgID string) *OrgSecondFactorWriteModel {
|
func NewOrgSecondFactorWriteModel(orgID string, factorType domain.SecondFactorType) *OrgSecondFactorWriteModel {
|
||||||
return &OrgSecondFactorWriteModel{
|
return &OrgSecondFactorWriteModel{
|
||||||
SecondFactorWriteModel{
|
SecondFactorWriteModel{
|
||||||
WriteModel: eventstore.WriteModel{
|
WriteModel: eventstore.WriteModel{
|
||||||
AggregateID: orgID,
|
AggregateID: orgID,
|
||||||
ResourceOwner: orgID,
|
ResourceOwner: orgID,
|
||||||
},
|
},
|
||||||
|
MFAType: factorType,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,15 +26,19 @@ func (wm *OrgSecondFactorWriteModel) AppendEvents(events ...eventstore.EventRead
|
|||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
switch e := event.(type) {
|
switch e := event.(type) {
|
||||||
case *org.LoginPolicySecondFactorAddedEvent:
|
case *org.LoginPolicySecondFactorAddedEvent:
|
||||||
wm.WriteModel.AppendEvents(&e.SecondFactorAddedEvent)
|
if wm.MFAType == e.MFAType {
|
||||||
|
wm.WriteModel.AppendEvents(&e.SecondFactorAddedEvent)
|
||||||
|
}
|
||||||
case *org.LoginPolicySecondFactorRemovedEvent:
|
case *org.LoginPolicySecondFactorRemovedEvent:
|
||||||
wm.WriteModel.AppendEvents(&e.SecondFactorRemovedEvent)
|
if wm.MFAType == e.MFAType {
|
||||||
|
wm.WriteModel.AppendEvents(&e.SecondFactorRemovedEvent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wm *OrgSecondFactorWriteModel) Reduce() error {
|
func (wm *OrgSecondFactorWriteModel) Reduce() error {
|
||||||
return wm.WriteModel.Reduce()
|
return wm.SecondFactorWriteModel.Reduce()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wm *OrgSecondFactorWriteModel) Query() *eventstore.SearchQueryBuilder {
|
func (wm *OrgSecondFactorWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||||
@@ -48,13 +54,14 @@ type OrgMultiFactorWriteModel struct {
|
|||||||
MultiFactoryWriteModel
|
MultiFactoryWriteModel
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOrgMultiFactorWriteModel(orgID string) *OrgMultiFactorWriteModel {
|
func NewOrgMultiFactorWriteModel(orgID string, factorType domain.MultiFactorType) *OrgMultiFactorWriteModel {
|
||||||
return &OrgMultiFactorWriteModel{
|
return &OrgMultiFactorWriteModel{
|
||||||
MultiFactoryWriteModel{
|
MultiFactoryWriteModel{
|
||||||
WriteModel: eventstore.WriteModel{
|
WriteModel: eventstore.WriteModel{
|
||||||
AggregateID: orgID,
|
AggregateID: orgID,
|
||||||
ResourceOwner: orgID,
|
ResourceOwner: orgID,
|
||||||
},
|
},
|
||||||
|
MFAType: factorType,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,15 +70,19 @@ func (wm *OrgMultiFactorWriteModel) AppendEvents(events ...eventstore.EventReade
|
|||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
switch e := event.(type) {
|
switch e := event.(type) {
|
||||||
case *org.LoginPolicyMultiFactorAddedEvent:
|
case *org.LoginPolicyMultiFactorAddedEvent:
|
||||||
wm.WriteModel.AppendEvents(&e.MultiFactorAddedEvent)
|
if wm.MFAType == e.MFAType {
|
||||||
|
wm.WriteModel.AppendEvents(&e.MultiFactorAddedEvent)
|
||||||
|
}
|
||||||
case *org.LoginPolicyMultiFactorRemovedEvent:
|
case *org.LoginPolicyMultiFactorRemovedEvent:
|
||||||
wm.WriteModel.AppendEvents(&e.MultiFactorRemovedEvent)
|
if wm.MFAType == e.MFAType {
|
||||||
|
wm.WriteModel.AppendEvents(&e.MultiFactorRemovedEvent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wm *OrgMultiFactorWriteModel) Reduce() error {
|
func (wm *OrgMultiFactorWriteModel) Reduce() error {
|
||||||
return wm.WriteModel.Reduce()
|
return wm.MultiFactoryWriteModel.Reduce()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wm *OrgMultiFactorWriteModel) Query() *eventstore.SearchQueryBuilder {
|
func (wm *OrgMultiFactorWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package command
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/caos/zitadel/internal/eventstore"
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
"github.com/caos/zitadel/internal/repository/iam"
|
"github.com/caos/zitadel/internal/repository/org"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OrgIdentityProviderWriteModel struct {
|
type OrgIdentityProviderWriteModel struct {
|
||||||
@@ -24,11 +24,16 @@ func NewOrgIdentityProviderWriteModel(orgID, idpConfigID string) *OrgIdentityPro
|
|||||||
func (wm *OrgIdentityProviderWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
func (wm *OrgIdentityProviderWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
switch e := event.(type) {
|
switch e := event.(type) {
|
||||||
case *iam.IdentityProviderAddedEvent:
|
case *org.IdentityProviderAddedEvent:
|
||||||
if e.IDPConfigID != wm.IDPConfigID {
|
if e.IDPConfigID != wm.IDPConfigID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
wm.IdentityProviderWriteModel.AppendEvents(&e.IdentityProviderAddedEvent)
|
wm.IdentityProviderWriteModel.AppendEvents(&e.IdentityProviderAddedEvent)
|
||||||
|
case *org.IdentityProviderRemovedEvent:
|
||||||
|
if e.IDPConfigID != wm.IDPConfigID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
wm.IdentityProviderWriteModel.AppendEvents(&e.IdentityProviderRemovedEvent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -38,7 +43,10 @@ func (wm *OrgIdentityProviderWriteModel) Reduce() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (wm *OrgIdentityProviderWriteModel) Query() *eventstore.SearchQueryBuilder {
|
func (wm *OrgIdentityProviderWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType).
|
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, org.AggregateType).
|
||||||
AggregateIDs(wm.AggregateID).
|
AggregateIDs(wm.AggregateID).
|
||||||
ResourceOwner(wm.ResourceOwner)
|
ResourceOwner(wm.ResourceOwner).
|
||||||
|
EventTypes(
|
||||||
|
org.LoginPolicyIDPProviderAddedEventType,
|
||||||
|
org.LoginPolicyIDPProviderRemovedEventType)
|
||||||
}
|
}
|
||||||
|
|||||||
1487
internal/command/org_policy_login_test.go
Normal file
1487
internal/command/org_policy_login_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *Commands) AddMailTemplate(ctx context.Context, resourceOwner string, policy *domain.MailTemplate) (*domain.MailTemplate, error) {
|
func (c *Commands) AddMailTemplate(ctx context.Context, resourceOwner string, policy *domain.MailTemplate) (*domain.MailTemplate, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-M8dfs", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
if !policy.IsValid() {
|
if !policy.IsValid() {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-3m9fs", "Errors.Org.MailTemplate.Invalid")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-3m9fs", "Errors.Org.MailTemplate.Invalid")
|
||||||
}
|
}
|
||||||
@@ -34,6 +37,9 @@ func (c *Commands) AddMailTemplate(ctx context.Context, resourceOwner string, po
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) ChangeMailTemplate(ctx context.Context, resourceOwner string, policy *domain.MailTemplate) (*domain.MailTemplate, error) {
|
func (c *Commands) ChangeMailTemplate(ctx context.Context, resourceOwner string, policy *domain.MailTemplate) (*domain.MailTemplate, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-M9fFs", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
if !policy.IsValid() {
|
if !policy.IsValid() {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-9f9ds", "Errors.Org.MailTemplate.Invalid")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-9f9ds", "Errors.Org.MailTemplate.Invalid")
|
||||||
}
|
}
|
||||||
@@ -64,6 +70,9 @@ func (c *Commands) ChangeMailTemplate(ctx context.Context, resourceOwner string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) RemoveMailTemplate(ctx context.Context, orgID string) error {
|
func (c *Commands) RemoveMailTemplate(ctx context.Context, orgID string) error {
|
||||||
|
if orgID == "" {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "Org-5Jgis", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
existingPolicy := NewOrgMailTemplateWriteModel(orgID)
|
existingPolicy := NewOrgMailTemplateWriteModel(orgID)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
381
internal/command/org_policy_mail_template_test.go
Normal file
381
internal/command/org_policy_mail_template_test.go
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/caos/zitadel/internal/repository/org"
|
||||||
|
"github.com/caos/zitadel/internal/repository/policy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_AddMailTemplate(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
policy *domain.MailTemplate
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.MailTemplate
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
policy: &domain.MailTemplate{
|
||||||
|
Template: []byte("template"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mail template already existing, already exists error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewMailTemplateAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
[]byte("template"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.MailTemplate{
|
||||||
|
Template: []byte("template"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorAlreadyExists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add policy,ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewMailTemplateAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
[]byte("template"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.MailTemplate{
|
||||||
|
Template: []byte("template"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.MailTemplate{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
Template: []byte("template"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.AddMailTemplate(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_ChangeMailTemplate(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
policy *domain.MailTemplate
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.MailTemplate
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
policy: &domain.MailTemplate{
|
||||||
|
Template: []byte("template"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mail template not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.MailTemplate{
|
||||||
|
Template: []byte("template"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no changes, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewMailTemplateAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
[]byte("template"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.MailTemplate{
|
||||||
|
Template: []byte("template"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "change, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewMailTemplateAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
[]byte("template"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
newMailTemplateChangedEvent(context.Background(), "org1", "template2"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.MailTemplate{
|
||||||
|
Template: []byte("template2"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.MailTemplate{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
Template: []byte("template2"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.ChangeMailTemplate(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_RemoveMailTemplate(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "policy not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewMailTemplateAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
[]byte("template"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewMailTemplateRemovedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
err := r.RemoveMailTemplate(tt.args.ctx, tt.args.orgID)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMailTemplateChangedEvent(ctx context.Context, orgID string, template string) *org.MailTemplateChangedEvent {
|
||||||
|
event, _ := org.NewMailTemplateChangedEvent(ctx,
|
||||||
|
&org.NewAggregate(orgID, orgID).Aggregate,
|
||||||
|
[]policy.MailTemplateChanges{
|
||||||
|
policy.ChangeTemplate([]byte(template)),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return event
|
||||||
|
}
|
||||||
@@ -9,8 +9,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *Commands) AddMailText(ctx context.Context, resourceOwner string, mailText *domain.MailText) (*domain.MailText, error) {
|
func (c *Commands) AddMailText(ctx context.Context, resourceOwner string, mailText *domain.MailText) (*domain.MailText, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-MFiig", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
if !mailText.IsValid() {
|
if !mailText.IsValid() {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-4778u", "Errors.Org.MailText.Invalid")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-4778u", "Errors.Org.MailText.Invalid")
|
||||||
}
|
}
|
||||||
addedPolicy := NewOrgMailTextWriteModel(resourceOwner, mailText.MailTextType, mailText.Language)
|
addedPolicy := NewOrgMailTextWriteModel(resourceOwner, mailText.MailTextType, mailText.Language)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
||||||
@@ -47,8 +50,11 @@ func (c *Commands) AddMailText(ctx context.Context, resourceOwner string, mailTe
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) ChangeMailText(ctx context.Context, resourceOwner string, mailText *domain.MailText) (*domain.MailText, error) {
|
func (c *Commands) ChangeMailText(ctx context.Context, resourceOwner string, mailText *domain.MailText) (*domain.MailText, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-NFus3", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
if !mailText.IsValid() {
|
if !mailText.IsValid() {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-3m9fs", "Errors.Org.MailText.Invalid")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-3m9fs", "Errors.Org.MailText.Invalid")
|
||||||
}
|
}
|
||||||
existingPolicy := NewOrgMailTextWriteModel(resourceOwner, mailText.MailTextType, mailText.Language)
|
existingPolicy := NewOrgMailTextWriteModel(resourceOwner, mailText.MailTextType, mailText.Language)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||||
@@ -88,6 +94,12 @@ func (c *Commands) ChangeMailText(ctx context.Context, resourceOwner string, mai
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) RemoveMailText(ctx context.Context, resourceOwner, mailTextType, language string) error {
|
func (c *Commands) RemoveMailText(ctx context.Context, resourceOwner, mailTextType, language string) error {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "Org-2N7fd", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
|
if mailTextType == "" || language == "" {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "Org-N8fsf", "Errors.Org.MailText.Invalid")
|
||||||
|
}
|
||||||
existingPolicy := NewOrgMailTextWriteModel(resourceOwner, mailTextType, language)
|
existingPolicy := NewOrgMailTextWriteModel(resourceOwner, mailTextType, language)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
563
internal/command/org_policy_mail_text_test.go
Normal file
563
internal/command/org_policy_mail_text_test.go
Normal file
@@ -0,0 +1,563 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/caos/zitadel/internal/repository/org"
|
||||||
|
"github.com/caos/zitadel/internal/repository/policy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_AddMailText(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
policy *domain.MailText
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.MailText
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
policy: &domain.MailText{
|
||||||
|
MailTextType: "mail-text-type",
|
||||||
|
Language: "de",
|
||||||
|
Title: "title",
|
||||||
|
PreHeader: "pre-header",
|
||||||
|
Subject: "subject",
|
||||||
|
Greeting: "greeting",
|
||||||
|
Text: "text",
|
||||||
|
ButtonText: "button-text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mail text already existing, already exists error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewMailTextAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"mail-text-type",
|
||||||
|
"de",
|
||||||
|
"title",
|
||||||
|
"pre-header",
|
||||||
|
"subject",
|
||||||
|
"greeting",
|
||||||
|
"text",
|
||||||
|
"button-text",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.MailText{
|
||||||
|
MailTextType: "mail-text-type",
|
||||||
|
Language: "de",
|
||||||
|
Title: "title",
|
||||||
|
PreHeader: "pre-header",
|
||||||
|
Subject: "subject",
|
||||||
|
Greeting: "greeting",
|
||||||
|
Text: "text",
|
||||||
|
ButtonText: "button-text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorAlreadyExists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mail text already existing, already exists error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewMailTextAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"mail-text-type",
|
||||||
|
"de",
|
||||||
|
"title",
|
||||||
|
"pre-header",
|
||||||
|
"subject",
|
||||||
|
"greeting",
|
||||||
|
"text",
|
||||||
|
"button-text",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.MailText{
|
||||||
|
MailTextType: "mail-text-type",
|
||||||
|
Language: "de",
|
||||||
|
Title: "title",
|
||||||
|
PreHeader: "pre-header",
|
||||||
|
Subject: "subject",
|
||||||
|
Greeting: "greeting",
|
||||||
|
Text: "text",
|
||||||
|
ButtonText: "button-text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorAlreadyExists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add policy,ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewMailTextAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"mail-text-type",
|
||||||
|
"de",
|
||||||
|
"title",
|
||||||
|
"pre-header",
|
||||||
|
"subject",
|
||||||
|
"greeting",
|
||||||
|
"text",
|
||||||
|
"button-text",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
uniqueConstraintsFromEventConstraint(policy.NewAddMailTextUniqueConstraint("org1", "mail-text-type", "de")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.MailText{
|
||||||
|
MailTextType: "mail-text-type",
|
||||||
|
Language: "de",
|
||||||
|
Title: "title",
|
||||||
|
PreHeader: "pre-header",
|
||||||
|
Subject: "subject",
|
||||||
|
Greeting: "greeting",
|
||||||
|
Text: "text",
|
||||||
|
ButtonText: "button-text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.MailText{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
MailTextType: "mail-text-type",
|
||||||
|
Language: "de",
|
||||||
|
Title: "title",
|
||||||
|
PreHeader: "pre-header",
|
||||||
|
Subject: "subject",
|
||||||
|
Greeting: "greeting",
|
||||||
|
Text: "text",
|
||||||
|
ButtonText: "button-text",
|
||||||
|
State: domain.PolicyStateActive,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.AddMailText(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_ChangeMailText(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
policy *domain.MailText
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.MailText
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
policy: &domain.MailText{
|
||||||
|
MailTextType: "mail-text-type",
|
||||||
|
Language: "de",
|
||||||
|
Title: "title",
|
||||||
|
PreHeader: "pre-header",
|
||||||
|
Subject: "subject",
|
||||||
|
Greeting: "greeting",
|
||||||
|
Text: "text",
|
||||||
|
ButtonText: "button-text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mailtext invalid, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
policy: &domain.MailText{},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mail template not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.MailText{
|
||||||
|
MailTextType: "mail-text-type",
|
||||||
|
Language: "de",
|
||||||
|
Title: "title",
|
||||||
|
PreHeader: "pre-header",
|
||||||
|
Subject: "subject",
|
||||||
|
Greeting: "greeting",
|
||||||
|
Text: "text",
|
||||||
|
ButtonText: "button-text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no changes, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewMailTextAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"mail-text-type",
|
||||||
|
"de",
|
||||||
|
"title",
|
||||||
|
"pre-header",
|
||||||
|
"subject",
|
||||||
|
"greeting",
|
||||||
|
"text",
|
||||||
|
"button-text",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.MailText{
|
||||||
|
MailTextType: "mail-text-type",
|
||||||
|
Language: "de",
|
||||||
|
Title: "title",
|
||||||
|
PreHeader: "pre-header",
|
||||||
|
Subject: "subject",
|
||||||
|
Greeting: "greeting",
|
||||||
|
Text: "text",
|
||||||
|
ButtonText: "button-text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "change, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewMailTextAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"mail-text-type",
|
||||||
|
"de",
|
||||||
|
"title",
|
||||||
|
"pre-header",
|
||||||
|
"subject",
|
||||||
|
"greeting",
|
||||||
|
"text",
|
||||||
|
"button-text",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
newMailTextChangedEvent(
|
||||||
|
context.Background(),
|
||||||
|
"org1",
|
||||||
|
"mail-text-type",
|
||||||
|
"de",
|
||||||
|
"title-change",
|
||||||
|
"pre-header-change",
|
||||||
|
"subject-change",
|
||||||
|
"greeting-change",
|
||||||
|
"text-change",
|
||||||
|
"button-text-change"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.MailText{
|
||||||
|
MailTextType: "mail-text-type",
|
||||||
|
Language: "de",
|
||||||
|
Title: "title-change",
|
||||||
|
PreHeader: "pre-header-change",
|
||||||
|
Subject: "subject-change",
|
||||||
|
Greeting: "greeting-change",
|
||||||
|
Text: "text-change",
|
||||||
|
ButtonText: "button-text-change",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.MailText{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
MailTextType: "mail-text-type",
|
||||||
|
Language: "de",
|
||||||
|
Title: "title-change",
|
||||||
|
PreHeader: "pre-header-change",
|
||||||
|
Subject: "subject-change",
|
||||||
|
Greeting: "greeting-change",
|
||||||
|
Text: "text-change",
|
||||||
|
ButtonText: "button-text-change",
|
||||||
|
State: domain.PolicyStateActive,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.ChangeMailText(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_RemoveMailText(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
mailTextType string
|
||||||
|
language string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "policy not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
mailTextType: "mail-text-type",
|
||||||
|
language: "de",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewMailTextAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"mail-text-type",
|
||||||
|
"de",
|
||||||
|
"title",
|
||||||
|
"pre-header",
|
||||||
|
"subject",
|
||||||
|
"greeting",
|
||||||
|
"text",
|
||||||
|
"button-text",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewMailTextRemovedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"mail-text-type",
|
||||||
|
"de"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
uniqueConstraintsFromEventConstraint(policy.NewRemoveMailTextUniqueConstraint("org1", "mail-text-type", "de")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
mailTextType: "mail-text-type",
|
||||||
|
language: "de",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
err := r.RemoveMailText(tt.args.ctx, tt.args.orgID, tt.args.mailTextType, tt.args.language)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMailTextChangedEvent(ctx context.Context, orgID, mailTextType, language, title, preHeader, subject, greeting, text, buttonText string) *org.MailTextChangedEvent {
|
||||||
|
event, _ := org.NewMailTextChangedEvent(ctx,
|
||||||
|
&org.NewAggregate(orgID, orgID).Aggregate,
|
||||||
|
mailTextType,
|
||||||
|
language,
|
||||||
|
[]policy.MailTextChanges{
|
||||||
|
policy.ChangeTitle(title),
|
||||||
|
policy.ChangePreHeader(preHeader),
|
||||||
|
policy.ChangeSubject(subject),
|
||||||
|
policy.ChangeGreeting(greeting),
|
||||||
|
policy.ChangeText(text),
|
||||||
|
policy.ChangeButtonText(buttonText),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return event
|
||||||
|
}
|
||||||
@@ -10,6 +10,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *Commands) AddOrgIAMPolicy(ctx context.Context, resourceOwner string, policy *domain.OrgIAMPolicy) (*domain.OrgIAMPolicy, error) {
|
func (c *Commands) AddOrgIAMPolicy(ctx context.Context, resourceOwner string, policy *domain.OrgIAMPolicy) (*domain.OrgIAMPolicy, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-4Jfsf", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
addedPolicy := NewORGOrgIAMPolicyWriteModel(resourceOwner)
|
addedPolicy := NewORGOrgIAMPolicyWriteModel(resourceOwner)
|
||||||
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.PolicyOrgIAMWriteModel.WriteModel)
|
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.PolicyOrgIAMWriteModel.WriteModel)
|
||||||
event, err := c.addOrgIAMPolicy(ctx, orgAgg, addedPolicy, policy)
|
event, err := c.addOrgIAMPolicy(ctx, orgAgg, addedPolicy, policy)
|
||||||
@@ -39,6 +42,9 @@ func (c *Commands) addOrgIAMPolicy(ctx context.Context, orgAgg *eventstore.Aggre
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) ChangeOrgIAMPolicy(ctx context.Context, resourceOwner string, policy *domain.OrgIAMPolicy) (*domain.OrgIAMPolicy, error) {
|
func (c *Commands) ChangeOrgIAMPolicy(ctx context.Context, resourceOwner string, policy *domain.OrgIAMPolicy) (*domain.OrgIAMPolicy, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-5H8fs", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
existingPolicy, err := c.orgIAMPolicyWriteModelByID(ctx, resourceOwner)
|
existingPolicy, err := c.orgIAMPolicyWriteModelByID(ctx, resourceOwner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -65,12 +71,15 @@ func (c *Commands) ChangeOrgIAMPolicy(ctx context.Context, resourceOwner string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) RemoveOrgIAMPolicy(ctx context.Context, orgID string) error {
|
func (c *Commands) RemoveOrgIAMPolicy(ctx context.Context, orgID string) error {
|
||||||
|
if orgID == "" {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "Org-3H8fs", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
existingPolicy, err := c.orgIAMPolicyWriteModelByID(ctx, orgID)
|
existingPolicy, err := c.orgIAMPolicyWriteModelByID(ctx, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||||
return caos_errs.ThrowNotFound(nil, "ORG-Dvsh3", "Errors.Org.OrgIAMPolicy.NotFound")
|
return caos_errs.ThrowNotFound(nil, "ORG-Dvsh3", "Errors.Org.OrgIAM.NotFound")
|
||||||
}
|
}
|
||||||
|
|
||||||
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.PolicyOrgIAMWriteModel.WriteModel)
|
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.PolicyOrgIAMWriteModel.WriteModel)
|
||||||
|
|||||||
381
internal/command/org_policy_org_iam_test.go
Normal file
381
internal/command/org_policy_org_iam_test.go
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/caos/zitadel/internal/repository/org"
|
||||||
|
"github.com/caos/zitadel/internal/repository/policy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_AddOrgIAMPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
policy *domain.OrgIAMPolicy
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.OrgIAMPolicy
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
policy: &domain.OrgIAMPolicy{
|
||||||
|
UserLoginMustBeDomain: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mail template already existing, already exists error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewOrgIAMPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.OrgIAMPolicy{
|
||||||
|
UserLoginMustBeDomain: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorAlreadyExists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add policy,ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewOrgIAMPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.OrgIAMPolicy{
|
||||||
|
UserLoginMustBeDomain: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.OrgIAMPolicy{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
UserLoginMustBeDomain: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.AddOrgIAMPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_ChangeOrgIAMPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
policy *domain.OrgIAMPolicy
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.OrgIAMPolicy
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
policy: &domain.OrgIAMPolicy{
|
||||||
|
UserLoginMustBeDomain: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "policy not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.OrgIAMPolicy{
|
||||||
|
UserLoginMustBeDomain: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no changes, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewOrgIAMPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.OrgIAMPolicy{
|
||||||
|
UserLoginMustBeDomain: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "change, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewOrgIAMPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
newOrgIAMPolicyChangedEvent(context.Background(), "org1", false),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.OrgIAMPolicy{
|
||||||
|
UserLoginMustBeDomain: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.OrgIAMPolicy{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
UserLoginMustBeDomain: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.ChangeOrgIAMPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_RemoveOrgIAMPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "policy not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewOrgIAMPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewOrgIAMPolicyRemovedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
err := r.RemoveOrgIAMPolicy(tt.args.ctx, tt.args.orgID)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOrgIAMPolicyChangedEvent(ctx context.Context, orgID string, userLoginMustBeDomain bool) *org.OrgIAMPolicyChangedEvent {
|
||||||
|
event, _ := org.NewOrgIAMPolicyChangedEvent(ctx,
|
||||||
|
&org.NewAggregate(orgID, orgID).Aggregate,
|
||||||
|
[]policy.OrgIAMPolicyChanges{
|
||||||
|
policy.ChangeUserLoginMustBeDomain(userLoginMustBeDomain),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return event
|
||||||
|
}
|
||||||
@@ -9,6 +9,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *Commands) AddPasswordAgePolicy(ctx context.Context, resourceOwner string, policy *domain.PasswordAgePolicy) (*domain.PasswordAgePolicy, error) {
|
func (c *Commands) AddPasswordAgePolicy(ctx context.Context, resourceOwner string, policy *domain.PasswordAgePolicy) (*domain.PasswordAgePolicy, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-M9fsd", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
addedPolicy := NewOrgPasswordAgePolicyWriteModel(resourceOwner)
|
addedPolicy := NewOrgPasswordAgePolicyWriteModel(resourceOwner)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -31,6 +34,9 @@ func (c *Commands) AddPasswordAgePolicy(ctx context.Context, resourceOwner strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) ChangePasswordAgePolicy(ctx context.Context, resourceOwner string, policy *domain.PasswordAgePolicy) (*domain.PasswordAgePolicy, error) {
|
func (c *Commands) ChangePasswordAgePolicy(ctx context.Context, resourceOwner string, policy *domain.PasswordAgePolicy) (*domain.PasswordAgePolicy, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-57tGs", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
existingPolicy := NewOrgPasswordAgePolicyWriteModel(resourceOwner)
|
existingPolicy := NewOrgPasswordAgePolicyWriteModel(resourceOwner)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -58,6 +64,9 @@ func (c *Commands) ChangePasswordAgePolicy(ctx context.Context, resourceOwner st
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) RemovePasswordAgePolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
|
func (c *Commands) RemovePasswordAgePolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
|
||||||
|
if orgID == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-2N8fs", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
existingPolicy := NewOrgPasswordAgePolicyWriteModel(orgID)
|
existingPolicy := NewOrgPasswordAgePolicyWriteModel(orgID)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
399
internal/command/org_policy_password_age_test.go
Normal file
399
internal/command/org_policy_password_age_test.go
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/caos/zitadel/internal/repository/org"
|
||||||
|
"github.com/caos/zitadel/internal/repository/policy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_AddPasswordAgePolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
policy *domain.PasswordAgePolicy
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.PasswordAgePolicy
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
policy: &domain.PasswordAgePolicy{
|
||||||
|
MaxAgeDays: 365,
|
||||||
|
ExpireWarnDays: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mail template already existing, already exists error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordAgePolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
365,
|
||||||
|
10,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.PasswordAgePolicy{
|
||||||
|
MaxAgeDays: 365,
|
||||||
|
ExpireWarnDays: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorAlreadyExists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add policy,ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordAgePolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
10,
|
||||||
|
365,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.PasswordAgePolicy{
|
||||||
|
MaxAgeDays: 365,
|
||||||
|
ExpireWarnDays: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.PasswordAgePolicy{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
MaxAgeDays: 365,
|
||||||
|
ExpireWarnDays: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.AddPasswordAgePolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_ChangePasswordAgePolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
policy *domain.PasswordAgePolicy
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.PasswordAgePolicy
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
policy: &domain.PasswordAgePolicy{
|
||||||
|
MaxAgeDays: 365,
|
||||||
|
ExpireWarnDays: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "policy not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.PasswordAgePolicy{
|
||||||
|
MaxAgeDays: 365,
|
||||||
|
ExpireWarnDays: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no changes, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordAgePolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
10,
|
||||||
|
365,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.PasswordAgePolicy{
|
||||||
|
MaxAgeDays: 365,
|
||||||
|
ExpireWarnDays: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "change, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordAgePolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
10,
|
||||||
|
365,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
newPasswordAgePolicyChangedEvent(context.Background(), "org1", 150, 5),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.PasswordAgePolicy{
|
||||||
|
MaxAgeDays: 150,
|
||||||
|
ExpireWarnDays: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.PasswordAgePolicy{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
MaxAgeDays: 150,
|
||||||
|
ExpireWarnDays: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.ChangePasswordAgePolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_RemovePasswordAgePolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "policy not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordAgePolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
10,
|
||||||
|
365,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordAgePolicyRemovedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.RemovePasswordAgePolicy(tt.args.ctx, tt.args.orgID)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPasswordAgePolicyChangedEvent(ctx context.Context, orgID string, maxAgeDays, expireWarnDays uint64) *org.PasswordAgePolicyChangedEvent {
|
||||||
|
event, _ := org.NewPasswordAgePolicyChangedEvent(ctx,
|
||||||
|
&org.NewAggregate(orgID, orgID).Aggregate,
|
||||||
|
[]policy.PasswordAgePolicyChanges{
|
||||||
|
policy.ChangeMaxAgeDays(maxAgeDays),
|
||||||
|
policy.ChangeExpireWarnDays(expireWarnDays),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return event
|
||||||
|
}
|
||||||
@@ -21,6 +21,9 @@ func (c *Commands) getOrgPasswordComplexityPolicy(ctx context.Context, orgID str
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) AddPasswordComplexityPolicy(ctx context.Context, resourceOwner string, policy *domain.PasswordComplexityPolicy) (*domain.PasswordComplexityPolicy, error) {
|
func (c *Commands) AddPasswordComplexityPolicy(ctx context.Context, resourceOwner string, policy *domain.PasswordComplexityPolicy) (*domain.PasswordComplexityPolicy, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-7ufEs", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
if err := policy.IsValid(); err != nil {
|
if err := policy.IsValid(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -55,6 +58,9 @@ func (c *Commands) AddPasswordComplexityPolicy(ctx context.Context, resourceOwne
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) ChangePasswordComplexityPolicy(ctx context.Context, resourceOwner string, policy *domain.PasswordComplexityPolicy) (*domain.PasswordComplexityPolicy, error) {
|
func (c *Commands) ChangePasswordComplexityPolicy(ctx context.Context, resourceOwner string, policy *domain.PasswordComplexityPolicy) (*domain.PasswordComplexityPolicy, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-3J8fs", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
if err := policy.IsValid(); err != nil {
|
if err := policy.IsValid(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -86,6 +92,9 @@ func (c *Commands) ChangePasswordComplexityPolicy(ctx context.Context, resourceO
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) RemovePasswordComplexityPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
|
func (c *Commands) RemovePasswordComplexityPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
|
||||||
|
if orgID == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-J8fsf", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
existingPolicy := NewOrgPasswordComplexityPolicyWriteModel(orgID)
|
existingPolicy := NewOrgPasswordComplexityPolicyWriteModel(orgID)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
429
internal/command/org_policy_password_complexity_test.go
Normal file
429
internal/command/org_policy_password_complexity_test.go
Normal file
@@ -0,0 +1,429 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/caos/zitadel/internal/repository/org"
|
||||||
|
"github.com/caos/zitadel/internal/repository/policy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_AddPasswordComplexityPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
policy *domain.PasswordComplexityPolicy
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.PasswordComplexityPolicy
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
policy: &domain.PasswordComplexityPolicy{
|
||||||
|
MinLength: 0,
|
||||||
|
HasUppercase: true,
|
||||||
|
HasLowercase: true,
|
||||||
|
HasNumber: true,
|
||||||
|
HasSymbol: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "policy already existing, already exists error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
8,
|
||||||
|
true, true, true, true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.PasswordComplexityPolicy{
|
||||||
|
MinLength: 8,
|
||||||
|
HasUppercase: true,
|
||||||
|
HasLowercase: true,
|
||||||
|
HasNumber: true,
|
||||||
|
HasSymbol: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorAlreadyExists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add policy,ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
8,
|
||||||
|
true, true, true, true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.PasswordComplexityPolicy{
|
||||||
|
MinLength: 8,
|
||||||
|
HasUppercase: true,
|
||||||
|
HasLowercase: true,
|
||||||
|
HasNumber: true,
|
||||||
|
HasSymbol: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.PasswordComplexityPolicy{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
MinLength: 8,
|
||||||
|
HasUppercase: true,
|
||||||
|
HasLowercase: true,
|
||||||
|
HasNumber: true,
|
||||||
|
HasSymbol: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.AddPasswordComplexityPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_ChangePasswordComplexityPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
policy *domain.PasswordComplexityPolicy
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.PasswordComplexityPolicy
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
policy: &domain.PasswordComplexityPolicy{
|
||||||
|
MinLength: 0,
|
||||||
|
HasUppercase: true,
|
||||||
|
HasLowercase: true,
|
||||||
|
HasNumber: true,
|
||||||
|
HasSymbol: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "policy not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.PasswordComplexityPolicy{
|
||||||
|
MinLength: 8,
|
||||||
|
HasUppercase: true,
|
||||||
|
HasLowercase: true,
|
||||||
|
HasNumber: true,
|
||||||
|
HasSymbol: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no changes, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
8,
|
||||||
|
true, true, true, true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.PasswordComplexityPolicy{
|
||||||
|
MinLength: 8,
|
||||||
|
HasUppercase: true,
|
||||||
|
HasLowercase: true,
|
||||||
|
HasNumber: true,
|
||||||
|
HasSymbol: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "change, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
8,
|
||||||
|
true, true, true, true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
newPasswordComplexityPolicyChangedEvent(context.Background(), "org1", 10, false, false, false, false),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.PasswordComplexityPolicy{
|
||||||
|
MinLength: 10,
|
||||||
|
HasUppercase: false,
|
||||||
|
HasLowercase: false,
|
||||||
|
HasNumber: false,
|
||||||
|
HasSymbol: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.PasswordComplexityPolicy{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
MinLength: 10,
|
||||||
|
HasUppercase: false,
|
||||||
|
HasLowercase: false,
|
||||||
|
HasNumber: false,
|
||||||
|
HasSymbol: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.ChangePasswordComplexityPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_RemovePasswordComplexityPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "policy not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
8,
|
||||||
|
true, true, true, true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordComplexityPolicyRemovedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.RemovePasswordComplexityPolicy(tt.args.ctx, tt.args.orgID)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPasswordComplexityPolicyChangedEvent(ctx context.Context, orgID string, minLength uint64, hasUpper, hasLower, hasNumber, hasSymbol bool) *org.PasswordComplexityPolicyChangedEvent {
|
||||||
|
event, _ := org.NewPasswordComplexityPolicyChangedEvent(ctx,
|
||||||
|
&org.NewAggregate(orgID, orgID).Aggregate,
|
||||||
|
[]policy.PasswordComplexityPolicyChanges{
|
||||||
|
policy.ChangeMinLength(minLength),
|
||||||
|
policy.ChangeHasUppercase(hasUpper),
|
||||||
|
policy.ChangeHasLowercase(hasLower),
|
||||||
|
policy.ChangeHasSymbol(hasNumber),
|
||||||
|
policy.ChangeHasNumber(hasSymbol),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return event
|
||||||
|
}
|
||||||
@@ -8,6 +8,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *Commands) AddPasswordLockoutPolicy(ctx context.Context, resourceOwner string, policy *domain.PasswordLockoutPolicy) (*domain.PasswordLockoutPolicy, error) {
|
func (c *Commands) AddPasswordLockoutPolicy(ctx context.Context, resourceOwner string, policy *domain.PasswordLockoutPolicy) (*domain.PasswordLockoutPolicy, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-8fJif", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
addedPolicy := NewOrgPasswordLockoutPolicyWriteModel(resourceOwner)
|
addedPolicy := NewOrgPasswordLockoutPolicyWriteModel(resourceOwner)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -30,6 +33,9 @@ func (c *Commands) AddPasswordLockoutPolicy(ctx context.Context, resourceOwner s
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) ChangePasswordLockoutPolicy(ctx context.Context, resourceOwner string, policy *domain.PasswordLockoutPolicy) (*domain.PasswordLockoutPolicy, error) {
|
func (c *Commands) ChangePasswordLockoutPolicy(ctx context.Context, resourceOwner string, policy *domain.PasswordLockoutPolicy) (*domain.PasswordLockoutPolicy, error) {
|
||||||
|
if resourceOwner == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-3J9fs", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
existingPolicy := NewOrgPasswordLockoutPolicyWriteModel(resourceOwner)
|
existingPolicy := NewOrgPasswordLockoutPolicyWriteModel(resourceOwner)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -57,6 +63,9 @@ func (c *Commands) ChangePasswordLockoutPolicy(ctx context.Context, resourceOwne
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) RemovePasswordLockoutPolicy(ctx context.Context, orgID string) error {
|
func (c *Commands) RemovePasswordLockoutPolicy(ctx context.Context, orgID string) error {
|
||||||
|
if orgID == "" {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "Org-4J9fs", "Errors.ResourceOwnerMissing")
|
||||||
|
}
|
||||||
existingPolicy := NewOrgPasswordLockoutPolicyWriteModel(orgID)
|
existingPolicy := NewOrgPasswordLockoutPolicyWriteModel(orgID)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
396
internal/command/org_policy_password_lockout_test.go
Normal file
396
internal/command/org_policy_password_lockout_test.go
Normal file
@@ -0,0 +1,396 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/caos/zitadel/internal/repository/org"
|
||||||
|
"github.com/caos/zitadel/internal/repository/policy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
policy *domain.PasswordLockoutPolicy
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.PasswordLockoutPolicy
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
policy: &domain.PasswordLockoutPolicy{
|
||||||
|
MaxAttempts: 10,
|
||||||
|
ShowLockOutFailures: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mail template already existing, already exists error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
10,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.PasswordLockoutPolicy{
|
||||||
|
MaxAttempts: 10,
|
||||||
|
ShowLockOutFailures: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorAlreadyExists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add policy,ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
10,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.PasswordLockoutPolicy{
|
||||||
|
MaxAttempts: 10,
|
||||||
|
ShowLockOutFailures: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.PasswordLockoutPolicy{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
MaxAttempts: 10,
|
||||||
|
ShowLockOutFailures: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.AddPasswordLockoutPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
policy *domain.PasswordLockoutPolicy
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.PasswordLockoutPolicy
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
policy: &domain.PasswordLockoutPolicy{
|
||||||
|
MaxAttempts: 10,
|
||||||
|
ShowLockOutFailures: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "policy not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.PasswordLockoutPolicy{
|
||||||
|
MaxAttempts: 10,
|
||||||
|
ShowLockOutFailures: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no changes, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
10,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.PasswordLockoutPolicy{
|
||||||
|
MaxAttempts: 10,
|
||||||
|
ShowLockOutFailures: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "change, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
10,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
newPasswordLockoutPolicyChangedEvent(context.Background(), "org1", 5, false),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.PasswordLockoutPolicy{
|
||||||
|
MaxAttempts: 5,
|
||||||
|
ShowLockOutFailures: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.PasswordLockoutPolicy{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
MaxAttempts: 5,
|
||||||
|
ShowLockOutFailures: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.ChangePasswordLockoutPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_RemovePasswordLockoutPolicy(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "policy not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
10,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordLockoutPolicyRemovedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
err := r.RemovePasswordLockoutPolicy(tt.args.ctx, tt.args.orgID)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPasswordLockoutPolicyChangedEvent(ctx context.Context, orgID string, maxAttempts uint64, showLockoutFailure bool) *org.PasswordLockoutPolicyChangedEvent {
|
||||||
|
event, _ := org.NewPasswordLockoutPolicyChangedEvent(ctx,
|
||||||
|
&org.NewAggregate(orgID, orgID).Aggregate,
|
||||||
|
[]policy.PasswordLockoutPolicyChanges{
|
||||||
|
policy.ChangeMaxAttempts(maxAttempts),
|
||||||
|
policy.ChangeShowLockOutFailures(showLockoutFailure),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return event
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ func (s *Step7) execute(ctx context.Context, commandSide *Commands) error {
|
|||||||
|
|
||||||
func (c *Commands) SetupStep7(ctx context.Context, step *Step7) error {
|
func (c *Commands) SetupStep7(ctx context.Context, step *Step7) error {
|
||||||
fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) {
|
fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) {
|
||||||
secondFactorModel := NewIAMSecondFactorWriteModel()
|
secondFactorModel := NewIAMSecondFactorWriteModel(domain.SecondFactorTypeOTP)
|
||||||
iamAgg := IAMAggregateFromWriteModel(&secondFactorModel.SecondFactorWriteModel.WriteModel)
|
iamAgg := IAMAggregateFromWriteModel(&secondFactorModel.SecondFactorWriteModel.WriteModel)
|
||||||
if !step.OTP {
|
if !step.OTP {
|
||||||
return []eventstore.EventPusher{}, nil
|
return []eventstore.EventPusher{}, nil
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ func (s *Step8) execute(ctx context.Context, commandSide *Commands) error {
|
|||||||
|
|
||||||
func (c *Commands) SetupStep8(ctx context.Context, step *Step8) error {
|
func (c *Commands) SetupStep8(ctx context.Context, step *Step8) error {
|
||||||
fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) {
|
fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) {
|
||||||
secondFactorModel := NewIAMSecondFactorWriteModel()
|
secondFactorModel := NewIAMSecondFactorWriteModel(domain.SecondFactorTypeU2F)
|
||||||
iamAgg := IAMAggregateFromWriteModel(&secondFactorModel.SecondFactorWriteModel.WriteModel)
|
iamAgg := IAMAggregateFromWriteModel(&secondFactorModel.SecondFactorWriteModel.WriteModel)
|
||||||
if !step.U2F {
|
if !step.U2F {
|
||||||
return []eventstore.EventPusher{}, nil
|
return []eventstore.EventPusher{}, nil
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ func (s *Step9) execute(ctx context.Context, commandSide *Commands) error {
|
|||||||
|
|
||||||
func (c *Commands) SetupStep9(ctx context.Context, step *Step9) error {
|
func (c *Commands) SetupStep9(ctx context.Context, step *Step9) error {
|
||||||
fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) {
|
fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) {
|
||||||
multiFactorModel := NewIAMMultiFactorWriteModel()
|
multiFactorModel := NewIAMMultiFactorWriteModel(domain.MultiFactorTypeU2FWithPIN)
|
||||||
iamAgg := IAMAggregateFromWriteModel(&multiFactorModel.MultiFactoryWriteModel.WriteModel)
|
iamAgg := IAMAggregateFromWriteModel(&multiFactorModel.MultiFactoryWriteModel.WriteModel)
|
||||||
if !step.Passwordless {
|
if !step.Passwordless {
|
||||||
return []eventstore.EventPusher{}, nil
|
return []eventstore.EventPusher{}, nil
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
|
|
||||||
func (c *Commands) ChangeUsername(ctx context.Context, orgID, userID, userName string) (*domain.ObjectDetails, error) {
|
func (c *Commands) ChangeUsername(ctx context.Context, orgID, userID, userName string) (*domain.ObjectDetails, error) {
|
||||||
if orgID == "" || userID == "" || userName == "" {
|
if orgID == "" || userID == "" || userName == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2N9fs", "Errors.IDMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-2N9fs", "Errors.IDMissing")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingUser, err := c.userWriteModelByID(ctx, userID, orgID)
|
existingUser, err := c.userWriteModelByID(ctx, userID, orgID)
|
||||||
@@ -35,7 +35,7 @@ func (c *Commands) ChangeUsername(ctx context.Context, orgID, userID, userName s
|
|||||||
|
|
||||||
orgIAMPolicy, err := c.getOrgIAMPolicy(ctx, orgID)
|
orgIAMPolicy, err := c.getOrgIAMPolicy(ctx, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-38fnu", "Errors.Org.OrgIAM.NotExisting")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := CheckOrgIAMPolicyForUserName(userName, orgIAMPolicy); err != nil {
|
if err := CheckOrgIAMPolicyForUserName(userName, orgIAMPolicy); err != nil {
|
||||||
@@ -57,7 +57,7 @@ func (c *Commands) ChangeUsername(ctx context.Context, orgID, userID, userName s
|
|||||||
|
|
||||||
func (c *Commands) DeactivateUser(ctx context.Context, userID, resourceOwner string) (*domain.ObjectDetails, error) {
|
func (c *Commands) DeactivateUser(ctx context.Context, userID, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-m0gDf", "Errors.User.UserIDMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-m0gDf", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingUser, err := c.userWriteModelByID(ctx, userID, resourceOwner)
|
existingUser, err := c.userWriteModelByID(ctx, userID, resourceOwner)
|
||||||
@@ -85,7 +85,7 @@ func (c *Commands) DeactivateUser(ctx context.Context, userID, resourceOwner str
|
|||||||
|
|
||||||
func (c *Commands) ReactivateUser(ctx context.Context, userID, resourceOwner string) (*domain.ObjectDetails, error) {
|
func (c *Commands) ReactivateUser(ctx context.Context, userID, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M9ds", "Errors.User.UserIDMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M9ds", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingUser, err := c.userWriteModelByID(ctx, userID, resourceOwner)
|
existingUser, err := c.userWriteModelByID(ctx, userID, resourceOwner)
|
||||||
@@ -113,7 +113,7 @@ func (c *Commands) ReactivateUser(ctx context.Context, userID, resourceOwner str
|
|||||||
|
|
||||||
func (c *Commands) LockUser(ctx context.Context, userID, resourceOwner string) (*domain.ObjectDetails, error) {
|
func (c *Commands) LockUser(ctx context.Context, userID, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M0sd", "Errors.User.UserIDMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-2M0sd", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingUser, err := c.userWriteModelByID(ctx, userID, resourceOwner)
|
existingUser, err := c.userWriteModelByID(ctx, userID, resourceOwner)
|
||||||
@@ -141,7 +141,7 @@ func (c *Commands) LockUser(ctx context.Context, userID, resourceOwner string) (
|
|||||||
|
|
||||||
func (c *Commands) UnlockUser(ctx context.Context, userID, resourceOwner string) (*domain.ObjectDetails, error) {
|
func (c *Commands) UnlockUser(ctx context.Context, userID, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-M0dse", "Errors.User.UserIDMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-M0dse", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingUser, err := c.userWriteModelByID(ctx, userID, resourceOwner)
|
existingUser, err := c.userWriteModelByID(ctx, userID, resourceOwner)
|
||||||
@@ -169,7 +169,7 @@ func (c *Commands) UnlockUser(ctx context.Context, userID, resourceOwner string)
|
|||||||
|
|
||||||
func (c *Commands) RemoveUser(ctx context.Context, userID, resourceOwner string, cascadingGrantIDs ...string) (*domain.ObjectDetails, error) {
|
func (c *Commands) RemoveUser(ctx context.Context, userID, resourceOwner string, cascadingGrantIDs ...string) (*domain.ObjectDetails, error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M0ds", "Errors.User.UserIDMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-2M0ds", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingUser, err := c.userWriteModelByID(ctx, userID, resourceOwner)
|
existingUser, err := c.userWriteModelByID(ctx, userID, resourceOwner)
|
||||||
@@ -182,7 +182,7 @@ func (c *Commands) RemoveUser(ctx context.Context, userID, resourceOwner string,
|
|||||||
|
|
||||||
orgIAMPolicy, err := c.getOrgIAMPolicy(ctx, existingUser.ResourceOwner)
|
orgIAMPolicy, err := c.getOrgIAMPolicy(ctx, existingUser.ResourceOwner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-3M9fs", "Errors.Org.OrgIAM.NotExisting")
|
||||||
}
|
}
|
||||||
var events []eventstore.EventPusher
|
var events []eventstore.EventPusher
|
||||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||||
@@ -210,7 +210,7 @@ func (c *Commands) RemoveUser(ctx context.Context, userID, resourceOwner string,
|
|||||||
|
|
||||||
func (c *Commands) AddUserToken(ctx context.Context, orgID, agentID, clientID, userID string, audience, scopes []string, lifetime time.Duration) (*domain.Token, error) {
|
func (c *Commands) AddUserToken(ctx context.Context, orgID, agentID, clientID, userID string, audience, scopes []string, lifetime time.Duration) (*domain.Token, error) {
|
||||||
if orgID == "" || userID == "" {
|
if orgID == "" || userID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-55n8M", "Errors.IDMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-55n8M", "Errors.IDMissing")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingUser, err := c.userWriteModelByID(ctx, userID, orgID)
|
existingUser, err := c.userWriteModelByID(ctx, userID, orgID)
|
||||||
@@ -286,6 +286,9 @@ func (c *Commands) userDomainClaimed(ctx context.Context, userID string) (events
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) UserDomainClaimedSent(ctx context.Context, orgID, userID string) (err error) {
|
func (c *Commands) UserDomainClaimedSent(ctx context.Context, orgID, userID string) (err error) {
|
||||||
|
if userID == "" {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-5m0fs", "Errors.IDMissing")
|
||||||
|
}
|
||||||
existingUser, err := c.userWriteModelByID(ctx, userID, orgID)
|
existingUser, err := c.userWriteModelByID(ctx, userID, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func writeModelToHuman(wm *HumanWriteModel) *domain.Human {
|
func writeModelToHuman(wm *HumanWriteModel) *domain.Human {
|
||||||
return &domain.Human{
|
human := &domain.Human{
|
||||||
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
|
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
|
||||||
Username: wm.UserName,
|
Username: wm.UserName,
|
||||||
State: wm.UserState,
|
State: wm.UserState,
|
||||||
@@ -22,14 +22,22 @@ func writeModelToHuman(wm *HumanWriteModel) *domain.Human {
|
|||||||
EmailAddress: wm.Email,
|
EmailAddress: wm.Email,
|
||||||
IsEmailVerified: wm.IsEmailVerified,
|
IsEmailVerified: wm.IsEmailVerified,
|
||||||
},
|
},
|
||||||
Address: &domain.Address{
|
}
|
||||||
|
if wm.Phone != "" {
|
||||||
|
human.Phone = &domain.Phone{
|
||||||
|
PhoneNumber: wm.Phone,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if wm.Country != "" || wm.Locality != "" || wm.PostalCode != "" || wm.Region != "" || wm.StreetAddress != "" {
|
||||||
|
human.Address = &domain.Address{
|
||||||
Country: wm.Country,
|
Country: wm.Country,
|
||||||
Locality: wm.Locality,
|
Locality: wm.Locality,
|
||||||
PostalCode: wm.PostalCode,
|
PostalCode: wm.PostalCode,
|
||||||
Region: wm.Region,
|
Region: wm.Region,
|
||||||
StreetAddress: wm.StreetAddress,
|
StreetAddress: wm.StreetAddress,
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
return human
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeModelToProfile(wm *HumanProfileWriteModel) *domain.Profile {
|
func writeModelToProfile(wm *HumanProfileWriteModel) *domain.Profile {
|
||||||
@@ -73,8 +81,10 @@ func writeModelToAddress(wm *HumanAddressWriteModel) *domain.Address {
|
|||||||
func writeModelToMachine(wm *MachineWriteModel) *domain.Machine {
|
func writeModelToMachine(wm *MachineWriteModel) *domain.Machine {
|
||||||
return &domain.Machine{
|
return &domain.Machine{
|
||||||
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
|
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
|
||||||
|
Username: wm.UserName,
|
||||||
Name: wm.Name,
|
Name: wm.Name,
|
||||||
Description: wm.Description,
|
Description: wm.Description,
|
||||||
|
State: wm.UserState,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func (c *Commands) AddHuman(ctx context.Context, orgID string, human *domain.Hum
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) addHuman(ctx context.Context, orgID string, human *domain.Human) ([]eventstore.EventPusher, *HumanWriteModel, error) {
|
func (c *Commands) addHuman(ctx context.Context, orgID string, human *domain.Human) ([]eventstore.EventPusher, *HumanWriteModel, error) {
|
||||||
if !human.IsValid() {
|
if orgID == "" || !human.IsValid() {
|
||||||
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M90d", "Errors.User.Invalid")
|
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M90d", "Errors.User.Invalid")
|
||||||
}
|
}
|
||||||
return c.createHuman(ctx, orgID, human, nil, false)
|
return c.createHuman(ctx, orgID, human, nil, false)
|
||||||
@@ -82,8 +82,8 @@ func (c *Commands) RegisterHuman(ctx context.Context, orgID string, human *domai
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) registerHuman(ctx context.Context, orgID string, human *domain.Human, externalIDP *domain.ExternalIDP) ([]eventstore.EventPusher, *HumanWriteModel, error) {
|
func (c *Commands) registerHuman(ctx context.Context, orgID string, human *domain.Human, externalIDP *domain.ExternalIDP) ([]eventstore.EventPusher, *HumanWriteModel, error) {
|
||||||
if !human.IsValid() || externalIDP == nil && (human.Password == nil || human.SecretString == "") {
|
if orgID == "" || !human.IsValid() || externalIDP == nil && (human.Password == nil || human.SecretString == "") {
|
||||||
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-9dk45", "Errors.User.Invalid")
|
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-9dk45", "Errors.User.Invalid")
|
||||||
}
|
}
|
||||||
return c.createHuman(ctx, orgID, human, externalIDP, true)
|
return c.createHuman(ctx, orgID, human, externalIDP, true)
|
||||||
}
|
}
|
||||||
@@ -96,17 +96,17 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.
|
|||||||
human.AggregateID = userID
|
human.AggregateID = userID
|
||||||
orgIAMPolicy, err := c.getOrgIAMPolicy(ctx, orgID)
|
orgIAMPolicy, err := c.getOrgIAMPolicy(ctx, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-33M9f", "Errors.Org.OrgIAMPolicy.NotFound")
|
||||||
}
|
|
||||||
pwPolicy, err := c.getOrgPasswordComplexityPolicy(ctx, orgID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
}
|
||||||
if err := human.CheckOrgIAMPolicy(orgIAMPolicy); err != nil {
|
if err := human.CheckOrgIAMPolicy(orgIAMPolicy); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
pwPolicy, err := c.getOrgPasswordComplexityPolicy(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.Org.PasswordComplexity.NotFound")
|
||||||
|
}
|
||||||
human.SetNamesAsDisplayname()
|
human.SetNamesAsDisplayname()
|
||||||
if err := human.HashPasswordIfExisting(pwPolicy, c.userPasswordAlg, true); err != nil {
|
if err := human.HashPasswordIfExisting(pwPolicy, c.userPasswordAlg, !selfregister); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
addedHuman := NewHumanWriteModel(human.AggregateID, orgID)
|
addedHuman := NewHumanWriteModel(human.AggregateID, orgID)
|
||||||
@@ -155,7 +155,7 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.
|
|||||||
|
|
||||||
func (c *Commands) HumanSkipMFAInit(ctx context.Context, userID, resourceowner string) (err error) {
|
func (c *Commands) HumanSkipMFAInit(ctx context.Context, userID, resourceowner string) (err error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2xpX9", "Errors.User.UserIDMissing")
|
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-2xpX9", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingHuman, err := c.getHumanWriteModelByID(ctx, userID, resourceowner)
|
existingHuman, err := c.getHumanWriteModelByID(ctx, userID, resourceowner)
|
||||||
@@ -236,10 +236,13 @@ func createRegisterHumanEvent(ctx context.Context, aggregate *eventstore.Aggrega
|
|||||||
|
|
||||||
func (c *Commands) HumansSignOut(ctx context.Context, agentID string, userIDs []string) error {
|
func (c *Commands) HumansSignOut(ctx context.Context, agentID string, userIDs []string) error {
|
||||||
if agentID == "" {
|
if agentID == "" {
|
||||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M0ds", "Errors.User.UserIDMissing")
|
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-2M0ds", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
events := make([]eventstore.EventPusher, len(userIDs))
|
if len(userIDs) == 0 {
|
||||||
for i, userID := range userIDs {
|
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-M0od3", "Errors.User.UserIDMissing")
|
||||||
|
}
|
||||||
|
events := make([]eventstore.EventPusher, 0)
|
||||||
|
for _, userID := range userIDs {
|
||||||
existingUser, err := c.getHumanWriteModelByID(ctx, userID, "")
|
existingUser, err := c.getHumanWriteModelByID(ctx, userID, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -247,12 +250,14 @@ func (c *Commands) HumansSignOut(ctx context.Context, agentID string, userIDs []
|
|||||||
if !isUserStateExists(existingUser.UserState) {
|
if !isUserStateExists(existingUser.UserState) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
events[i] = user.NewHumanSignedOutEvent(
|
events = append(events, user.NewHumanSignedOutEvent(
|
||||||
ctx,
|
ctx,
|
||||||
UserAggregateFromWriteModel(&existingUser.WriteModel),
|
UserAggregateFromWriteModel(&existingUser.WriteModel),
|
||||||
agentID)
|
agentID))
|
||||||
|
}
|
||||||
|
if len(events) == 0 {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := c.eventstore.PushEvents(ctx, events...)
|
_, err := c.eventstore.PushEvents(ctx, events...)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,10 +13,13 @@ func (c *Commands) ChangeHumanAddress(ctx context.Context, address *domain.Addre
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if existingAddress.State == domain.AddressStateUnspecified || existingAddress.State == domain.AddressStateRemoved {
|
if existingAddress.State == domain.AddressStateUnspecified || existingAddress.State == domain.AddressStateRemoved {
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-0pLdo", "Errors.User.Address.NotFound")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-0pLdo", "Errors.User.Address.NotFound")
|
||||||
}
|
}
|
||||||
userAgg := UserAggregateFromWriteModel(&existingAddress.WriteModel)
|
userAgg := UserAggregateFromWriteModel(&existingAddress.WriteModel)
|
||||||
changedEvent, hasChanged := existingAddress.NewChangedEvent(ctx, userAgg, address.Country, address.Locality, address.PostalCode, address.Region, address.StreetAddress)
|
changedEvent, hasChanged, err := existingAddress.NewChangedEvent(ctx, userAgg, address.Country, address.Locality, address.PostalCode, address.Region, address.StreetAddress)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if !hasChanged {
|
if !hasChanged {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3M0cs", "Errors.User.Address.NotChanged")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3M0cs", "Errors.User.Address.NotChanged")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package command
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/caos/zitadel/internal/eventstore"
|
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/domain"
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
"github.com/caos/zitadel/internal/repository/user"
|
"github.com/caos/zitadel/internal/repository/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -87,28 +87,31 @@ func (wm *HumanAddressWriteModel) NewChangedEvent(
|
|||||||
postalCode,
|
postalCode,
|
||||||
region,
|
region,
|
||||||
streetAddress string,
|
streetAddress string,
|
||||||
) (*user.HumanAddressChangedEvent, bool) {
|
) (*user.HumanAddressChangedEvent, bool, error) {
|
||||||
hasChanged := false
|
changes := make([]user.AddressChanges, 0)
|
||||||
changedEvent := user.NewHumanAddressChangedEvent(ctx, aggregate)
|
var err error
|
||||||
|
|
||||||
if wm.Country != country {
|
if wm.Country != country {
|
||||||
hasChanged = true
|
changes = append(changes, user.ChangeCountry(country))
|
||||||
changedEvent.Country = &country
|
|
||||||
}
|
}
|
||||||
if wm.Locality != locality {
|
if wm.Locality != locality {
|
||||||
hasChanged = true
|
changes = append(changes, user.ChangeLocality(locality))
|
||||||
changedEvent.Locality = &locality
|
|
||||||
}
|
}
|
||||||
if wm.PostalCode != postalCode {
|
if wm.PostalCode != postalCode {
|
||||||
hasChanged = true
|
changes = append(changes, user.ChangePostalCode(postalCode))
|
||||||
changedEvent.PostalCode = &postalCode
|
|
||||||
}
|
}
|
||||||
if wm.Region != region {
|
if wm.Region != region {
|
||||||
hasChanged = true
|
changes = append(changes, user.ChangeRegion(region))
|
||||||
changedEvent.Region = ®ion
|
|
||||||
}
|
}
|
||||||
if wm.StreetAddress != streetAddress {
|
if wm.StreetAddress != streetAddress {
|
||||||
hasChanged = true
|
changes = append(changes, user.ChangeStreetAddress(streetAddress))
|
||||||
changedEvent.StreetAddress = &streetAddress
|
|
||||||
}
|
}
|
||||||
return changedEvent, hasChanged
|
if len(changes) == 0 {
|
||||||
|
return nil, false, nil
|
||||||
|
}
|
||||||
|
changeEvent, err := user.NewAddressChangedEvent(ctx, aggregate, changes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
return changeEvent, true, nil
|
||||||
}
|
}
|
||||||
|
|||||||
209
internal/command/user_human_adress_test.go
Normal file
209
internal/command/user_human_adress_test.go
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/caos/zitadel/internal/repository/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_ChangeHumanAddress(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
address *domain.Address
|
||||||
|
resourceOwner string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.Address
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "user not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
address: &domain.Address{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
Country: "Switzerland",
|
||||||
|
Locality: "St. Gallen",
|
||||||
|
PostalCode: "9000",
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "address not changed, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
newAddressChangedEvent(context.Background(),
|
||||||
|
"user1", "org1",
|
||||||
|
"country",
|
||||||
|
"locality",
|
||||||
|
"postalcode",
|
||||||
|
"region",
|
||||||
|
"street",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
address: &domain.Address{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
Country: "country",
|
||||||
|
Locality: "locality",
|
||||||
|
PostalCode: "postalcode",
|
||||||
|
Region: "region",
|
||||||
|
StreetAddress: "street",
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "address changed, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
newAddressChangedEvent(context.Background(),
|
||||||
|
"user1", "org1",
|
||||||
|
"country",
|
||||||
|
"locality",
|
||||||
|
"postalcode",
|
||||||
|
"region",
|
||||||
|
"street",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
address: &domain.Address{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
Country: "country",
|
||||||
|
Locality: "locality",
|
||||||
|
PostalCode: "postalcode",
|
||||||
|
Region: "region",
|
||||||
|
StreetAddress: "street",
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.Address{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
Country: "country",
|
||||||
|
Locality: "locality",
|
||||||
|
PostalCode: "postalcode",
|
||||||
|
Region: "region",
|
||||||
|
StreetAddress: "street",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.ChangeHumanAddress(tt.args.ctx, tt.args.address)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAddressChangedEvent(ctx context.Context, userID, resourceOwner, country, locality, postalCode, region, street string) *user.HumanAddressChangedEvent {
|
||||||
|
event, _ := user.NewAddressChangedEvent(ctx,
|
||||||
|
&user.NewAggregate(userID, resourceOwner).Aggregate,
|
||||||
|
[]user.AddressChanges{
|
||||||
|
user.ChangeCountry(country),
|
||||||
|
user.ChangeLocality(locality),
|
||||||
|
user.ChangePostalCode(postalCode),
|
||||||
|
user.ChangeRegion(region),
|
||||||
|
user.ChangeStreetAddress(street),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return event
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
|
|
||||||
func (c *Commands) ChangeHumanEmail(ctx context.Context, email *domain.Email) (*domain.Email, error) {
|
func (c *Commands) ChangeHumanEmail(ctx context.Context, email *domain.Email) (*domain.Email, error) {
|
||||||
if !email.IsValid() || email.AggregateID == "" {
|
if !email.IsValid() || email.AggregateID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M9sf", "Errors.Email.Invalid")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M9sf", "Errors.Email.Invalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingEmail, err := c.emailWriteModel(ctx, email.AggregateID, email.ResourceOwner)
|
existingEmail, err := c.emailWriteModel(ctx, email.AggregateID, email.ResourceOwner)
|
||||||
@@ -21,7 +21,7 @@ func (c *Commands) ChangeHumanEmail(ctx context.Context, email *domain.Email) (*
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted {
|
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted {
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-0Pe4r", "Errors.User.Email.NotFound")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-0Pe4r", "Errors.User.Email.NotFound")
|
||||||
}
|
}
|
||||||
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
|
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
|
||||||
changedEvent, hasChanged := existingEmail.NewChangedEvent(ctx, userAgg, email.EmailAddress)
|
changedEvent, hasChanged := existingEmail.NewChangedEvent(ctx, userAgg, email.EmailAddress)
|
||||||
@@ -54,10 +54,10 @@ func (c *Commands) ChangeHumanEmail(ctx context.Context, email *domain.Email) (*
|
|||||||
|
|
||||||
func (c *Commands) VerifyHumanEmail(ctx context.Context, userID, code, resourceowner string) (*domain.ObjectDetails, error) {
|
func (c *Commands) VerifyHumanEmail(ctx context.Context, userID, code, resourceowner string) (*domain.ObjectDetails, error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M0ds", "Errors.User.UserIDMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M0ds", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
if code == "" {
|
if code == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-çm0ds", "Errors.User.Code.Empty")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-çm0ds", "Errors.User.Code.Empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingCode, err := c.emailWriteModel(ctx, userID, resourceowner)
|
existingCode, err := c.emailWriteModel(ctx, userID, resourceowner)
|
||||||
@@ -89,7 +89,7 @@ func (c *Commands) VerifyHumanEmail(ctx context.Context, userID, code, resourceo
|
|||||||
|
|
||||||
func (c *Commands) CreateHumanEmailVerificationCode(ctx context.Context, userID, resourceOwner string) (*domain.ObjectDetails, error) {
|
func (c *Commands) CreateHumanEmailVerificationCode(ctx context.Context, userID, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M0ds", "Errors.User.UserIDMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M0ds", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingEmail, err := c.emailWriteModel(ctx, userID, resourceOwner)
|
existingEmail, err := c.emailWriteModel(ctx, userID, resourceOwner)
|
||||||
@@ -122,6 +122,9 @@ func (c *Commands) CreateHumanEmailVerificationCode(ctx context.Context, userID,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) HumanEmailVerificationCodeSent(ctx context.Context, orgID, userID string) (err error) {
|
func (c *Commands) HumanEmailVerificationCodeSent(ctx context.Context, orgID, userID string) (err error) {
|
||||||
|
if userID == "" {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-4m9fs", "Errors.IDMissing")
|
||||||
|
}
|
||||||
existingEmail, err := c.emailWriteModel(ctx, userID, orgID)
|
existingEmail, err := c.emailWriteModel(ctx, userID, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -82,11 +82,8 @@ func (wm *HumanEmailWriteModel) NewChangedEvent(
|
|||||||
aggregate *eventstore.Aggregate,
|
aggregate *eventstore.Aggregate,
|
||||||
email string,
|
email string,
|
||||||
) (*user.HumanEmailChangedEvent, bool) {
|
) (*user.HumanEmailChangedEvent, bool) {
|
||||||
hasChanged := false
|
if wm.Email == email {
|
||||||
changedEvent := user.NewHumanEmailChangedEvent(ctx, aggregate)
|
return nil, false
|
||||||
if wm.Email != email {
|
|
||||||
hasChanged = true
|
|
||||||
changedEvent.EmailAddress = email
|
|
||||||
}
|
}
|
||||||
return changedEvent, hasChanged
|
return user.NewHumanEmailChangedEvent(ctx, aggregate, email), true
|
||||||
}
|
}
|
||||||
|
|||||||
822
internal/command/user_human_email_test.go
Normal file
822
internal/command/user_human_email_test.go
Normal file
@@ -0,0 +1,822 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/caos/zitadel/internal/repository/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
secretGenerator crypto.Generator
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
email *domain.Email
|
||||||
|
resourceOwner string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.Email
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "invalid email, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
email: &domain.Email{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
email: &domain.Email{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
EmailAddress: "email@test.com",
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "email not changed, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
email: &domain.Email{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
EmailAddress: "email@test.ch",
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "verified email changed, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailChangedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"email-changed@test.ch",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailVerifiedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
email: &domain.Email{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
EmailAddress: "email-changed@test.ch",
|
||||||
|
IsEmailVerified: true,
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.Email{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
EmailAddress: "email-changed@test.ch",
|
||||||
|
IsEmailVerified: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "email changed with code, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailChangedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"email-changed@test.ch",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
email: &domain.Email{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
EmailAddress: "email-changed@test.ch",
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.Email{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
EmailAddress: "email-changed@test.ch",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
emailVerificationCode: tt.fields.secretGenerator,
|
||||||
|
}
|
||||||
|
got, err := r.ChangeHumanEmail(tt.args.ctx, tt.args.email)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
secretGenerator crypto.Generator
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
userID string
|
||||||
|
code string
|
||||||
|
resourceOwner string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "userid missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
code: "aa",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
code: "aa",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
code: "aa",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid code, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailVerificationFailedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
code: "test",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid code, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusherWithCreationDateNow(
|
||||||
|
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailVerifiedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
code: "a",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
emailVerificationCode: tt.fields.secretGenerator,
|
||||||
|
}
|
||||||
|
got, err := r.VerifyHumanEmail(tt.args.ctx, tt.args.userID, tt.args.code, tt.args.resourceOwner)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
secretGenerator crypto.Generator
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
userID string
|
||||||
|
resourceOwner string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "userid missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user not initialized, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "email already verified, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailVerifiedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "new code, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailVerifiedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailChangedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"email2@test.ch",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
emailVerificationCode: tt.fields.secretGenerator,
|
||||||
|
}
|
||||||
|
got, err := r.CreateHumanEmailVerificationCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_EmailVerificationCodeSent(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
userID string
|
||||||
|
resourceOwner string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "userid missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code sent, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailVerifiedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailChangedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"email2@test.ch",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailCodeSentEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
err := r.HumanEmailVerificationCodeSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,8 +11,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *Commands) BulkAddedHumanExternalIDP(ctx context.Context, userID, resourceOwner string, externalIDPs []*domain.ExternalIDP) (err error) {
|
func (c *Commands) BulkAddedHumanExternalIDP(ctx context.Context, userID, resourceOwner string, externalIDPs []*domain.ExternalIDP) (err error) {
|
||||||
|
if userID == "" {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-03j8f", "Errors.IDMissing")
|
||||||
|
}
|
||||||
if len(externalIDPs) == 0 {
|
if len(externalIDPs) == 0 {
|
||||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Ek9s", "Errors.User.ExternalIDP.MinimumExternalIDPNeeded")
|
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-Ek9s", "Errors.User.ExternalIDP.MinimumExternalIDPNeeded")
|
||||||
}
|
}
|
||||||
|
|
||||||
events := make([]eventstore.EventPusher, len(externalIDPs))
|
events := make([]eventstore.EventPusher, len(externalIDPs))
|
||||||
@@ -31,15 +34,18 @@ func (c *Commands) BulkAddedHumanExternalIDP(ctx context.Context, userID, resour
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) addHumanExternalIDP(ctx context.Context, humanAgg *eventstore.Aggregate, externalIDP *domain.ExternalIDP) (eventstore.EventPusher, error) {
|
func (c *Commands) addHumanExternalIDP(ctx context.Context, humanAgg *eventstore.Aggregate, externalIDP *domain.ExternalIDP) (eventstore.EventPusher, error) {
|
||||||
|
if externalIDP.AggregateID != "" && humanAgg.ID != externalIDP.AggregateID {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-33M0g", "Errors.IDMissing")
|
||||||
|
}
|
||||||
if !externalIDP.IsValid() {
|
if !externalIDP.IsValid() {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6m9Kd", "Errors.User.ExternalIDP.Invalid")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-6m9Kd", "Errors.User.ExternalIDP.Invalid")
|
||||||
}
|
}
|
||||||
_, err := c.getOrgIDPConfigByID(ctx, externalIDP.IDPConfigID, humanAgg.ResourceOwner)
|
_, err := c.getOrgIDPConfigByID(ctx, externalIDP.IDPConfigID, humanAgg.ResourceOwner)
|
||||||
if caos_errs.IsNotFound(err) {
|
if caos_errs.IsNotFound(err) {
|
||||||
_, err = c.getIAMIDPConfigByID(ctx, externalIDP.IDPConfigID)
|
_, err = c.getIAMIDPConfigByID(ctx, externalIDP.IDPConfigID)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-39nfs", "Errors.IDPConfig.NotExisting")
|
||||||
}
|
}
|
||||||
return user.NewHumanExternalIDPAddedEvent(ctx, humanAgg, externalIDP.IDPConfigID, externalIDP.DisplayName, externalIDP.ExternalUserID), nil
|
return user.NewHumanExternalIDPAddedEvent(ctx, humanAgg, externalIDP.IDPConfigID, externalIDP.DisplayName, externalIDP.ExternalUserID), nil
|
||||||
}
|
}
|
||||||
@@ -61,8 +67,8 @@ func (c *Commands) RemoveHumanExternalIDP(ctx context.Context, externalIDP *doma
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) removeHumanExternalIDP(ctx context.Context, externalIDP *domain.ExternalIDP, cascade bool) (eventstore.EventPusher, *HumanExternalIDPWriteModel, error) {
|
func (c *Commands) removeHumanExternalIDP(ctx context.Context, externalIDP *domain.ExternalIDP, cascade bool) (eventstore.EventPusher, *HumanExternalIDPWriteModel, error) {
|
||||||
if externalIDP.IsValid() {
|
if !externalIDP.IsValid() || externalIDP.AggregateID == "" {
|
||||||
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3M9ds", "Errors.IDMissing")
|
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-3M9ds", "Errors.IDMissing")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingExternalIDP, err := c.externalIDPWriteModelByID(ctx, externalIDP.AggregateID, externalIDP.IDPConfigID, externalIDP.ExternalUserID, externalIDP.ResourceOwner)
|
existingExternalIDP, err := c.externalIDPWriteModelByID(ctx, externalIDP.AggregateID, externalIDP.IDPConfigID, externalIDP.ExternalUserID, externalIDP.ResourceOwner)
|
||||||
@@ -81,7 +87,7 @@ func (c *Commands) removeHumanExternalIDP(ctx context.Context, externalIDP *doma
|
|||||||
|
|
||||||
func (c *Commands) HumanExternalLoginChecked(ctx context.Context, orgID, userID string, authRequest *domain.AuthRequest) (err error) {
|
func (c *Commands) HumanExternalLoginChecked(ctx context.Context, orgID, userID string, authRequest *domain.AuthRequest) (err error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return caos_errs.ThrowNotFound(nil, "COMMAND-5n8sM", "Errors.IDMissing")
|
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-5n8sM", "Errors.IDMissing")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingHuman, err := c.getHumanWriteModelByID(ctx, userID, orgID)
|
existingHuman, err := c.getHumanWriteModelByID(ctx, userID, orgID)
|
||||||
@@ -89,7 +95,7 @@ func (c *Commands) HumanExternalLoginChecked(ctx context.Context, orgID, userID
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if existingHuman.UserState == domain.UserStateUnspecified || existingHuman.UserState == domain.UserStateDeleted {
|
if existingHuman.UserState == domain.UserStateUnspecified || existingHuman.UserState == domain.UserStateDeleted {
|
||||||
return caos_errs.ThrowNotFound(nil, "COMMAND-dn88J", "Errors.User.NotFound")
|
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-dn88J", "Errors.User.NotFound")
|
||||||
}
|
}
|
||||||
|
|
||||||
userAgg := UserAggregateFromWriteModel(&existingHuman.WriteModel)
|
userAgg := UserAggregateFromWriteModel(&existingHuman.WriteModel)
|
||||||
|
|||||||
582
internal/command/user_human_externalidp_test.go
Normal file
582
internal/command/user_human_externalidp_test.go
Normal file
@@ -0,0 +1,582 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/caos/zitadel/internal/repository/iam"
|
||||||
|
"github.com/caos/zitadel/internal/repository/org"
|
||||||
|
"github.com/caos/zitadel/internal/repository/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_BulkAddExternalIDPs(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
userID string
|
||||||
|
resourceOwner string
|
||||||
|
externalIDPs []*domain.ExternalIDP
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "missing userid, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "",
|
||||||
|
externalIDPs: []*domain.ExternalIDP{
|
||||||
|
{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
ExternalUserID: "externaluser1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no external idps, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "userID doesnt match aggregate id, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
externalIDPs: []*domain.ExternalIDP{
|
||||||
|
{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user2",
|
||||||
|
},
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
ExternalUserID: "externaluser1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid external idp, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
externalIDPs: []*domain.ExternalIDP{
|
||||||
|
{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
IDPConfigID: "",
|
||||||
|
ExternalUserID: "externaluser1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "config not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
externalIDPs: []*domain.ExternalIDP{
|
||||||
|
{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
ExternalUserID: "externaluser1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add external idp org config, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewIDPConfigAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name",
|
||||||
|
domain.IDPConfigTypeOIDC,
|
||||||
|
domain.IDPConfigStylingTypeUnspecified,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanExternalIDPAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name",
|
||||||
|
"externaluser1",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
uniqueConstraintsFromEventConstraint(user.NewAddExternalIDPUniqueConstraint("config1", "externaluser1")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
externalIDPs: []*domain.ExternalIDP{
|
||||||
|
{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
DisplayName: "name",
|
||||||
|
ExternalUserID: "externaluser1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add external idp iam config, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
iam.NewIDPConfigAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name",
|
||||||
|
domain.IDPConfigTypeOIDC,
|
||||||
|
domain.IDPConfigStylingTypeUnspecified,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanExternalIDPAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name",
|
||||||
|
"externaluser1",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
uniqueConstraintsFromEventConstraint(user.NewAddExternalIDPUniqueConstraint("config1", "externaluser1")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
externalIDPs: []*domain.ExternalIDP{
|
||||||
|
{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
DisplayName: "name",
|
||||||
|
ExternalUserID: "externaluser1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
err := r.BulkAddedHumanExternalIDP(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.externalIDPs)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_RemoveExternalIDP(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
externalIDP *domain.ExternalIDP
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "invalid idp, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
externalIDP: &domain.ExternalIDP{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
IDPConfigID: "",
|
||||||
|
ExternalUserID: "externaluser1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "aggregate id missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
externalIDP: &domain.ExternalIDP{
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
ExternalUserID: "externaluser1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user removed, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanExternalIDPAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name",
|
||||||
|
"externaluser1",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewUserRemovedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
externalIDP: &domain.ExternalIDP{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
ExternalUserID: "externaluser1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "external idp not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
externalIDP: &domain.ExternalIDP{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
ExternalUserID: "externaluser1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove external idp, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanExternalIDPAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name",
|
||||||
|
"externaluser1",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanExternalIDPRemovedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"config1",
|
||||||
|
"externaluser1",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
uniqueConstraintsFromEventConstraint(user.NewRemoveExternalIDPUniqueConstraint("config1", "externaluser1")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
externalIDP: &domain.ExternalIDP{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
ExternalUserID: "externaluser1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.RemoveHumanExternalIDP(tt.args.ctx, tt.args.externalIDP)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_ExternalLoginCheck(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
userID string
|
||||||
|
authRequest *domain.AuthRequest
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "userid missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
userID: "",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user removed, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanExternalIDPAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name",
|
||||||
|
"externaluser1",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewUserRemovedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
userID: "user1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "external login check, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanExternalIDPCheckSucceededEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&user.AuthRequestInfo{
|
||||||
|
ID: "request1",
|
||||||
|
UserAgentID: "useragent1",
|
||||||
|
SelectedIDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
userID: "user1",
|
||||||
|
authRequest: &domain.AuthRequest{
|
||||||
|
ID: "request1",
|
||||||
|
AgentID: "useragent1",
|
||||||
|
SelectedIDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
err := r.HumanExternalLoginChecked(tt.args.ctx, tt.args.orgID, tt.args.userID, tt.args.authRequest)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
//ResendInitialMail resend inital mail and changes email if provided
|
//ResendInitialMail resend inital mail and changes email if provided
|
||||||
func (c *Commands) ResendInitialMail(ctx context.Context, userID, email, resourceOwner string) (objectDetails *domain.ObjectDetails, err error) {
|
func (c *Commands) ResendInitialMail(ctx context.Context, userID, email, resourceOwner string) (objectDetails *domain.ObjectDetails, err error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2n8vs", "Errors.User.UserIDMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-2n8vs", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingCode, err := c.getHumanInitWriteModelByID(ctx, userID, resourceOwner)
|
existingCode, err := c.getHumanInitWriteModelByID(ctx, userID, resourceOwner)
|
||||||
@@ -50,10 +50,10 @@ func (c *Commands) ResendInitialMail(ctx context.Context, userID, email, resourc
|
|||||||
|
|
||||||
func (c *Commands) HumanVerifyInitCode(ctx context.Context, userID, resourceOwner, code, passwordString string) error {
|
func (c *Commands) HumanVerifyInitCode(ctx context.Context, userID, resourceOwner, code, passwordString string) error {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-mkM9f", "Errors.User.UserIDMissing")
|
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-mkM9f", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
if code == "" {
|
if code == "" {
|
||||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-44G8s", "Errors.User.Code.Empty")
|
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-44G8s", "Errors.User.Code.Empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingCode, err := c.getHumanInitWriteModelByID(ctx, userID, resourceOwner)
|
existingCode, err := c.getHumanInitWriteModelByID(ctx, userID, resourceOwner)
|
||||||
@@ -79,6 +79,7 @@ func (c *Commands) HumanVerifyInitCode(ctx context.Context, userID, resourceOwne
|
|||||||
}
|
}
|
||||||
if passwordString != "" {
|
if passwordString != "" {
|
||||||
passwordWriteModel := NewHumanPasswordWriteModel(userID, existingCode.ResourceOwner)
|
passwordWriteModel := NewHumanPasswordWriteModel(userID, existingCode.ResourceOwner)
|
||||||
|
passwordWriteModel.UserState = domain.UserStateActive
|
||||||
password := &domain.Password{
|
password := &domain.Password{
|
||||||
SecretString: passwordString,
|
SecretString: passwordString,
|
||||||
ChangeRequired: false,
|
ChangeRequired: false,
|
||||||
@@ -89,12 +90,14 @@ func (c *Commands) HumanVerifyInitCode(ctx context.Context, userID, resourceOwne
|
|||||||
}
|
}
|
||||||
events = append(events, passwordEvent)
|
events = append(events, passwordEvent)
|
||||||
}
|
}
|
||||||
events = append(events, user.NewHumanInitialCodeSentEvent(ctx, userAgg))
|
|
||||||
_, err = c.eventstore.PushEvents(ctx, events...)
|
_, err = c.eventstore.PushEvents(ctx, events...)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) HumanInitCodeSent(ctx context.Context, orgID, userID string) (err error) {
|
func (c *Commands) HumanInitCodeSent(ctx context.Context, orgID, userID string) (err error) {
|
||||||
|
if userID == "" {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-3M9fs", "Errors.IDMissing")
|
||||||
|
}
|
||||||
existingInitCode, err := c.getHumanInitWriteModelByID(ctx, userID, orgID)
|
existingInitCode, err := c.getHumanInitWriteModelByID(ctx, userID, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -84,11 +84,6 @@ func (wm *HumanInitCodeWriteModel) NewChangedEvent(
|
|||||||
aggregate *eventstore.Aggregate,
|
aggregate *eventstore.Aggregate,
|
||||||
email string,
|
email string,
|
||||||
) (*user.HumanEmailChangedEvent, bool) {
|
) (*user.HumanEmailChangedEvent, bool) {
|
||||||
hasChanged := false
|
changedEvent := user.NewHumanEmailChangedEvent(ctx, aggregate, email)
|
||||||
changedEvent := user.NewHumanEmailChangedEvent(ctx, aggregate)
|
return changedEvent, wm.Email != email
|
||||||
if wm.Email != email {
|
|
||||||
hasChanged = true
|
|
||||||
changedEvent.EmailAddress = email
|
|
||||||
}
|
|
||||||
return changedEvent, hasChanged
|
|
||||||
}
|
}
|
||||||
|
|||||||
720
internal/command/user_human_init_test.go
Normal file
720
internal/command/user_human_init_test.go
Normal file
@@ -0,0 +1,720 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/repository/org"
|
||||||
|
"github.com/caos/zitadel/internal/repository/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_ResendInitialMail(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
secretGenerator crypto.Generator
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
userID string
|
||||||
|
email string
|
||||||
|
resourceOwner string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "userid missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user not initialized, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailVerifiedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "new code email not changed, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
email: "email@test.ch",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "new code, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "new code with change email, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailChangedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"email2@test.ch")),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
email: "email2@test.ch",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
initializeUserCode: tt.fields.secretGenerator,
|
||||||
|
}
|
||||||
|
got, err := r.ResendInitialMail(tt.args.ctx, tt.args.userID, tt.args.email, tt.args.resourceOwner)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_VerifyInitCode(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
secretGenerator crypto.Generator
|
||||||
|
userPasswordAlg crypto.HashAlgorithm
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
userID string
|
||||||
|
code string
|
||||||
|
resourceOwner string
|
||||||
|
password string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "userid missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
code: "aa",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
code: "aa",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
code: "aa",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid code, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanInitializedCheckFailedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
code: "test",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid code, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusherWithCreationDateNow(
|
||||||
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanInitializedCheckSucceededEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailVerifiedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
code: "a",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid code with password, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailVerifiedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate)),
|
||||||
|
eventFromEventPusherWithCreationDateNow(
|
||||||
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanInitializedCheckSucceededEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPasswordChangedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeHash,
|
||||||
|
Algorithm: "hash",
|
||||||
|
KeyID: "",
|
||||||
|
Crypted: []byte("password"),
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
"")),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
code: "a",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
password: "password",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
initializeUserCode: tt.fields.secretGenerator,
|
||||||
|
userPasswordAlg: tt.fields.userPasswordAlg,
|
||||||
|
}
|
||||||
|
err := r.HumanVerifyInitCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.code, tt.args.password)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_InitCodeSent(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
userID string
|
||||||
|
resourceOwner string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "userid missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code sent, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanInitialCodeSentEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
err := r.HumanInitCodeSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,22 +12,22 @@ import (
|
|||||||
|
|
||||||
func (c *Commands) AddHumanOTP(ctx context.Context, userID, resourceowner string) (*domain.OTP, error) {
|
func (c *Commands) AddHumanOTP(ctx context.Context, userID, resourceowner string) (*domain.OTP, error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-5M0sd", "Errors.User.UserIDMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-5M0sd", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
human, err := c.getHuman(ctx, userID, resourceowner)
|
human, err := c.getHuman(ctx, userID, resourceowner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Log("COMMAND-DAqe1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get human for loginname")
|
logging.Log("COMMAND-DAqe1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get human for loginname")
|
||||||
return nil, err
|
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-MM9fs", "Errors.User.NotFound")
|
||||||
}
|
}
|
||||||
org, err := c.getOrg(ctx, human.ResourceOwner)
|
org, err := c.getOrg(ctx, human.ResourceOwner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Log("COMMAND-Cm0ds").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get org for loginname")
|
logging.Log("COMMAND-Cm0ds").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get org for loginname")
|
||||||
return nil, err
|
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-55M9f", "Errors.Org.NotFound")
|
||||||
}
|
}
|
||||||
orgPolicy, err := c.getOrgIAMPolicy(ctx, org.AggregateID)
|
orgPolicy, err := c.getOrgIAMPolicy(ctx, org.AggregateID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Log("COMMAND-y5zv9").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get org policy for loginname")
|
logging.Log("COMMAND-y5zv9").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get org policy for loginname")
|
||||||
return nil, err
|
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-8ugTs", "Errors.Org.OrgIAM.NotFound")
|
||||||
}
|
}
|
||||||
otpWriteModel, err := c.otpWriteModelByID(ctx, userID, resourceowner)
|
otpWriteModel, err := c.otpWriteModelByID(ctx, userID, resourceowner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -114,7 +114,7 @@ func (c *Commands) HumanCheckMFAOTP(ctx context.Context, userID, code, resourceo
|
|||||||
|
|
||||||
func (c *Commands) HumanRemoveOTP(ctx context.Context, userID, resourceOwner string) (*domain.ObjectDetails, error) {
|
func (c *Commands) HumanRemoveOTP(ctx context.Context, userID, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-5M0sd", "Errors.User.UserIDMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-5M0sd", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingOTP, err := c.otpWriteModelByID(ctx, userID, resourceOwner)
|
existingOTP, err := c.otpWriteModelByID(ctx, userID, resourceOwner)
|
||||||
|
|||||||
339
internal/command/user_human_otp_test.go
Normal file
339
internal/command/user_human_otp_test.go
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/repository/org"
|
||||||
|
"github.com/caos/zitadel/internal/repository/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_AddHumanOTP(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type (
|
||||||
|
args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
userID string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "userid missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
userID: "",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
userID: "user1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "org not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
userID: "user1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "org iam policy not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewOrgAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectFilter(),
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
userID: "user1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "otp already exists, already exists error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewOrgAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
"org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewOrgIAMPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1", "org1").Aggregate,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanOTPAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanOTPVerifiedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"agent1")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
userID: "user1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorAlreadyExists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.AddHumanOTP(tt.args.ctx, tt.args.userID, tt.args.orgID)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_RemoveHumanOTP(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type (
|
||||||
|
args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
userID string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "userid missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
userID: "",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "otp not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
userID: "user1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "otp not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanOTPAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanOTPRemovedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
userID: "user1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.HumanRemoveOTP(tt.args.ctx, tt.args.userID, tt.args.orgID)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,11 +14,16 @@ import (
|
|||||||
func (c *Commands) SetOneTimePassword(ctx context.Context, orgID, userID, passwordString string) (objectDetails *domain.ObjectDetails, err error) {
|
func (c *Commands) SetOneTimePassword(ctx context.Context, orgID, userID, passwordString string) (objectDetails *domain.ObjectDetails, err error) {
|
||||||
ctx, span := tracing.NewSpan(ctx)
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
|
if userID == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-3M0fs", "Errors.IDMissing")
|
||||||
|
}
|
||||||
existingPassword, err := c.passwordWriteModel(ctx, userID, orgID)
|
existingPassword, err := c.passwordWriteModel(ctx, userID, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if !existingPassword.UserState.Exists() {
|
||||||
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3M0fs", "Errors.User.NotFound")
|
||||||
|
}
|
||||||
password := &domain.Password{
|
password := &domain.Password{
|
||||||
SecretString: passwordString,
|
SecretString: passwordString,
|
||||||
ChangeRequired: true,
|
ChangeRequired: true,
|
||||||
@@ -43,16 +48,22 @@ func (c *Commands) SetPassword(ctx context.Context, orgID, userID, code, passwor
|
|||||||
ctx, span := tracing.NewSpan(ctx)
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
|
if userID == "" {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-3M9fs", "Errors.IDMissing")
|
||||||
|
}
|
||||||
|
if passwordString == "" {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-Mf0sd", "Errors.User.Password.Empty")
|
||||||
|
}
|
||||||
existingCode, err := c.passwordWriteModel(ctx, userID, orgID)
|
existingCode, err := c.passwordWriteModel(ctx, userID, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if existingCode.Code == nil || existingCode.UserState == domain.UserStateUnspecified || existingCode.UserState == domain.UserStateDeleted {
|
if existingCode.Code == nil || existingCode.UserState == domain.UserStateUnspecified || existingCode.UserState == domain.UserStateDeleted {
|
||||||
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9fs", "Errors.User.Code.NotFound")
|
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.Code.NotFound")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, c.emailVerificationCode)
|
err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, c.passwordVerificationCode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -74,6 +85,12 @@ func (c *Commands) ChangePassword(ctx context.Context, orgID, userID, oldPasswor
|
|||||||
ctx, span := tracing.NewSpan(ctx)
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
|
if userID == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-3M0fs", "Errors.IDMissing")
|
||||||
|
}
|
||||||
|
if oldPassword == "" || newPassword == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-3M0fs", "Errors.User.Password.Empty")
|
||||||
|
}
|
||||||
existingPassword, err := c.passwordWriteModel(ctx, userID, orgID)
|
existingPassword, err := c.passwordWriteModel(ctx, userID, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -114,7 +131,7 @@ func (c *Commands) changePassword(ctx context.Context, userAgentID string, passw
|
|||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
if existingPassword.UserState == domain.UserStateUnspecified || existingPassword.UserState == domain.UserStateDeleted {
|
if existingPassword.UserState == domain.UserStateUnspecified || existingPassword.UserState == domain.UserStateDeleted {
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-G8dh3", "Errors.User.Email.NotFound")
|
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-G8dh3", "Errors.User.Password.NotFound")
|
||||||
}
|
}
|
||||||
if existingPassword.UserState == domain.UserStateInitial {
|
if existingPassword.UserState == domain.UserStateInitial {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-M9dse", "Errors.User.NotInitialised")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-M9dse", "Errors.User.NotInitialised")
|
||||||
@@ -130,12 +147,16 @@ func (c *Commands) changePassword(ctx context.Context, userAgentID string, passw
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) RequestSetPassword(ctx context.Context, userID, resourceOwner string, notifyType domain.NotificationType) (objectDetails *domain.ObjectDetails, err error) {
|
func (c *Commands) RequestSetPassword(ctx context.Context, userID, resourceOwner string, notifyType domain.NotificationType) (objectDetails *domain.ObjectDetails, err error) {
|
||||||
|
if userID == "" {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-M00oL", "Errors.User.UserIDMissing")
|
||||||
|
}
|
||||||
|
|
||||||
existingHuman, err := c.userWriteModelByID(ctx, userID, resourceOwner)
|
existingHuman, err := c.userWriteModelByID(ctx, userID, resourceOwner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if existingHuman.UserState == domain.UserStateUnspecified || existingHuman.UserState == domain.UserStateDeleted {
|
if existingHuman.UserState == domain.UserStateUnspecified || existingHuman.UserState == domain.UserStateDeleted {
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-Hj9ds", "Errors.User.NotFound")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Hj9ds", "Errors.User.NotFound")
|
||||||
}
|
}
|
||||||
if existingHuman.UserState == domain.UserStateInitial {
|
if existingHuman.UserState == domain.UserStateInitial {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sd", "Errors.User.NotInitialised")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sd", "Errors.User.NotInitialised")
|
||||||
@@ -157,12 +178,16 @@ func (c *Commands) RequestSetPassword(ctx context.Context, userID, resourceOwner
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) PasswordCodeSent(ctx context.Context, orgID, userID string) (err error) {
|
func (c *Commands) PasswordCodeSent(ctx context.Context, orgID, userID string) (err error) {
|
||||||
|
if userID == "" {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-MM9fs", "Errors.User.UserIDMissing")
|
||||||
|
}
|
||||||
|
|
||||||
existingPassword, err := c.passwordWriteModel(ctx, userID, orgID)
|
existingPassword, err := c.passwordWriteModel(ctx, userID, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if existingPassword.UserState == domain.UserStateUnspecified || existingPassword.UserState == domain.UserStateDeleted {
|
if existingPassword.UserState == domain.UserStateUnspecified || existingPassword.UserState == domain.UserStateDeleted {
|
||||||
return caos_errs.ThrowNotFound(nil, "COMMAND-3n77z", "Errors.User.NotFound")
|
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3n77z", "Errors.User.NotFound")
|
||||||
}
|
}
|
||||||
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
|
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
|
||||||
_, err = c.eventstore.PushEvents(ctx, user.NewHumanPasswordCodeSentEvent(ctx, userAgg))
|
_, err = c.eventstore.PushEvents(ctx, user.NewHumanPasswordCodeSentEvent(ctx, userAgg))
|
||||||
@@ -173,8 +198,11 @@ func (c *Commands) HumanCheckPassword(ctx context.Context, orgID, userID, passwo
|
|||||||
ctx, span := tracing.NewSpan(ctx)
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
|
if userID == "" {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-4Mfsf", "Errors.User.UserIDMissing")
|
||||||
|
}
|
||||||
if password == "" {
|
if password == "" {
|
||||||
return caos_errs.ThrowNotFound(nil, "COMMAND-3n8fs", "Errors.User.Password.Empty")
|
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-3n8fs", "Errors.User.Password.Empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingPassword, err := c.passwordWriteModel(ctx, userID, orgID)
|
existingPassword, err := c.passwordWriteModel(ctx, userID, orgID)
|
||||||
@@ -182,11 +210,11 @@ func (c *Commands) HumanCheckPassword(ctx context.Context, orgID, userID, passwo
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if existingPassword.UserState == domain.UserStateUnspecified || existingPassword.UserState == domain.UserStateDeleted {
|
if existingPassword.UserState == domain.UserStateUnspecified || existingPassword.UserState == domain.UserStateDeleted {
|
||||||
return caos_errs.ThrowNotFound(nil, "COMMAND-3n77z", "Errors.User.NotFound")
|
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3n77z", "Errors.User.NotFound")
|
||||||
}
|
}
|
||||||
|
|
||||||
if existingPassword.Secret == nil {
|
if existingPassword.Secret == nil {
|
||||||
return caos_errs.ThrowNotFound(nil, "COMMAND-3n77z", "Errors.User.Password.NotSet")
|
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3n77z", "Errors.User.Password.NotSet")
|
||||||
}
|
}
|
||||||
|
|
||||||
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
|
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
|
||||||
|
|||||||
1248
internal/command/user_human_password_test.go
Normal file
1248
internal/command/user_human_password_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -14,15 +14,15 @@ import (
|
|||||||
|
|
||||||
func (c *Commands) ChangeHumanPhone(ctx context.Context, phone *domain.Phone) (*domain.Phone, error) {
|
func (c *Commands) ChangeHumanPhone(ctx context.Context, phone *domain.Phone) (*domain.Phone, error) {
|
||||||
if !phone.IsValid() {
|
if !phone.IsValid() {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M0ds", "Errors.Phone.Invalid")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-6M0ds", "Errors.Phone.Invalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingPhone, err := c.phoneWriteModelByID(ctx, phone.AggregateID, phone.ResourceOwner)
|
existingPhone, err := c.phoneWriteModelByID(ctx, phone.AggregateID, phone.ResourceOwner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !existingPhone.State.Exists() {
|
if !existingPhone.UserState.Exists() {
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-aM9cs", "Errors.User.Phone.NotFound")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3M0fs", "Errors.User.NotFound")
|
||||||
}
|
}
|
||||||
|
|
||||||
userAgg := UserAggregateFromWriteModel(&existingPhone.WriteModel)
|
userAgg := UserAggregateFromWriteModel(&existingPhone.WriteModel)
|
||||||
@@ -56,17 +56,20 @@ func (c *Commands) ChangeHumanPhone(ctx context.Context, phone *domain.Phone) (*
|
|||||||
|
|
||||||
func (c *Commands) VerifyHumanPhone(ctx context.Context, userID, code, resourceowner string) (*domain.ObjectDetails, error) {
|
func (c *Commands) VerifyHumanPhone(ctx context.Context, userID, code, resourceowner string) (*domain.ObjectDetails, error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Km9ds", "Errors.User.UserIDMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-Km9ds", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
if code == "" {
|
if code == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-wMe9f", "Errors.User.Code.Empty")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-wMe9f", "Errors.User.Code.Empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingCode, err := c.phoneWriteModelByID(ctx, userID, resourceowner)
|
existingCode, err := c.phoneWriteModelByID(ctx, userID, resourceowner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !existingCode.State.Exists() {
|
if !existingCode.UserState.Exists() {
|
||||||
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Rsj8c", "Errors.User.NotFound")
|
||||||
|
}
|
||||||
|
if !existingCode.State.Exists() || existingCode.Code == nil {
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-Rsj8c", "Errors.User.Code.NotFound")
|
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-Rsj8c", "Errors.User.Code.NotFound")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +93,7 @@ func (c *Commands) VerifyHumanPhone(ctx context.Context, userID, code, resourceo
|
|||||||
|
|
||||||
func (c *Commands) CreateHumanPhoneVerificationCode(ctx context.Context, userID, resourceowner string) (*domain.ObjectDetails, error) {
|
func (c *Commands) CreateHumanPhoneVerificationCode(ctx context.Context, userID, resourceowner string) (*domain.ObjectDetails, error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M0ds", "Errors.User.UserIDMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M0ds", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingPhone, err := c.phoneWriteModelByID(ctx, userID, resourceowner)
|
existingPhone, err := c.phoneWriteModelByID(ctx, userID, resourceowner)
|
||||||
@@ -98,6 +101,9 @@ func (c *Commands) CreateHumanPhoneVerificationCode(ctx context.Context, userID,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !existingPhone.UserState.Exists() {
|
||||||
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M0fs", "Errors.User.NotFound")
|
||||||
|
}
|
||||||
if !existingPhone.State.Exists() {
|
if !existingPhone.State.Exists() {
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-2b7Hf", "Errors.User.Phone.NotFound")
|
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-2b7Hf", "Errors.User.Phone.NotFound")
|
||||||
}
|
}
|
||||||
@@ -123,10 +129,17 @@ func (c *Commands) CreateHumanPhoneVerificationCode(ctx context.Context, userID,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) HumanPhoneVerificationCodeSent(ctx context.Context, orgID, userID string) (err error) {
|
func (c *Commands) HumanPhoneVerificationCodeSent(ctx context.Context, orgID, userID string) (err error) {
|
||||||
|
if userID == "" {
|
||||||
|
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-3m9Fs", "Errors.User.UserIDMissing")
|
||||||
|
}
|
||||||
|
|
||||||
existingPhone, err := c.phoneWriteModelByID(ctx, userID, orgID)
|
existingPhone, err := c.phoneWriteModelByID(ctx, userID, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if !existingPhone.UserState.Exists() {
|
||||||
|
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3M9fs", "Errors.User.NotFound")
|
||||||
|
}
|
||||||
if !existingPhone.State.Exists() {
|
if !existingPhone.State.Exists() {
|
||||||
return caos_errs.ThrowNotFound(nil, "COMMAND-66n8J", "Errors.User.Phone.NotFound")
|
return caos_errs.ThrowNotFound(nil, "COMMAND-66n8J", "Errors.User.Phone.NotFound")
|
||||||
}
|
}
|
||||||
@@ -138,13 +151,16 @@ func (c *Commands) HumanPhoneVerificationCodeSent(ctx context.Context, orgID, us
|
|||||||
|
|
||||||
func (c *Commands) RemoveHumanPhone(ctx context.Context, userID, resourceOwner string) (*domain.ObjectDetails, error) {
|
func (c *Commands) RemoveHumanPhone(ctx context.Context, userID, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M0ds", "Errors.User.UserIDMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-6M0ds", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingPhone, err := c.phoneWriteModelByID(ctx, userID, resourceOwner)
|
existingPhone, err := c.phoneWriteModelByID(ctx, userID, resourceOwner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if !existingPhone.UserState.Exists() {
|
||||||
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3M9fs", "Errors.User.NotFound")
|
||||||
|
}
|
||||||
if !existingPhone.State.Exists() {
|
if !existingPhone.State.Exists() {
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-p6rsc", "Errors.User.Phone.NotFound")
|
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-p6rsc", "Errors.User.Phone.NotFound")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ type HumanPhoneWriteModel struct {
|
|||||||
CodeCreationDate time.Time
|
CodeCreationDate time.Time
|
||||||
CodeExpiry time.Duration
|
CodeExpiry time.Duration
|
||||||
|
|
||||||
State domain.PhoneState
|
State domain.PhoneState
|
||||||
|
UserState domain.UserState
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHumanPhoneWriteModel(userID, resourceOwner string) *HumanPhoneWriteModel {
|
func NewHumanPhoneWriteModel(userID, resourceOwner string) *HumanPhoneWriteModel {
|
||||||
@@ -38,13 +39,15 @@ func (wm *HumanPhoneWriteModel) Reduce() error {
|
|||||||
case *user.HumanAddedEvent:
|
case *user.HumanAddedEvent:
|
||||||
if e.PhoneNumber != "" {
|
if e.PhoneNumber != "" {
|
||||||
wm.Phone = e.PhoneNumber
|
wm.Phone = e.PhoneNumber
|
||||||
|
wm.State = domain.PhoneStateActive
|
||||||
}
|
}
|
||||||
wm.State = domain.PhoneStateActive
|
wm.UserState = domain.UserStateActive
|
||||||
case *user.HumanRegisteredEvent:
|
case *user.HumanRegisteredEvent:
|
||||||
if e.PhoneNumber != "" {
|
if e.PhoneNumber != "" {
|
||||||
wm.Phone = e.PhoneNumber
|
wm.Phone = e.PhoneNumber
|
||||||
wm.State = domain.PhoneStateActive
|
wm.State = domain.PhoneStateActive
|
||||||
}
|
}
|
||||||
|
wm.UserState = domain.UserStateActive
|
||||||
case *user.HumanPhoneChangedEvent:
|
case *user.HumanPhoneChangedEvent:
|
||||||
wm.Phone = e.PhoneNumber
|
wm.Phone = e.PhoneNumber
|
||||||
wm.IsPhoneVerified = false
|
wm.IsPhoneVerified = false
|
||||||
@@ -60,7 +63,7 @@ func (wm *HumanPhoneWriteModel) Reduce() error {
|
|||||||
case *user.HumanPhoneRemovedEvent:
|
case *user.HumanPhoneRemovedEvent:
|
||||||
wm.State = domain.PhoneStateRemoved
|
wm.State = domain.PhoneStateRemoved
|
||||||
case *user.UserRemovedEvent:
|
case *user.UserRemovedEvent:
|
||||||
wm.State = domain.PhoneStateRemoved
|
wm.UserState = domain.UserStateDeleted
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return wm.WriteModel.Reduce()
|
return wm.WriteModel.Reduce()
|
||||||
@@ -84,11 +87,6 @@ func (wm *HumanPhoneWriteModel) NewChangedEvent(
|
|||||||
aggregate *eventstore.Aggregate,
|
aggregate *eventstore.Aggregate,
|
||||||
phone string,
|
phone string,
|
||||||
) (*user.HumanPhoneChangedEvent, bool) {
|
) (*user.HumanPhoneChangedEvent, bool) {
|
||||||
hasChanged := false
|
changedEvent := user.NewHumanPhoneChangedEvent(ctx, aggregate, phone)
|
||||||
changedEvent := user.NewHumanPhoneChangedEvent(ctx, aggregate)
|
return changedEvent, phone != wm.Phone
|
||||||
if wm.Phone != phone {
|
|
||||||
hasChanged = true
|
|
||||||
changedEvent.PhoneNumber = phone
|
|
||||||
}
|
|
||||||
return changedEvent, hasChanged
|
|
||||||
}
|
}
|
||||||
|
|||||||
962
internal/command/user_human_phone_test.go
Normal file
962
internal/command/user_human_phone_test.go
Normal file
@@ -0,0 +1,962 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/caos/zitadel/internal/repository/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_ChangeHumanPhone(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
secretGenerator crypto.Generator
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
email *domain.Phone
|
||||||
|
resourceOwner string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.Phone
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "invalid phone, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
email: &domain.Phone{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
email: &domain.Phone{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
PhoneNumber: "0711234567",
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "phone not changed, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneChangedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"+41711234567",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
email: &domain.Phone{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
PhoneNumber: "0711234567",
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "verified phone changed, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneChangedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"+41711234567",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneChangedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"+41719876543",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneVerifiedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
email: &domain.Phone{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
PhoneNumber: "0719876543",
|
||||||
|
IsPhoneVerified: true,
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.Phone{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
PhoneNumber: "+41719876543",
|
||||||
|
IsPhoneVerified: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "phone changed with code, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneChangedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"+41711234567",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
email: &domain.Phone{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
PhoneNumber: "0711234567",
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.Phone{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
PhoneNumber: "+41711234567",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
phoneVerificationCode: tt.fields.secretGenerator,
|
||||||
|
}
|
||||||
|
got, err := r.ChangeHumanPhone(tt.args.ctx, tt.args.email)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_VerifyHumanPhone(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
secretGenerator crypto.Generator
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
userID string
|
||||||
|
code string
|
||||||
|
resourceOwner string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "userid missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
code: "aa",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
code: "aa",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code not existing, not found error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
code: "aa",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid code, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneChangedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"+411234567",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneVerificationFailedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
code: "test",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid code, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneChangedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"+411234567",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusherWithCreationDateNow(
|
||||||
|
user.NewHumanPhoneCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneVerifiedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
code: "a",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
phoneVerificationCode: tt.fields.secretGenerator,
|
||||||
|
}
|
||||||
|
got, err := r.VerifyHumanPhone(tt.args.ctx, tt.args.userID, tt.args.code, tt.args.resourceOwner)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
secretGenerator crypto.Generator
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
userID string
|
||||||
|
resourceOwner string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "userid missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "phone already verified, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneChangedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"+411234567",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneVerifiedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "new code, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneChangedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"+411234567",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
phoneVerificationCode: tt.fields.secretGenerator,
|
||||||
|
}
|
||||||
|
got, err := r.CreateHumanPhoneVerificationCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
userID string
|
||||||
|
resourceOwner string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "userid missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code sent, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneChangedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"+411234567",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneCodeSentEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
err := r.HumanPhoneVerificationCodeSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_RemoveHumanPhone(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
userID string
|
||||||
|
resourceOwner string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.ObjectDetails
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "userid missing, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "phone not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove phone, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusherWithCreationDateNow(
|
||||||
|
user.NewHumanPhoneChangedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"+411234567",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanPhoneRemovedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.RemoveHumanPhone(tt.args.ctx, tt.args.userID, tt.args.resourceOwner)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,9 +18,13 @@ func (c *Commands) ChangeHumanProfile(ctx context.Context, profile *domain.Profi
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if existingProfile.UserState == domain.UserStateUnspecified || existingProfile.UserState == domain.UserStateDeleted {
|
if existingProfile.UserState == domain.UserStateUnspecified || existingProfile.UserState == domain.UserStateDeleted {
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-3M9sd", "Errors.User.Profile.NotFound")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3M9sd", "Errors.User.Profile.NotFound")
|
||||||
|
}
|
||||||
|
userAgg := UserAggregateFromWriteModel(&existingProfile.WriteModel)
|
||||||
|
changedEvent, hasChanged, err := existingProfile.NewChangedEvent(ctx, userAgg, profile.FirstName, profile.LastName, profile.NickName, profile.DisplayName, profile.PreferredLanguage, profile.Gender)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
changedEvent, hasChanged := existingProfile.NewChangedEvent(ctx, profile.FirstName, profile.LastName, profile.NickName, profile.DisplayName, profile.PreferredLanguage, profile.Gender)
|
|
||||||
if !hasChanged {
|
if !hasChanged {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M0fs", "Errors.User.Profile.NotChanged")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M0fs", "Errors.User.Profile.NotChanged")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,39 +89,41 @@ func (wm *HumanProfileWriteModel) Query() *eventstore.SearchQueryBuilder {
|
|||||||
|
|
||||||
func (wm *HumanProfileWriteModel) NewChangedEvent(
|
func (wm *HumanProfileWriteModel) NewChangedEvent(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
aggregate *eventstore.Aggregate,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
nickName,
|
nickName,
|
||||||
displayName string,
|
displayName string,
|
||||||
preferredLanguage language.Tag,
|
preferredLanguage language.Tag,
|
||||||
gender domain.Gender,
|
gender domain.Gender,
|
||||||
) (*user.HumanProfileChangedEvent, bool) {
|
) (*user.HumanProfileChangedEvent, bool, error) {
|
||||||
hasChanged := false
|
changes := make([]user.ProfileChanges, 0)
|
||||||
changedEvent := user.NewHumanProfileChangedEvent(ctx, UserAggregateFromWriteModel(&wm.WriteModel))
|
var err error
|
||||||
|
|
||||||
if wm.FirstName != firstName {
|
if wm.FirstName != firstName {
|
||||||
hasChanged = true
|
changes = append(changes, user.ChangeFirstName(firstName))
|
||||||
changedEvent.FirstName = firstName
|
|
||||||
}
|
}
|
||||||
if wm.LastName != lastName {
|
if wm.LastName != lastName {
|
||||||
hasChanged = true
|
changes = append(changes, user.ChangeLastName(lastName))
|
||||||
changedEvent.LastName = lastName
|
|
||||||
}
|
}
|
||||||
if wm.NickName != nickName {
|
if wm.NickName != nickName {
|
||||||
hasChanged = true
|
changes = append(changes, user.ChangeNickName(nickName))
|
||||||
changedEvent.NickName = &nickName
|
|
||||||
}
|
}
|
||||||
if wm.DisplayName != displayName {
|
if wm.DisplayName != displayName {
|
||||||
hasChanged = true
|
changes = append(changes, user.ChangeDisplayName(displayName))
|
||||||
changedEvent.DisplayName = &displayName
|
|
||||||
}
|
}
|
||||||
if wm.PreferredLanguage != preferredLanguage {
|
if wm.PreferredLanguage != preferredLanguage {
|
||||||
hasChanged = true
|
changes = append(changes, user.ChangePreferredLanguage(preferredLanguage))
|
||||||
changedEvent.PreferredLanguage = &preferredLanguage
|
|
||||||
}
|
}
|
||||||
if gender.Valid() && wm.Gender != gender {
|
if wm.Gender != gender {
|
||||||
hasChanged = true
|
changes = append(changes, user.ChangeGender(gender))
|
||||||
changedEvent.Gender = &gender
|
|
||||||
}
|
}
|
||||||
|
if len(changes) == 0 {
|
||||||
return changedEvent, hasChanged
|
return nil, false, nil
|
||||||
|
}
|
||||||
|
changeEvent, err := user.NewHumanProfileChangedEvent(ctx, aggregate, changes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
return changeEvent, true, nil
|
||||||
}
|
}
|
||||||
|
|||||||
207
internal/command/user_human_profile_test.go
Normal file
207
internal/command/user_human_profile_test.go
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/caos/zitadel/internal/repository/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_ChangeHumanProfile(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
address *domain.Profile
|
||||||
|
resourceOwner string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.Profile
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "user not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
address: &domain.Profile{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
FirstName: "firstname",
|
||||||
|
LastName: "lastname",
|
||||||
|
NickName: "nickname",
|
||||||
|
DisplayName: "displayname",
|
||||||
|
PreferredLanguage: language.German,
|
||||||
|
Gender: domain.GenderFemale,
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "profile not changed, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderFemale,
|
||||||
|
"email",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
address: &domain.Profile{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
FirstName: "firstname",
|
||||||
|
LastName: "lastname",
|
||||||
|
NickName: "nickname",
|
||||||
|
DisplayName: "displayname",
|
||||||
|
PreferredLanguage: language.German,
|
||||||
|
Gender: domain.GenderFemale,
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "profile changed, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
newProfileChangedEvent(context.Background(),
|
||||||
|
"user1", "org1",
|
||||||
|
"firstname2",
|
||||||
|
"lastname2",
|
||||||
|
"nickname2",
|
||||||
|
"displayname2",
|
||||||
|
language.English,
|
||||||
|
domain.GenderMale,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
address: &domain.Profile{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
FirstName: "firstname2",
|
||||||
|
LastName: "lastname2",
|
||||||
|
NickName: "nickname2",
|
||||||
|
DisplayName: "displayname2",
|
||||||
|
PreferredLanguage: language.English,
|
||||||
|
Gender: domain.GenderMale,
|
||||||
|
},
|
||||||
|
resourceOwner: "org1",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.Profile{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
FirstName: "firstname2",
|
||||||
|
LastName: "lastname2",
|
||||||
|
NickName: "nickname2",
|
||||||
|
DisplayName: "displayname2",
|
||||||
|
PreferredLanguage: language.English,
|
||||||
|
Gender: domain.GenderMale,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.ChangeHumanProfile(tt.args.ctx, tt.args.address)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newProfileChangedEvent(ctx context.Context, userID, resourceOwner, fistName, lastName, nickName, displayName string, lang language.Tag, gender domain.Gender) *user.HumanProfileChangedEvent {
|
||||||
|
event, _ := user.NewHumanProfileChangedEvent(ctx,
|
||||||
|
&user.NewAggregate(userID, resourceOwner).Aggregate,
|
||||||
|
[]user.ProfileChanges{
|
||||||
|
user.ChangeFirstName(fistName),
|
||||||
|
user.ChangeLastName(lastName),
|
||||||
|
user.ChangeNickName(nickName),
|
||||||
|
user.ChangeDisplayName(displayName),
|
||||||
|
user.ChangePreferredLanguage(lang),
|
||||||
|
user.ChangeGender(gender),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return event
|
||||||
|
}
|
||||||
1605
internal/command/user_human_test.go
Normal file
1605
internal/command/user_human_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,21 +13,18 @@ func (c *Commands) AddMachine(ctx context.Context, orgID string, machine *domain
|
|||||||
if !machine.IsValid() {
|
if !machine.IsValid() {
|
||||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-bm9Ds", "Errors.User.Invalid")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-bm9Ds", "Errors.User.Invalid")
|
||||||
}
|
}
|
||||||
|
orgIAMPolicy, err := c.getOrgIAMPolicy(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-3M9fs", "Errors.Org.OrgIAMPolicy.NotFound")
|
||||||
|
}
|
||||||
|
if !orgIAMPolicy.UserLoginMustBeDomain {
|
||||||
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M0ds", "Errors.User.Invalid")
|
||||||
|
}
|
||||||
userID, err := c.idGenerator.Next()
|
userID, err := c.idGenerator.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
machine.AggregateID = userID
|
machine.AggregateID = userID
|
||||||
|
|
||||||
orgIAMPolicy, err := c.getOrgIAMPolicy(ctx, orgID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !orgIAMPolicy.UserLoginMustBeDomain {
|
|
||||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-6M0ds", "Errors.User.Invalid")
|
|
||||||
}
|
|
||||||
|
|
||||||
addedMachine := NewMachineWriteModel(machine.AggregateID, orgID)
|
addedMachine := NewMachineWriteModel(machine.AggregateID, orgID)
|
||||||
userAgg := UserAggregateFromWriteModel(&addedMachine.WriteModel)
|
userAgg := UserAggregateFromWriteModel(&addedMachine.WriteModel)
|
||||||
events, err := c.eventstore.PushEvents(ctx, user.NewMachineAddedEvent(
|
events, err := c.eventstore.PushEvents(ctx, user.NewMachineAddedEvent(
|
||||||
@@ -58,7 +55,10 @@ func (c *Commands) ChangeMachine(ctx context.Context, machine *domain.Machine) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
userAgg := UserAggregateFromWriteModel(&existingMachine.WriteModel)
|
userAgg := UserAggregateFromWriteModel(&existingMachine.WriteModel)
|
||||||
changedEvent, hasChanged := existingMachine.NewChangedEvent(ctx, userAgg, machine.Name, machine.Description)
|
changedEvent, hasChanged, err := existingMachine.NewChangedEvent(ctx, userAgg, machine.Name, machine.Description)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if !hasChanged {
|
if !hasChanged {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2n8vs", "Errors.User.NotChanged")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2n8vs", "Errors.User.NotChanged")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,16 +86,22 @@ func (wm *MachineWriteModel) NewChangedEvent(
|
|||||||
aggregate *eventstore.Aggregate,
|
aggregate *eventstore.Aggregate,
|
||||||
name,
|
name,
|
||||||
description string,
|
description string,
|
||||||
) (*user.MachineChangedEvent, bool) {
|
) (*user.MachineChangedEvent, bool, error) {
|
||||||
hasChanged := false
|
changes := make([]user.MachineChanges, 0)
|
||||||
changedEvent := user.NewMachineChangedEvent(ctx, aggregate)
|
var err error
|
||||||
|
|
||||||
if wm.Name != name {
|
if wm.Name != name {
|
||||||
hasChanged = true
|
changes = append(changes, user.ChangeName(name))
|
||||||
changedEvent.Name = &name
|
|
||||||
}
|
}
|
||||||
if wm.Description != description {
|
if wm.Description != description {
|
||||||
hasChanged = true
|
changes = append(changes, user.ChangeDescription(description))
|
||||||
changedEvent.Description = &description
|
|
||||||
}
|
}
|
||||||
return changedEvent, hasChanged
|
if len(changes) == 0 {
|
||||||
|
return nil, false, nil
|
||||||
|
}
|
||||||
|
changeEvent, err := user.NewMachineChangedEvent(ctx, aggregate, changes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
return changeEvent, true, nil
|
||||||
}
|
}
|
||||||
|
|||||||
350
internal/command/user_machine_test.go
Normal file
350
internal/command/user_machine_test.go
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/caos/zitadel/internal/id"
|
||||||
|
id_mock "github.com/caos/zitadel/internal/id/mock"
|
||||||
|
"github.com/caos/zitadel/internal/repository/org"
|
||||||
|
"github.com/caos/zitadel/internal/repository/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandSide_AddMachine(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
idGenerator id.Generator
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
machine *domain.Machine
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.Machine
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "user invalid, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
machine: &domain.Machine{
|
||||||
|
Username: "username",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "org policy not found, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
machine: &domain.Machine{
|
||||||
|
Username: "username",
|
||||||
|
Name: "name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "org policy global, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewOrgIAMPolicyAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
machine: &domain.Machine{
|
||||||
|
Username: "username",
|
||||||
|
Name: "name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add machine, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewOrgIAMPolicyAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewMachineAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"name",
|
||||||
|
"description",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
machine: &domain.Machine{
|
||||||
|
Username: "username",
|
||||||
|
Description: "description",
|
||||||
|
Name: "name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.Machine{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
Username: "username",
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
State: domain.UserStateActive,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
idGenerator: tt.fields.idGenerator,
|
||||||
|
}
|
||||||
|
got, err := r.AddMachine(tt.args.ctx, tt.args.orgID, tt.args.machine)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandSide_ChangeMachine(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
orgID string
|
||||||
|
machine *domain.Machine
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
want *domain.Machine
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "user invalid, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
machine: &domain.Machine{
|
||||||
|
Username: "username",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user not existing, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
machine: &domain.Machine{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
Username: "username",
|
||||||
|
Name: "name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsNotFound,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no changes, precondition error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewMachineAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"name",
|
||||||
|
"description",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
machine: &domain.Machine{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "change machine, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewMachineAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"name",
|
||||||
|
"description",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
newMachineChangedEvent(context.Background(), "user1", "org1", "name1", "description1"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
machine: &domain.Machine{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
},
|
||||||
|
Description: "description1",
|
||||||
|
Name: "name1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.Machine{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
Username: "username",
|
||||||
|
Name: "name1",
|
||||||
|
Description: "description1",
|
||||||
|
State: domain.UserStateActive,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := &Commands{
|
||||||
|
eventstore: tt.fields.eventstore,
|
||||||
|
}
|
||||||
|
got, err := r.ChangeMachine(tt.args.ctx, tt.args.machine)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMachineChangedEvent(ctx context.Context, userID, resourceOwner, name, description string) *user.MachineChangedEvent {
|
||||||
|
event, _ := user.NewMachineChangedEvent(ctx,
|
||||||
|
&user.NewAggregate(userID, resourceOwner).Aggregate,
|
||||||
|
[]user.MachineChanges{
|
||||||
|
user.ChangeName(name),
|
||||||
|
user.ChangeDescription(description),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return event
|
||||||
|
}
|
||||||
1275
internal/command/user_test.go
Normal file
1275
internal/command/user_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,8 @@ const (
|
|||||||
SecondFactorTypeUnspecified SecondFactorType = iota
|
SecondFactorTypeUnspecified SecondFactorType = iota
|
||||||
SecondFactorTypeOTP
|
SecondFactorTypeOTP
|
||||||
SecondFactorTypeU2F
|
SecondFactorTypeU2F
|
||||||
|
|
||||||
|
secondFactorCount
|
||||||
)
|
)
|
||||||
|
|
||||||
type MultiFactorType int32
|
type MultiFactorType int32
|
||||||
@@ -13,6 +15,8 @@ type MultiFactorType int32
|
|||||||
const (
|
const (
|
||||||
MultiFactorTypeUnspecified MultiFactorType = iota
|
MultiFactorTypeUnspecified MultiFactorType = iota
|
||||||
MultiFactorTypeU2FWithPIN
|
MultiFactorTypeU2FWithPIN
|
||||||
|
|
||||||
|
multiFactorCount
|
||||||
)
|
)
|
||||||
|
|
||||||
type FactorState int32
|
type FactorState int32
|
||||||
@@ -25,6 +29,14 @@ const (
|
|||||||
factorStateCount
|
factorStateCount
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (f SecondFactorType) Valid() bool {
|
||||||
|
return f > 0 && f < secondFactorCount
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f MultiFactorType) Valid() bool {
|
||||||
|
return f > 0 && f < multiFactorCount
|
||||||
|
}
|
||||||
|
|
||||||
func (f FactorState) Valid() bool {
|
func (f FactorState) Valid() bool {
|
||||||
return f >= 0 && f < factorStateCount
|
return f >= 0 && f < factorStateCount
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,12 +18,7 @@ type Human struct {
|
|||||||
*Email
|
*Email
|
||||||
*Phone
|
*Phone
|
||||||
*Address
|
*Address
|
||||||
ExternalIDPs []*ExternalIDP
|
ExternalIDPs []*ExternalIDP
|
||||||
OTP *OTP
|
|
||||||
U2FTokens []*WebAuthNToken
|
|
||||||
PasswordlessTokens []*WebAuthNToken
|
|
||||||
U2FLogins []*WebAuthNLogin
|
|
||||||
PasswordlessLogins []*WebAuthNLogin
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h Human) GetUsername() string {
|
func (h Human) GetUsername() string {
|
||||||
@@ -57,7 +52,7 @@ func (f Gender) Valid() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *Human) IsValid() bool {
|
func (u *Human) IsValid() bool {
|
||||||
return u.Profile != nil && u.FirstName != "" && u.LastName != "" && u.Email != nil && u.Email.IsValid() && u.Phone == nil || (u.Phone != nil && u.Phone.PhoneNumber != "" && u.Phone.IsValid())
|
return u.Profile != nil && u.Profile.IsValid() && u.Email != nil && u.Email.IsValid() && u.Phone == nil || (u.Phone != nil && u.Phone.PhoneNumber != "" && u.Phone.IsValid())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Human) CheckOrgIAMPolicy(policy *OrgIAMPolicy) error {
|
func (u *Human) CheckOrgIAMPolicy(policy *OrgIAMPolicy) error {
|
||||||
|
|||||||
@@ -3,9 +3,12 @@ package domain
|
|||||||
import (
|
import (
|
||||||
"github.com/caos/zitadel/internal/crypto"
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
|
es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||||
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var emailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
|
||||||
|
|
||||||
type Email struct {
|
type Email struct {
|
||||||
es_models.ObjectRoot
|
es_models.ObjectRoot
|
||||||
|
|
||||||
@@ -21,7 +24,7 @@ type EmailCode struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *Email) IsValid() bool {
|
func (e *Email) IsValid() bool {
|
||||||
return e.EmailAddress != ""
|
return e.EmailAddress != "" && emailRegex.MatchString(e.EmailAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEmailCode(emailGenerator crypto.Generator) (*EmailCode, error) {
|
func NewEmailCode(emailGenerator crypto.Generator) (*EmailCode, error) {
|
||||||
|
|||||||
74
internal/domain/human_email_test.go
Normal file
74
internal/domain/human_email_test.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEmailValid(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
email *Email
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
result bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty email, invalid",
|
||||||
|
args: args{
|
||||||
|
email: &Email{},
|
||||||
|
},
|
||||||
|
result: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only letters email, invalid",
|
||||||
|
args: args{
|
||||||
|
email: &Email{EmailAddress: "testemail"},
|
||||||
|
},
|
||||||
|
result: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nothing after @, invalid",
|
||||||
|
args: args{
|
||||||
|
email: &Email{EmailAddress: "testemail@"},
|
||||||
|
},
|
||||||
|
result: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "email, valid",
|
||||||
|
args: args{
|
||||||
|
email: &Email{EmailAddress: "testemail@gmail.com"},
|
||||||
|
},
|
||||||
|
result: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "email, valid",
|
||||||
|
args: args{
|
||||||
|
email: &Email{EmailAddress: "test.email@gmail.com"},
|
||||||
|
},
|
||||||
|
result: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "email, valid",
|
||||||
|
args: args{
|
||||||
|
email: &Email{EmailAddress: "test/email@gmail.com"},
|
||||||
|
},
|
||||||
|
result: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "email, valid",
|
||||||
|
args: args{
|
||||||
|
email: &Email{EmailAddress: "test/email@gmail.com"},
|
||||||
|
},
|
||||||
|
result: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := tt.args.email.IsValid()
|
||||||
|
if result != tt.result {
|
||||||
|
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ type ExternalIDP struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (idp *ExternalIDP) IsValid() bool {
|
func (idp *ExternalIDP) IsValid() bool {
|
||||||
return idp.AggregateID != "" && idp.IDPConfigID != "" && idp.ExternalUserID != ""
|
return idp.IDPConfigID != "" && idp.ExternalUserID != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExternalIDPState int32
|
type ExternalIDPState int32
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func (p *Phone) IsValid() bool {
|
|||||||
func (p *Phone) formatPhone() error {
|
func (p *Phone) formatPhone() error {
|
||||||
phoneNr, err := libphonenumber.Parse(p.PhoneNumber, defaultRegion)
|
phoneNr, err := libphonenumber.Parse(p.PhoneNumber, defaultRegion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-so0wa", "Errors.User.Phone.Invalid")
|
return caos_errs.ThrowInvalidArgument(nil, "EVENT-so0wa", "Errors.User.Phone.Invalid")
|
||||||
}
|
}
|
||||||
p.PhoneNumber = libphonenumber.Format(phoneNr, libphonenumber.E164)
|
p.PhoneNumber = libphonenumber.Format(phoneNr, libphonenumber.E164)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
107
internal/domain/human_phone_test.go
Normal file
107
internal/domain/human_phone_test.go
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFormatPhoneNumber(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
phone *Phone
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
result *Phone
|
||||||
|
errFunc func(err error) bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "invalid phone number",
|
||||||
|
args: args{
|
||||||
|
phone: &Phone{
|
||||||
|
PhoneNumber: "PhoneNumber",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errFunc: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "format phone 071...",
|
||||||
|
args: args{
|
||||||
|
phone: &Phone{
|
||||||
|
PhoneNumber: "0711234567",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: &Phone{
|
||||||
|
PhoneNumber: "+41711234567",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "format phone 0041...",
|
||||||
|
args: args{
|
||||||
|
phone: &Phone{
|
||||||
|
PhoneNumber: "0041711234567",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: &Phone{
|
||||||
|
PhoneNumber: "+41711234567",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "format phone 071 xxx xx xx",
|
||||||
|
args: args{
|
||||||
|
phone: &Phone{
|
||||||
|
PhoneNumber: "071 123 45 67",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: &Phone{
|
||||||
|
PhoneNumber: "+41711234567",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "format phone +4171 xxx xx xx",
|
||||||
|
args: args{
|
||||||
|
phone: &Phone{
|
||||||
|
PhoneNumber: "+4171 123 45 67",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: &Phone{
|
||||||
|
PhoneNumber: "+41711234567",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "format phone 004171 xxx xx xx",
|
||||||
|
args: args{
|
||||||
|
phone: &Phone{
|
||||||
|
PhoneNumber: "004171 123 45 67",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: &Phone{
|
||||||
|
PhoneNumber: "+41711234567",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "format non swiss phone 004371 xxx xx xx",
|
||||||
|
args: args{
|
||||||
|
phone: &Phone{
|
||||||
|
PhoneNumber: "004371 123 45 67",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: &Phone{
|
||||||
|
PhoneNumber: "+43711234567",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
err := tt.args.phone.formatPhone()
|
||||||
|
|
||||||
|
if tt.errFunc == nil && tt.result.PhoneNumber != tt.args.phone.PhoneNumber {
|
||||||
|
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.args.phone.PhoneNumber, tt.result.PhoneNumber)
|
||||||
|
}
|
||||||
|
if tt.errFunc != nil && !tt.errFunc(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,5 +20,5 @@ func (m Machine) GetState() UserState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sa *Machine) IsValid() bool {
|
func (sa *Machine) IsValid() bool {
|
||||||
return sa.Name != ""
|
return sa.Name != "" && sa.Username != ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ type OrgDomain struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (domain *OrgDomain) IsValid() bool {
|
func (domain *OrgDomain) IsValid() bool {
|
||||||
return domain.AggregateID != "" && domain.Domain != ""
|
return domain.Domain != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (domain *OrgDomain) GenerateVerificationCode(codeGenerator crypto.Generator) (string, error) {
|
func (domain *OrgDomain) GenerateVerificationCode(codeGenerator crypto.Generator) (string, error) {
|
||||||
|
|||||||
@@ -13,3 +13,7 @@ const (
|
|||||||
func (f PolicyState) Valid() bool {
|
func (f PolicyState) Valid() bool {
|
||||||
return f >= 0 && f < policyStateCount
|
return f >= 0 && f < policyStateCount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s PolicyState) Exists() bool {
|
||||||
|
return s != PolicyStateUnspecified && s != PolicyStateRemoved
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ type IDPProvider struct {
|
|||||||
IDPState IDPConfigState
|
IDPState IDPConfigState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p IDPProvider) IsValid() bool {
|
||||||
|
return p.IDPConfigID != ""
|
||||||
|
}
|
||||||
|
|
||||||
type PasswordlessType int32
|
type PasswordlessType int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -22,3 +22,7 @@ const (
|
|||||||
func (f UserState) Valid() bool {
|
func (f UserState) Valid() bool {
|
||||||
return f >= 0 && f < userStateCount
|
return f >= 0 && f < userStateCount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s UserState) Exists() bool {
|
||||||
|
return s != UserStateUnspecified && s != UserStateDeleted
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func GetIDPProviderByAggregateIDAndConfigID(db *gorm.DB, table, aggregateID, idp
|
|||||||
query := repository.PrepareGetByQuery(table, aggIDQuery, idpConfigIDQuery)
|
query := repository.PrepareGetByQuery(table, aggIDQuery, idpConfigIDQuery)
|
||||||
err := query(db, policy)
|
err := query(db, policy)
|
||||||
if caos_errs.IsNotFound(err) {
|
if caos_errs.IsNotFound(err) {
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "VIEW-Skvi8", "Errors.IAM.LoginPolicy.IdpProviderNotExisting")
|
return nil, caos_errs.ThrowNotFound(nil, "VIEW-Skvi8", "Errors.IAM.LoginPolicy.IDP.NotExisting")
|
||||||
}
|
}
|
||||||
return policy, err
|
return policy, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ func IDPByID(db *gorm.DB, table, idpID string) (*model.IDPConfigView, error) {
|
|||||||
query := repository.PrepareGetByQuery(table, idpIDQuery)
|
query := repository.PrepareGetByQuery(table, idpIDQuery)
|
||||||
err := query(db, idp)
|
err := query(db, idp)
|
||||||
if caos_errs.IsNotFound(err) {
|
if caos_errs.IsNotFound(err) {
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "VIEW-Ahq2s", "Errors.IAM.IdpNotExisting")
|
return nil, caos_errs.ThrowNotFound(nil, "VIEW-Ahq2s", "Errors.IDP.NotExisting")
|
||||||
}
|
}
|
||||||
return idp, err
|
return idp, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ func (i *ExternalIDP) getOrgIDPConfig(ctx context.Context, aggregateID, idpConfi
|
|||||||
if _, i := existing.GetIDP(idpConfigID); i != nil {
|
if _, i := existing.GetIDP(idpConfigID); i != nil {
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
return nil, caos_errs.ThrowNotFound(nil, "EVENT-22n7G", "Errors.Org.IdpNotExisting")
|
return nil, caos_errs.ThrowNotFound(nil, "EVENT-22n7G", "Errors.IDP.NotExisting")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ExternalIDP) getOrgByID(ctx context.Context, orgID string) (*org_model.Org, error) {
|
func (i *ExternalIDP) getOrgByID(ctx context.Context, orgID string) (*org_model.Org, error) {
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ func NewIdentityProviderAddedEvent(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
aggregate *eventstore.Aggregate,
|
aggregate *eventstore.Aggregate,
|
||||||
idpConfigID string,
|
idpConfigID string,
|
||||||
idpProviderType domain.IdentityProviderType,
|
|
||||||
) *IdentityProviderAddedEvent {
|
) *IdentityProviderAddedEvent {
|
||||||
|
|
||||||
return &IdentityProviderAddedEvent{
|
return &IdentityProviderAddedEvent{
|
||||||
@@ -33,7 +32,7 @@ func NewIdentityProviderAddedEvent(
|
|||||||
aggregate,
|
aggregate,
|
||||||
LoginPolicyIDPProviderAddedEventType),
|
LoginPolicyIDPProviderAddedEventType),
|
||||||
idpConfigID,
|
idpConfigID,
|
||||||
idpProviderType),
|
domain.IdentityProviderTypeSystem),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -247,14 +247,15 @@ func (e *DomainRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstr
|
|||||||
return []*eventstore.EventUniqueConstraint{NewRemoveOrgDomainUniqueConstraint(e.Domain)}
|
return []*eventstore.EventUniqueConstraint{NewRemoveOrgDomainUniqueConstraint(e.Domain)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDomainRemovedEvent(ctx context.Context, aggregate *eventstore.Aggregate, domain string) *DomainRemovedEvent {
|
func NewDomainRemovedEvent(ctx context.Context, aggregate *eventstore.Aggregate, domain string, verified bool) *DomainRemovedEvent {
|
||||||
return &DomainRemovedEvent{
|
return &DomainRemovedEvent{
|
||||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||||
ctx,
|
ctx,
|
||||||
aggregate,
|
aggregate,
|
||||||
OrgDomainRemovedEventType,
|
OrgDomainRemovedEventType,
|
||||||
),
|
),
|
||||||
Domain: domain,
|
Domain: domain,
|
||||||
|
isVerified: verified,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,14 +32,57 @@ func (e *HumanAddressChangedEvent) UniqueConstraints() []*eventstore.EventUnique
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHumanAddressChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *HumanAddressChangedEvent {
|
func NewAddressChangedEvent(
|
||||||
return &HumanAddressChangedEvent{
|
ctx context.Context,
|
||||||
|
aggregate *eventstore.Aggregate,
|
||||||
|
changes []AddressChanges,
|
||||||
|
) (*HumanAddressChangedEvent, error) {
|
||||||
|
if len(changes) == 0 {
|
||||||
|
return nil, errors.ThrowPreconditionFailed(nil, "USER-3n8fs", "Errors.NoChangesFound")
|
||||||
|
}
|
||||||
|
changeEvent := &HumanAddressChangedEvent{
|
||||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||||
ctx,
|
ctx,
|
||||||
aggregate,
|
aggregate,
|
||||||
HumanAddressChangedType,
|
HumanAddressChangedType,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
for _, change := range changes {
|
||||||
|
change(changeEvent)
|
||||||
|
}
|
||||||
|
return changeEvent, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddressChanges func(event *HumanAddressChangedEvent)
|
||||||
|
|
||||||
|
func ChangeCountry(country string) func(event *HumanAddressChangedEvent) {
|
||||||
|
return func(e *HumanAddressChangedEvent) {
|
||||||
|
e.Country = &country
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChangeLocality(locality string) func(event *HumanAddressChangedEvent) {
|
||||||
|
return func(e *HumanAddressChangedEvent) {
|
||||||
|
e.Locality = &locality
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChangePostalCode(code string) func(event *HumanAddressChangedEvent) {
|
||||||
|
return func(e *HumanAddressChangedEvent) {
|
||||||
|
e.PostalCode = &code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChangeRegion(region string) func(event *HumanAddressChangedEvent) {
|
||||||
|
return func(e *HumanAddressChangedEvent) {
|
||||||
|
e.Region = ®ion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChangeStreetAddress(street string) func(event *HumanAddressChangedEvent) {
|
||||||
|
return func(e *HumanAddressChangedEvent) {
|
||||||
|
e.StreetAddress = &street
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HumanAddressChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
|
func HumanAddressChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
|
||||||
|
|||||||
@@ -34,13 +34,14 @@ func (e *HumanEmailChangedEvent) UniqueConstraints() []*eventstore.EventUniqueCo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHumanEmailChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *HumanEmailChangedEvent {
|
func NewHumanEmailChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate, emailAddress string) *HumanEmailChangedEvent {
|
||||||
return &HumanEmailChangedEvent{
|
return &HumanEmailChangedEvent{
|
||||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||||
ctx,
|
ctx,
|
||||||
aggregate,
|
aggregate,
|
||||||
HumanEmailChangedType,
|
HumanEmailChangedType,
|
||||||
),
|
),
|
||||||
|
EmailAddress: emailAddress,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,13 +35,14 @@ func (e *HumanPhoneChangedEvent) UniqueConstraints() []*eventstore.EventUniqueCo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHumanPhoneChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *HumanPhoneChangedEvent {
|
func NewHumanPhoneChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate, phone string) *HumanPhoneChangedEvent {
|
||||||
return &HumanPhoneChangedEvent{
|
return &HumanPhoneChangedEvent{
|
||||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||||
ctx,
|
ctx,
|
||||||
aggregate,
|
aggregate,
|
||||||
HumanPhoneChangedType,
|
HumanPhoneChangedType,
|
||||||
),
|
),
|
||||||
|
PhoneNumber: phone,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,14 +35,63 @@ func (e *HumanProfileChangedEvent) UniqueConstraints() []*eventstore.EventUnique
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHumanProfileChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *HumanProfileChangedEvent {
|
func NewHumanProfileChangedEvent(
|
||||||
return &HumanProfileChangedEvent{
|
ctx context.Context,
|
||||||
|
aggregate *eventstore.Aggregate,
|
||||||
|
changes []ProfileChanges,
|
||||||
|
) (*HumanProfileChangedEvent, error) {
|
||||||
|
if len(changes) == 0 {
|
||||||
|
return nil, errors.ThrowPreconditionFailed(nil, "USER-33n8F", "Errors.NoChangesFound")
|
||||||
|
}
|
||||||
|
changeEvent := &HumanProfileChangedEvent{
|
||||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||||
ctx,
|
ctx,
|
||||||
aggregate,
|
aggregate,
|
||||||
HumanProfileChangedType,
|
HumanProfileChangedType,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
for _, change := range changes {
|
||||||
|
change(changeEvent)
|
||||||
|
}
|
||||||
|
return changeEvent, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProfileChanges func(event *HumanProfileChangedEvent)
|
||||||
|
|
||||||
|
func ChangeFirstName(firstName string) func(event *HumanProfileChangedEvent) {
|
||||||
|
return func(e *HumanProfileChangedEvent) {
|
||||||
|
e.FirstName = firstName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChangeLastName(lastName string) func(event *HumanProfileChangedEvent) {
|
||||||
|
return func(e *HumanProfileChangedEvent) {
|
||||||
|
e.LastName = lastName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChangeNickName(nickName string) func(event *HumanProfileChangedEvent) {
|
||||||
|
return func(e *HumanProfileChangedEvent) {
|
||||||
|
e.NickName = &nickName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChangeDisplayName(displayName string) func(event *HumanProfileChangedEvent) {
|
||||||
|
return func(e *HumanProfileChangedEvent) {
|
||||||
|
e.DisplayName = &displayName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChangePreferredLanguage(language language.Tag) func(event *HumanProfileChangedEvent) {
|
||||||
|
return func(e *HumanProfileChangedEvent) {
|
||||||
|
e.PreferredLanguage = &language
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChangeGender(gender domain.Gender) func(event *HumanProfileChangedEvent) {
|
||||||
|
return func(e *HumanProfileChangedEvent) {
|
||||||
|
e.Gender = &gender
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HumanProfileChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
|
func HumanProfileChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ type MachineAddedEvent struct {
|
|||||||
eventstore.BaseEvent `json:"-"`
|
eventstore.BaseEvent `json:"-"`
|
||||||
|
|
||||||
UserName string `json:"userName"`
|
UserName string `json:"userName"`
|
||||||
UserLoginMustBeDomain bool
|
userLoginMustBeDomain bool `json:"-"`
|
||||||
|
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
@@ -30,7 +30,7 @@ func (e *MachineAddedEvent) Data() interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *MachineAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
func (e *MachineAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||||
return []*eventstore.EventUniqueConstraint{NewAddUsernameUniqueConstraint(e.UserName, e.Aggregate().ResourceOwner, e.UserLoginMustBeDomain)}
|
return []*eventstore.EventUniqueConstraint{NewAddUsernameUniqueConstraint(e.UserName, e.Aggregate().ResourceOwner, e.userLoginMustBeDomain)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMachineAddedEvent(
|
func NewMachineAddedEvent(
|
||||||
@@ -50,7 +50,7 @@ func NewMachineAddedEvent(
|
|||||||
UserName: userName,
|
UserName: userName,
|
||||||
Name: name,
|
Name: name,
|
||||||
Description: description,
|
Description: description,
|
||||||
UserLoginMustBeDomain: userLoginMustBeDomain,
|
userLoginMustBeDomain: userLoginMustBeDomain,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,14 +86,36 @@ func (e *MachineChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConst
|
|||||||
func NewMachineChangedEvent(
|
func NewMachineChangedEvent(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
aggregate *eventstore.Aggregate,
|
aggregate *eventstore.Aggregate,
|
||||||
) *MachineChangedEvent {
|
changes []MachineChanges,
|
||||||
return &MachineChangedEvent{
|
) (*MachineChangedEvent, error) {
|
||||||
|
if len(changes) == 0 {
|
||||||
|
return nil, errors.ThrowPreconditionFailed(nil, "USER-3M9fs", "Errors.NoChangesFound")
|
||||||
|
}
|
||||||
|
changeEvent := &MachineChangedEvent{
|
||||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||||
ctx,
|
ctx,
|
||||||
aggregate,
|
aggregate,
|
||||||
MachineChangedEventType,
|
MachineChangedEventType,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
for _, change := range changes {
|
||||||
|
change(changeEvent)
|
||||||
|
}
|
||||||
|
return changeEvent, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type MachineChanges func(event *MachineChangedEvent)
|
||||||
|
|
||||||
|
func ChangeName(name string) func(event *MachineChangedEvent) {
|
||||||
|
return func(e *MachineChangedEvent) {
|
||||||
|
e.Name = &name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChangeDescription(description string) func(event *MachineChangedEvent) {
|
||||||
|
return func(e *MachineChangedEvent) {
|
||||||
|
e.Description = &description
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func MachineChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
|
func MachineChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ Errors:
|
|||||||
NoChangesFound: Keine Änderungen gefunden
|
NoChangesFound: Keine Änderungen gefunden
|
||||||
OriginNotAllowed: Dieser "Origin" ist nicht freigeschaltet
|
OriginNotAllowed: Dieser "Origin" ist nicht freigeschaltet
|
||||||
IDMissing: ID fehlt
|
IDMissing: ID fehlt
|
||||||
|
ResourceOwnerMissing: Organisation fehlt
|
||||||
User:
|
User:
|
||||||
NotFound: Benutzer konnte nicht gefunden werden
|
NotFound: Benutzer konnte nicht gefunden werden
|
||||||
AlreadyExists: Benutzer existierts bereits
|
AlreadyExists: Benutzer existierts bereits
|
||||||
@@ -225,14 +226,11 @@ Errors:
|
|||||||
IdpIsNotOIDC: IDP Konfiguration ist nicht vom Typ OIDC
|
IdpIsNotOIDC: IDP Konfiguration ist nicht vom Typ OIDC
|
||||||
LoginPolicyInvalid: Login Policy ist ungültig
|
LoginPolicyInvalid: Login Policy ist ungültig
|
||||||
LoginPolicyNotExisting: Login Policy nicht vorhanden
|
LoginPolicyNotExisting: Login Policy nicht vorhanden
|
||||||
IdpProviderInvalid: Idp Provider ist ungültig
|
|
||||||
LoginPolicy:
|
LoginPolicy:
|
||||||
NotFound: Default Login Policy konnte nicht gefunden
|
NotFound: Default Login Policy konnte nicht gefunden
|
||||||
NotChanged: Default Login Policy wurde nicht verändert
|
NotChanged: Default Login Policy wurde nicht verändert
|
||||||
NotExisting: Default Login Policy existiert nicht
|
NotExisting: Default Login Policy existiert nicht
|
||||||
AlreadyExists: Default Login Policy existiert bereits
|
AlreadyExists: Default Login Policy existiert bereits
|
||||||
IdpProviderAlreadyExisting: Idp Provider existiert bereits
|
|
||||||
IdpProviderNotExisting: Idp Provider existiert nicht
|
|
||||||
MFA:
|
MFA:
|
||||||
AlreadyExists: Multifaktor existiert bereits
|
AlreadyExists: Multifaktor existiert bereits
|
||||||
NotExisting: Multifaktor existiert nicht
|
NotExisting: Multifaktor existiert nicht
|
||||||
@@ -240,9 +238,9 @@ Errors:
|
|||||||
IDP:
|
IDP:
|
||||||
AlreadyExists: Identitäts Provider existiert bereits
|
AlreadyExists: Identitäts Provider existiert bereits
|
||||||
NotExisting: Identitäts Provider existiert nicht
|
NotExisting: Identitäts Provider existiert nicht
|
||||||
|
Invalid: Idp Provider ist ungültig
|
||||||
IDPConfig:
|
IDPConfig:
|
||||||
AlreadyExists: Identitäts Provider Konfiguration existiert bereits
|
AlreadyExists: Identitäts Provider Konfiguration existiert bereits
|
||||||
NotExisting: Identitäts Provider Konfiguration existiert nicht
|
|
||||||
NotInactive: Identitäts Provider Konfiguration nicht inaktive
|
NotInactive: Identitäts Provider Konfiguration nicht inaktive
|
||||||
NotActive: Identitäts Provider Konfiguration nicht aktive
|
NotActive: Identitäts Provider Konfiguration nicht aktive
|
||||||
LabelPolicy:
|
LabelPolicy:
|
||||||
@@ -298,6 +296,7 @@ Errors:
|
|||||||
AlreadyExists: Member existiert bereits
|
AlreadyExists: Member existiert bereits
|
||||||
IDPConfig:
|
IDPConfig:
|
||||||
AlreadyExists: IDP Konfiguration mit diesem Name existiert bereits
|
AlreadyExists: IDP Konfiguration mit diesem Name existiert bereits
|
||||||
|
NotExisting: Identitäts Provider Konfiguration existiert nicht
|
||||||
Changes:
|
Changes:
|
||||||
NotFound: Es konnte kein Änderungsverlauf gefunden werden
|
NotFound: Es konnte kein Änderungsverlauf gefunden werden
|
||||||
Token:
|
Token:
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user