fix: import user, hide login name suffix (#1474)

* fix: import user, and label policy command side

* feat: Import user and hide loginname suffix (#1464)

* fix: import user

* fix: label policy

* fix: label policy

* fix: label policy

* fix: migrations

* fix: migrations

* fix: migrations

* fix: label policy

* loginSuffix in login ui

* suffix

* fix cursor on disabled user selection

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

(cherry picked from commit 03ddb8fc388494d6ec99b1db9e16d16c28ee9649)

* feat: Import user and hide loginname suffix (#1464)

* fix: import user

* fix: label policy

* fix: label policy

* fix: label policy

* fix: migrations

* fix: migrations

* fix: migrations

* fix: label policy

* loginSuffix in login ui

* suffix

* fix cursor on disabled user selection

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

(cherry picked from commit 03ddb8fc388494d6ec99b1db9e16d16c28ee9649)

* feat: Import user and hide loginname suffix (#1464)

* fix: import user

* fix: label policy

* fix: label policy

* fix: label policy

* fix: migrations

* fix: migrations

* fix: migrations

* fix: label policy

* loginSuffix in login ui

* suffix

* fix cursor on disabled user selection

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

(cherry picked from commit 03ddb8fc388494d6ec99b1db9e16d16c28ee9649)

* fix: label policy events

* loginname placeholder

* fix: tests

* fix: tests

* Update internal/command/iam_policy_label_model.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
Fabi 2021-03-25 14:41:07 +01:00 committed by GitHub
parent d7255130a4
commit 4d10f3e715
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 1444 additions and 309 deletions

View File

@ -9,5 +9,6 @@ func updateLabelPolicyToDomain(policy *admin_pb.UpdateLabelPolicyRequest) *domai
return &domain.LabelPolicy{ return &domain.LabelPolicy{
PrimaryColor: policy.PrimaryColor, PrimaryColor: policy.PrimaryColor,
SecondaryColor: policy.SecondaryColor, SecondaryColor: policy.SecondaryColor,
HideLoginNameSuffix: policy.HideLoginNameSuffix,
} }
} }

View File

@ -0,0 +1,64 @@
package management
import (
"context"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/grpc/object"
policy_grpc "github.com/caos/zitadel/internal/api/grpc/policy"
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
)
func (s *Server) GetLabelPolicy(ctx context.Context, req *mgmt_pb.GetLabelPolicyRequest) (*mgmt_pb.GetLabelPolicyResponse, error) {
policy, err := s.org.GetLabelPolicy(ctx)
if err != nil {
return nil, err
}
return &mgmt_pb.GetLabelPolicyResponse{Policy: policy_grpc.ModelLabelPolicyToPb(policy)}, nil
}
func (s *Server) GetDefaultLabelPolicy(ctx context.Context, req *mgmt_pb.GetDefaultLabelPolicyRequest) (*mgmt_pb.GetDefaultLabelPolicyResponse, error) {
policy, err := s.org.GetDefaultLabelPolicy(ctx)
if err != nil {
return nil, err
}
return &mgmt_pb.GetDefaultLabelPolicyResponse{Policy: policy_grpc.ModelLabelPolicyToPb(policy)}, nil
}
func (s *Server) AddCustomLabelPolicy(ctx context.Context, req *mgmt_pb.AddCustomLabelPolicyRequest) (*mgmt_pb.AddCustomLabelPolicyResponse, error) {
policy, err := s.command.AddLabelPolicy(ctx, authz.GetCtxData(ctx).OrgID, addLabelPolicyToDomain(req))
if err != nil {
return nil, err
}
return &mgmt_pb.AddCustomLabelPolicyResponse{
Details: object.AddToDetailsPb(
policy.Sequence,
policy.ChangeDate,
policy.ResourceOwner,
),
}, nil
}
func (s *Server) UpdateCustomLabelPolicy(ctx context.Context, req *mgmt_pb.UpdateCustomLabelPolicyRequest) (*mgmt_pb.UpdateCustomLabelPolicyResponse, error) {
policy, err := s.command.ChangeLabelPolicy(ctx, authz.GetCtxData(ctx).OrgID, updateLabelPolicyToDomain(req))
if err != nil {
return nil, err
}
return &mgmt_pb.UpdateCustomLabelPolicyResponse{
Details: object.ChangeToDetailsPb(
policy.Sequence,
policy.ChangeDate,
policy.ResourceOwner,
),
}, nil
}
func (s *Server) ResetLabelPolicyToDefault(ctx context.Context, req *mgmt_pb.ResetLabelPolicyToDefaultRequest) (*mgmt_pb.ResetLabelPolicyToDefaultResponse, error) {
objectDetails, err := s.command.RemoveLabelPolicy(ctx, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return &mgmt_pb.ResetLabelPolicyToDefaultResponse{
Details: object.DomainToChangeDetailsPb(objectDetails),
}, nil
}

View File

@ -0,0 +1,22 @@
package management
import (
"github.com/caos/zitadel/internal/domain"
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
)
func addLabelPolicyToDomain(p *mgmt_pb.AddCustomLabelPolicyRequest) *domain.LabelPolicy {
return &domain.LabelPolicy{
PrimaryColor: p.PrimaryColor,
SecondaryColor: p.SecondaryColor,
HideLoginNameSuffix: p.HideLoginNameSuffix,
}
}
func updateLabelPolicyToDomain(p *mgmt_pb.UpdateCustomLabelPolicyRequest) *domain.LabelPolicy {
return &domain.LabelPolicy{
PrimaryColor: p.PrimaryColor,
SecondaryColor: p.SecondaryColor,
HideLoginNameSuffix: p.HideLoginNameSuffix,
}
}

View File

@ -87,6 +87,21 @@ func (s *Server) AddHumanUser(ctx context.Context, req *mgmt_pb.AddHumanUserRequ
}, nil }, nil
} }
func (s *Server) ImportHumanUser(ctx context.Context, req *mgmt_pb.ImportHumanUserRequest) (*mgmt_pb.ImportHumanUserResponse, error) {
human, err := s.command.ImportHuman(ctx, authz.GetCtxData(ctx).OrgID, ImportHumanUserRequestToDomain(req))
if err != nil {
return nil, err
}
return &mgmt_pb.ImportHumanUserResponse{
UserId: human.AggregateID,
Details: obj_grpc.AddToDetailsPb(
human.Sequence,
human.ChangeDate,
human.ResourceOwner,
),
}, nil
}
func (s *Server) AddMachineUser(ctx context.Context, req *mgmt_pb.AddMachineUserRequest) (*mgmt_pb.AddMachineUserResponse, error) { func (s *Server) AddMachineUser(ctx context.Context, req *mgmt_pb.AddMachineUserRequest) (*mgmt_pb.AddMachineUserResponse, error) {
machine, err := s.command.AddMachine(ctx, authz.GetCtxData(ctx).OrgID, AddMachineUserRequestToDomain(req)) machine, err := s.command.AddMachine(ctx, authz.GetCtxData(ctx).OrgID, AddMachineUserRequestToDomain(req))
if err != nil { if err != nil {

View File

@ -69,6 +69,38 @@ func AddHumanUserRequestToDomain(req *mgmt_pb.AddHumanUserRequest) *domain.Human
return h return h
} }
func ImportHumanUserRequestToDomain(req *mgmt_pb.ImportHumanUserRequest) *domain.Human {
h := &domain.Human{
Username: req.UserName,
}
preferredLanguage, err := language.Parse(req.Profile.PreferredLanguage)
logging.Log("MANAG-3GUFJ").OnError(err).Debug("language malformed")
h.Profile = &domain.Profile{
FirstName: req.Profile.FirstName,
LastName: req.Profile.LastName,
NickName: req.Profile.NickName,
DisplayName: req.Profile.DisplayName,
PreferredLanguage: preferredLanguage,
Gender: user_grpc.GenderToDomain(req.Profile.Gender),
}
h.Email = &domain.Email{
EmailAddress: req.Email.Email,
IsEmailVerified: req.Email.IsEmailVerified,
}
if req.Phone != nil {
h.Phone = &domain.Phone{
PhoneNumber: req.Phone.Phone,
IsPhoneVerified: req.Phone.IsPhoneVerified,
}
}
if req.Password != "" {
h.Password = &domain.Password{SecretString: req.Password}
h.Password.ChangeRequired = true
}
return h
}
func AddMachineUserRequestToDomain(req *mgmt_pb.AddMachineUserRequest) *domain.Machine { func AddMachineUserRequestToDomain(req *mgmt_pb.AddMachineUserRequest) *domain.Machine {
return &domain.Machine{ return &domain.Machine{
Username: req.UserName, Username: req.UserName,

View File

@ -11,6 +11,7 @@ func ModelLabelPolicyToPb(policy *model.LabelPolicyView) *policy_pb.LabelPolicy
IsDefault: policy.Default, IsDefault: policy.Default,
PrimaryColor: policy.PrimaryColor, PrimaryColor: policy.PrimaryColor,
SecondaryColor: policy.SecondaryColor, SecondaryColor: policy.SecondaryColor,
HideLoginNameSuffix: policy.HideLoginNameSuffix,
Details: object.ToViewDetailsPb( Details: object.ToViewDetailsPb(
policy.Sequence, policy.Sequence,
policy.CreationDate, policy.CreationDate,

View File

@ -244,7 +244,7 @@ func (repo *AuthRequestRepo) SelectUser(ctx context.Context, id, userID, userAge
if request.RequestedOrgID != "" && request.RequestedOrgID != user.ResourceOwner { if request.RequestedOrgID != "" && request.RequestedOrgID != user.ResourceOwner {
return errors.ThrowPreconditionFailed(nil, "EVENT-fJe2a", "Errors.User.NotAllowedOrg") return errors.ThrowPreconditionFailed(nil, "EVENT-fJe2a", "Errors.User.NotAllowedOrg")
} }
request.SetUserInfo(user.ID, user.PreferredLoginName, user.DisplayName, user.ResourceOwner) request.SetUserInfo(user.ID, user.UserName, user.PreferredLoginName, user.DisplayName, user.ResourceOwner)
return repo.AuthRequests.UpdateAuthRequest(ctx, request) return repo.AuthRequests.UpdateAuthRequest(ctx, request)
} }
@ -425,21 +425,30 @@ func (repo *AuthRequestRepo) fillLoginPolicy(ctx context.Context, request *domai
orgID = repo.IAMID orgID = repo.IAMID
} }
policy, idpProviders, err := repo.getLoginPolicyAndIDPProviders(ctx, orgID) loginPolicy, idpProviders, err := repo.getLoginPolicyAndIDPProviders(ctx, orgID)
if err != nil { if err != nil {
return err return err
} }
request.LoginPolicy = policy request.LoginPolicy = loginPolicy
if idpProviders != nil { if idpProviders != nil {
request.AllowedExternalIDPs = idpProviders request.AllowedExternalIDPs = idpProviders
} }
labelPolicy, err := repo.getLabelPolicy(ctx, orgID)
if err != nil {
return err
}
request.LabelPolicy = labelPolicy
return nil return nil
} }
func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *domain.AuthRequest, loginName string) (err error) { func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *domain.AuthRequest, loginName string) (err error) {
user := new(user_view_model.UserView) user := new(user_view_model.UserView)
if request.RequestedOrgID != "" { if request.RequestedOrgID != "" {
user, err = repo.View.UserByLoginNameAndResourceOwner(loginName, request.RequestedOrgID) preferredLoginName := loginName
if request.RequestedOrgID != "" {
preferredLoginName += "@" + request.RequestedPrimaryDomain
}
user, err = repo.View.UserByLoginNameAndResourceOwner(preferredLoginName, request.RequestedOrgID)
} else { } else {
user, err = repo.View.UserByLoginName(loginName) user, err = repo.View.UserByLoginName(loginName)
if err == nil { if err == nil {
@ -453,7 +462,7 @@ func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *domain
return err return err
} }
request.SetUserInfo(user.ID, loginName, "", user.ResourceOwner) request.SetUserInfo(user.ID, loginName, user.PreferredLoginName, "", user.ResourceOwner)
return nil return nil
} }
@ -496,7 +505,7 @@ func (repo *AuthRequestRepo) checkExternalUserLogin(request *domain.AuthRequest,
if err != nil { if err != nil {
return err return err
} }
request.SetUserInfo(externalIDP.UserID, "", "", externalIDP.ResourceOwner) request.SetUserInfo(externalIDP.UserID, "", "", "", externalIDP.ResourceOwner)
return nil return nil
} }
@ -599,6 +608,7 @@ func (repo *AuthRequestRepo) usersForUserSelection(request *domain.AuthRequest)
users[i] = domain.UserSelection{ users[i] = domain.UserSelection{
UserID: session.UserID, UserID: session.UserID,
DisplayName: session.DisplayName, DisplayName: session.DisplayName,
UserName: session.UserName,
LoginName: session.LoginName, LoginName: session.LoginName,
UserSessionState: auth_req_model.UserSessionStateToDomain(session.State), UserSessionState: auth_req_model.UserSessionStateToDomain(session.State),
SelectionPossible: request.RequestedOrgID == "" || request.RequestedOrgID == session.ResourceOwner, SelectionPossible: request.RequestedOrgID == "" || request.RequestedOrgID == session.ResourceOwner,
@ -695,6 +705,21 @@ func (repo *AuthRequestRepo) getLoginPolicy(ctx context.Context, orgID string) (
return iam_es_model.LoginPolicyViewToModel(policy), err return iam_es_model.LoginPolicyViewToModel(policy), err
} }
func (repo *AuthRequestRepo) getLabelPolicy(ctx context.Context, orgID string) (*domain.LabelPolicy, error) {
policy, err := repo.View.LabelPolicyByAggregateID(orgID)
if errors.IsNotFound(err) {
policy, err = repo.View.LabelPolicyByAggregateID(repo.IAMID)
if err != nil {
return nil, err
}
policy.Default = true
}
if err != nil {
return nil, err
}
return policy.ToDomain(), err
}
func setOrgID(orgViewProvider orgViewProvider, request *domain.AuthRequest) error { func setOrgID(orgViewProvider orgViewProvider, request *domain.AuthRequest) error {
primaryDomain := request.GetScopeOrgPrimaryDomain() primaryDomain := request.GetScopeOrgPrimaryDomain()
if primaryDomain == "" { if primaryDomain == "" {
@ -707,6 +732,7 @@ func setOrgID(orgViewProvider orgViewProvider, request *domain.AuthRequest) erro
} }
request.RequestedOrgID = org.ID request.RequestedOrgID = org.ID
request.RequestedOrgName = org.Name request.RequestedOrgName = org.Name
request.RequestedPrimaryDomain = primaryDomain
return nil return nil
} }

View File

@ -67,6 +67,7 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
newOrgIAMPolicy( newOrgIAMPolicy(
handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount, es}),
newProjectRole(handler{view, bulkLimit, configs.cycleDuration("ProjectRole"), errorCount, es}), newProjectRole(handler{view, bulkLimit, configs.cycleDuration("ProjectRole"), errorCount, es}),
newLabelPolicy(handler{view, bulkLimit, configs.cycleDuration("LabelPolicy"), errorCount, es}),
} }
} }

View File

@ -0,0 +1,104 @@
package handler
import (
"github.com/caos/logging"
v1 "github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/eventstore/v1/models"
es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/eventstore/v1/query"
"github.com/caos/zitadel/internal/eventstore/v1/spooler"
iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
iam_model "github.com/caos/zitadel/internal/iam/repository/view/model"
"github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
)
const (
labelPolicyTable = "auth.label_policies"
)
type LabelPolicy struct {
handler
subscription *v1.Subscription
}
func newLabelPolicy(handler handler) *LabelPolicy {
h := &LabelPolicy{
handler: handler,
}
h.subscribe()
return h
}
func (m *LabelPolicy) subscribe() {
m.subscription = m.es.Subscribe(m.AggregateTypes()...)
go func() {
for event := range m.subscription.Events {
query.ReduceEvent(m, event)
}
}()
}
func (m *LabelPolicy) ViewModel() string {
return labelPolicyTable
}
func (_ *LabelPolicy) AggregateTypes() []models.AggregateType {
return []models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate}
}
func (m *LabelPolicy) CurrentSequence() (uint64, error) {
sequence, err := m.view.GetLatestLabelPolicySequence()
if err != nil {
return 0, err
}
return sequence.CurrentSequence, nil
}
func (m *LabelPolicy) EventQuery() (*models.SearchQuery, error) {
sequence, err := m.view.GetLatestLabelPolicySequence()
if err != nil {
return nil, err
}
return es_models.NewSearchQuery().
AggregateTypeFilter(m.AggregateTypes()...).
LatestSequenceFilter(sequence.CurrentSequence), nil
}
func (m *LabelPolicy) Reduce(event *models.Event) (err error) {
switch event.AggregateType {
case model.OrgAggregate, iam_es_model.IAMAggregate:
err = m.processLabelPolicy(event)
}
return err
}
func (m *LabelPolicy) processLabelPolicy(event *models.Event) (err error) {
policy := new(iam_model.LabelPolicyView)
switch event.Type {
case iam_es_model.LabelPolicyAdded, model.LabelPolicyAdded:
err = policy.AppendEvent(event)
case iam_es_model.LabelPolicyChanged, model.LabelPolicyChanged:
policy, err = m.view.LabelPolicyByAggregateID(event.AggregateID)
if err != nil {
return err
}
err = policy.AppendEvent(event)
default:
return m.view.ProcessedLabelPolicySequence(event)
}
if err != nil {
return err
}
return m.view.PutLabelPolicy(policy, event)
}
func (m *LabelPolicy) OnError(event *models.Event, err error) error {
logging.LogWithFields("SPOOL-4Djo9", "id", event.AggregateID).WithError(err).Warn("something went wrong in label policy handler")
return spooler.HandleError(event, err, m.view.GetLatestLabelPolicyFailedEvent, m.view.ProcessedLabelPolicyFailedEvent, m.view.ProcessedLabelPolicySequence, m.errorCountUntilSkip)
}
func (m *LabelPolicy) OnSuccess() error {
return spooler.HandleSuccess(m.view.UpdateLabelPolicySpoolerRunTimestamp)
}

View File

@ -0,0 +1,53 @@
package view
import (
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/iam/repository/view"
"github.com/caos/zitadel/internal/iam/repository/view/model"
global_view "github.com/caos/zitadel/internal/view/repository"
)
const (
labelPolicyTable = "auth.label_policies"
)
func (v *View) LabelPolicyByAggregateID(aggregateID string) (*model.LabelPolicyView, error) {
return view.GetLabelPolicyByAggregateID(v.Db, labelPolicyTable, aggregateID)
}
func (v *View) PutLabelPolicy(policy *model.LabelPolicyView, event *models.Event) error {
err := view.PutLabelPolicy(v.Db, labelPolicyTable, policy)
if err != nil {
return err
}
return v.ProcessedLabelPolicySequence(event)
}
func (v *View) DeleteLabelPolicy(aggregateID string, event *models.Event) error {
err := view.DeleteLabelPolicy(v.Db, labelPolicyTable, aggregateID)
if err != nil && !errors.IsNotFound(err) {
return err
}
return v.ProcessedLabelPolicySequence(event)
}
func (v *View) GetLatestLabelPolicySequence() (*global_view.CurrentSequence, error) {
return v.latestSequence(labelPolicyTable)
}
func (v *View) ProcessedLabelPolicySequence(event *models.Event) error {
return v.saveCurrentSequence(labelPolicyTable, event)
}
func (v *View) UpdateLabelPolicySpoolerRunTimestamp() error {
return v.updateSpoolerRunSequence(labelPolicyTable)
}
func (v *View) GetLatestLabelPolicyFailedEvent(sequence uint64) (*global_view.FailedEvent, error) {
return v.latestFailedEvent(labelPolicyTable, sequence)
}
func (v *View) ProcessedLabelPolicyFailedEvent(failedEvent *global_view.FailedEvent) error {
return v.saveFailedEvent(failedEvent)
}

View File

@ -29,11 +29,13 @@ type AuthRequest struct {
levelOfAssurance LevelOfAssurance levelOfAssurance LevelOfAssurance
UserID string UserID string
UserName string
LoginName string LoginName string
DisplayName string DisplayName string
UserOrgID string UserOrgID string
RequestedOrgID string RequestedOrgID string
RequestedOrgName string RequestedOrgName string
RequestedPrimaryDomain string
SelectedIDPConfigID string SelectedIDPConfigID string
LinkingUsers []*ExternalUser LinkingUsers []*ExternalUser
PossibleSteps []NextStep PossibleSteps []NextStep
@ -43,6 +45,7 @@ type AuthRequest struct {
AuthTime time.Time AuthTime time.Time
Code string Code string
LoginPolicy *model.LoginPolicyView LoginPolicy *model.LoginPolicyView
LabelPolicy *model.LabelPolicyView
AllowedExternalIDPs []*model.IDPProviderView AllowedExternalIDPs []*model.IDPProviderView
} }
@ -123,8 +126,9 @@ func (a *AuthRequest) WithCurrentInfo(info *BrowserInfo) *AuthRequest {
return a return a
} }
func (a *AuthRequest) SetUserInfo(userID, loginName, displayName, userOrgID string) { func (a *AuthRequest) SetUserInfo(userID, userName, loginName, displayName, userOrgID string) {
a.UserID = userID a.UserID = userID
a.UserName = userName
a.LoginName = loginName a.LoginName = loginName
a.DisplayName = displayName a.DisplayName = displayName
a.UserOrgID = userOrgID a.UserOrgID = userOrgID

View File

@ -54,6 +54,7 @@ func (s *SelectUserStep) Type() NextStepType {
type UserSelection struct { type UserSelection struct {
UserID string UserID string
DisplayName string DisplayName string
UserName string
LoginName string LoginName string
UserSessionState UserSessionState UserSessionState UserSessionState
SelectionPossible bool SelectionPossible bool

View File

@ -49,6 +49,7 @@ func writeModelToLabelPolicy(wm *LabelPolicyWriteModel) *domain.LabelPolicy {
ObjectRoot: writeModelToObjectRoot(wm.WriteModel), ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
PrimaryColor: wm.PrimaryColor, PrimaryColor: wm.PrimaryColor,
SecondaryColor: wm.SecondaryColor, SecondaryColor: wm.SecondaryColor,
HideLoginNameSuffix: wm.HideLoginNameSuffix,
} }
} }

View File

@ -40,7 +40,7 @@ func (c *Commands) addDefaultLabelPolicy(ctx context.Context, iamAgg *eventstore
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LabelPolicy.AlreadyExists") return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LabelPolicy.AlreadyExists")
} }
return iam_repo.NewLabelPolicyAddedEvent(ctx, iamAgg, policy.PrimaryColor, policy.SecondaryColor), nil return iam_repo.NewLabelPolicyAddedEvent(ctx, iamAgg, policy.PrimaryColor, policy.SecondaryColor, policy.HideLoginNameSuffix), nil
} }
@ -57,7 +57,7 @@ func (c *Commands) ChangeDefaultLabelPolicy(ctx context.Context, policy *domain.
return nil, caos_errs.ThrowNotFound(nil, "IAM-0K9dq", "Errors.IAM.LabelPolicy.NotFound") return nil, caos_errs.ThrowNotFound(nil, "IAM-0K9dq", "Errors.IAM.LabelPolicy.NotFound")
} }
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel) iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, iamAgg, policy.PrimaryColor, policy.SecondaryColor) changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, iamAgg, policy.PrimaryColor, policy.SecondaryColor, policy.HideLoginNameSuffix)
if !hasChanged { if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged") return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
} }

View File

@ -53,6 +53,7 @@ func (wm *IAMLabelPolicyWriteModel) NewChangedEvent(
aggregate *eventstore.Aggregate, aggregate *eventstore.Aggregate,
primaryColor, primaryColor,
secondaryColor string, secondaryColor string,
hideLoginNameSuffix bool,
) (*iam.LabelPolicyChangedEvent, bool) { ) (*iam.LabelPolicyChangedEvent, bool) {
changes := make([]policy.LabelPolicyChanges, 0) changes := make([]policy.LabelPolicyChanges, 0)
if wm.PrimaryColor != primaryColor { if wm.PrimaryColor != primaryColor {
@ -61,6 +62,9 @@ func (wm *IAMLabelPolicyWriteModel) NewChangedEvent(
if wm.SecondaryColor != secondaryColor { if wm.SecondaryColor != secondaryColor {
changes = append(changes, policy.ChangeSecondaryColor(secondaryColor)) changes = append(changes, policy.ChangeSecondaryColor(secondaryColor))
} }
if wm.HideLoginNameSuffix != hideLoginNameSuffix {
changes = append(changes, policy.ChangeHideLoginNameSuffix(hideLoginNameSuffix))
}
if len(changes) == 0 { if len(changes) == 0 {
return nil, false return nil, false
} }

View File

@ -60,6 +60,7 @@ func TestCommandSide_AddDefaultLabelPolicy(t *testing.T) {
&iam.NewAggregate().Aggregate, &iam.NewAggregate().Aggregate,
"primary-color", "primary-color",
"secondary-color", "secondary-color",
true,
), ),
), ),
), ),
@ -70,6 +71,7 @@ func TestCommandSide_AddDefaultLabelPolicy(t *testing.T) {
policy: &domain.LabelPolicy{ policy: &domain.LabelPolicy{
PrimaryColor: "primary-color", PrimaryColor: "primary-color",
SecondaryColor: "secondary-color", SecondaryColor: "secondary-color",
HideLoginNameSuffix: true,
}, },
}, },
res: res{ res: res{
@ -89,6 +91,7 @@ func TestCommandSide_AddDefaultLabelPolicy(t *testing.T) {
&iam.NewAggregate().Aggregate, &iam.NewAggregate().Aggregate,
"primary-color", "primary-color",
"secondary-color", "secondary-color",
true,
), ),
), ),
}, },
@ -100,6 +103,7 @@ func TestCommandSide_AddDefaultLabelPolicy(t *testing.T) {
policy: &domain.LabelPolicy{ policy: &domain.LabelPolicy{
PrimaryColor: "primary-color", PrimaryColor: "primary-color",
SecondaryColor: "secondary-color", SecondaryColor: "secondary-color",
HideLoginNameSuffix: true,
}, },
}, },
res: res{ res: res{
@ -110,6 +114,7 @@ func TestCommandSide_AddDefaultLabelPolicy(t *testing.T) {
}, },
PrimaryColor: "primary-color", PrimaryColor: "primary-color",
SecondaryColor: "secondary-color", SecondaryColor: "secondary-color",
HideLoginNameSuffix: true,
}, },
}, },
}, },
@ -199,6 +204,7 @@ func TestCommandSide_ChangeDefaultLabelPolicy(t *testing.T) {
&iam.NewAggregate().Aggregate, &iam.NewAggregate().Aggregate,
"primary-color", "primary-color",
"secondary-color", "secondary-color",
true,
), ),
), ),
), ),
@ -209,6 +215,7 @@ func TestCommandSide_ChangeDefaultLabelPolicy(t *testing.T) {
policy: &domain.LabelPolicy{ policy: &domain.LabelPolicy{
PrimaryColor: "primary-color", PrimaryColor: "primary-color",
SecondaryColor: "secondary-color", SecondaryColor: "secondary-color",
HideLoginNameSuffix: true,
}, },
}, },
res: res{ res: res{
@ -226,13 +233,14 @@ func TestCommandSide_ChangeDefaultLabelPolicy(t *testing.T) {
&iam.NewAggregate().Aggregate, &iam.NewAggregate().Aggregate,
"primary-color", "primary-color",
"secondary-color", "secondary-color",
true,
), ),
), ),
), ),
expectPush( expectPush(
[]*repository.Event{ []*repository.Event{
eventFromEventPusher( eventFromEventPusher(
newDefaultLabelPolicyChangedEvent(context.Background(), "primary-color-change", "secondary-color-change"), newDefaultLabelPolicyChangedEvent(context.Background(), "primary-color-change", "secondary-color-change", false),
), ),
}, },
), ),
@ -243,6 +251,7 @@ func TestCommandSide_ChangeDefaultLabelPolicy(t *testing.T) {
policy: &domain.LabelPolicy{ policy: &domain.LabelPolicy{
PrimaryColor: "primary-color-change", PrimaryColor: "primary-color-change",
SecondaryColor: "secondary-color-change", SecondaryColor: "secondary-color-change",
HideLoginNameSuffix: false,
}, },
}, },
res: res{ res: res{
@ -253,6 +262,7 @@ func TestCommandSide_ChangeDefaultLabelPolicy(t *testing.T) {
}, },
PrimaryColor: "primary-color-change", PrimaryColor: "primary-color-change",
SecondaryColor: "secondary-color-change", SecondaryColor: "secondary-color-change",
HideLoginNameSuffix: false,
}, },
}, },
}, },
@ -276,12 +286,13 @@ func TestCommandSide_ChangeDefaultLabelPolicy(t *testing.T) {
} }
} }
func newDefaultLabelPolicyChangedEvent(ctx context.Context, primaryColor, secondaryColor string) *iam.LabelPolicyChangedEvent { func newDefaultLabelPolicyChangedEvent(ctx context.Context, primaryColor, secondaryColor string, hideLoginNameSuffix bool) *iam.LabelPolicyChangedEvent {
event, _ := iam.NewLabelPolicyChangedEvent(ctx, event, _ := iam.NewLabelPolicyChangedEvent(ctx,
&iam.NewAggregate().Aggregate, &iam.NewAggregate().Aggregate,
[]policy.LabelPolicyChanges{ []policy.LabelPolicyChanges{
policy.ChangePrimaryColor(primaryColor), policy.ChangePrimaryColor(primaryColor),
policy.ChangeSecondaryColor(secondaryColor), policy.ChangeSecondaryColor(secondaryColor),
policy.ChangeHideLoginNameSuffix(hideLoginNameSuffix),
}, },
) )
return event return event

View File

@ -25,7 +25,7 @@ func (c *Commands) AddLabelPolicy(ctx context.Context, resourceOwner string, pol
} }
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.LabelPolicyWriteModel.WriteModel) orgAgg := OrgAggregateFromWriteModel(&addedPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyAddedEvent(ctx, orgAgg, policy.PrimaryColor, policy.SecondaryColor)) pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyAddedEvent(ctx, orgAgg, policy.PrimaryColor, policy.SecondaryColor, policy.HideLoginNameSuffix))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -53,7 +53,7 @@ func (c *Commands) ChangeLabelPolicy(ctx context.Context, resourceOwner string,
} }
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel) orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.PrimaryColor, policy.SecondaryColor) changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.PrimaryColor, policy.SecondaryColor, policy.HideLoginNameSuffix)
if !hasChanged { if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-4M9vs", "Errors.Org.LabelPolicy.NotChanged") return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-4M9vs", "Errors.Org.LabelPolicy.NotChanged")
} }
@ -69,19 +69,26 @@ func (c *Commands) ChangeLabelPolicy(ctx context.Context, resourceOwner string,
return writeModelToLabelPolicy(&existingPolicy.LabelPolicyWriteModel), nil return writeModelToLabelPolicy(&existingPolicy.LabelPolicyWriteModel), nil
} }
func (c *Commands) RemoveLabelPolicy(ctx context.Context, orgID string) error { func (c *Commands) RemoveLabelPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
if orgID == "" { if orgID == "" {
return caos_errs.ThrowInvalidArgument(nil, "Org-Mf9sf", "Errors.ResourceOwnerMissing") return nil, 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 {
return err return nil, 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-3M9df", "Errors.Org.LabelPolicy.NotFound") return nil, caos_errs.ThrowNotFound(nil, "Org-3M9df", "Errors.Org.LabelPolicy.NotFound")
} }
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.WriteModel) orgAgg := OrgAggregateFromWriteModel(&existingPolicy.WriteModel)
_, err = c.eventstore.PushEvents(ctx, org.NewLabelPolicyRemovedEvent(ctx, orgAgg)) pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyRemovedEvent(ctx, orgAgg))
return err if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
} }

View File

@ -52,6 +52,7 @@ func (wm *OrgLabelPolicyWriteModel) NewChangedEvent(
aggregate *eventstore.Aggregate, aggregate *eventstore.Aggregate,
primaryColor, primaryColor,
secondaryColor string, secondaryColor string,
hideLoginNameSuffix bool,
) (*org.LabelPolicyChangedEvent, bool) { ) (*org.LabelPolicyChangedEvent, bool) {
changes := make([]policy.LabelPolicyChanges, 0) changes := make([]policy.LabelPolicyChanges, 0)
if wm.PrimaryColor != primaryColor { if wm.PrimaryColor != primaryColor {
@ -60,6 +61,9 @@ func (wm *OrgLabelPolicyWriteModel) NewChangedEvent(
if wm.SecondaryColor != secondaryColor { if wm.SecondaryColor != secondaryColor {
changes = append(changes, policy.ChangeSecondaryColor(secondaryColor)) changes = append(changes, policy.ChangeSecondaryColor(secondaryColor))
} }
if wm.HideLoginNameSuffix != hideLoginNameSuffix {
changes = append(changes, policy.ChangeHideLoginNameSuffix(hideLoginNameSuffix))
}
if len(changes) == 0 { if len(changes) == 0 {
return nil, false return nil, false
} }

View File

@ -82,6 +82,7 @@ func TestCommandSide_AddLabelPolicy(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate, &org.NewAggregate("org1", "org1").Aggregate,
"primary-color", "primary-color",
"secondary-color", "secondary-color",
true,
), ),
), ),
), ),
@ -93,6 +94,7 @@ func TestCommandSide_AddLabelPolicy(t *testing.T) {
policy: &domain.LabelPolicy{ policy: &domain.LabelPolicy{
PrimaryColor: "primary-color", PrimaryColor: "primary-color",
SecondaryColor: "secondary-color", SecondaryColor: "secondary-color",
HideLoginNameSuffix: true,
}, },
}, },
res: res{ res: res{
@ -112,6 +114,7 @@ func TestCommandSide_AddLabelPolicy(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate, &org.NewAggregate("org1", "org1").Aggregate,
"primary-color", "primary-color",
"secondary-color", "secondary-color",
true,
), ),
), ),
}, },
@ -124,6 +127,7 @@ func TestCommandSide_AddLabelPolicy(t *testing.T) {
policy: &domain.LabelPolicy{ policy: &domain.LabelPolicy{
PrimaryColor: "primary-color", PrimaryColor: "primary-color",
SecondaryColor: "secondary-color", SecondaryColor: "secondary-color",
HideLoginNameSuffix: true,
}, },
}, },
res: res{ res: res{
@ -134,6 +138,7 @@ func TestCommandSide_AddLabelPolicy(t *testing.T) {
}, },
PrimaryColor: "primary-color", PrimaryColor: "primary-color",
SecondaryColor: "secondary-color", SecondaryColor: "secondary-color",
HideLoginNameSuffix: true,
}, },
}, },
}, },
@ -244,6 +249,7 @@ func TestCommandSide_ChangeLabelPolicy(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate, &org.NewAggregate("org1", "org1").Aggregate,
"primary-color", "primary-color",
"secondary-color", "secondary-color",
true,
), ),
), ),
), ),
@ -255,6 +261,7 @@ func TestCommandSide_ChangeLabelPolicy(t *testing.T) {
policy: &domain.LabelPolicy{ policy: &domain.LabelPolicy{
PrimaryColor: "primary-color", PrimaryColor: "primary-color",
SecondaryColor: "secondary-color", SecondaryColor: "secondary-color",
HideLoginNameSuffix: true,
}, },
}, },
res: res{ res: res{
@ -272,13 +279,14 @@ func TestCommandSide_ChangeLabelPolicy(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate, &org.NewAggregate("org1", "org1").Aggregate,
"primary-color", "primary-color",
"secondary-color", "secondary-color",
true,
), ),
), ),
), ),
expectPush( expectPush(
[]*repository.Event{ []*repository.Event{
eventFromEventPusher( eventFromEventPusher(
newLabelPolicyChangedEvent(context.Background(), "org1", "primary-color-change", "secondary-color-change"), newLabelPolicyChangedEvent(context.Background(), "org1", "primary-color-change", "secondary-color-change", false),
), ),
}, },
), ),
@ -290,6 +298,7 @@ func TestCommandSide_ChangeLabelPolicy(t *testing.T) {
policy: &domain.LabelPolicy{ policy: &domain.LabelPolicy{
PrimaryColor: "primary-color-change", PrimaryColor: "primary-color-change",
SecondaryColor: "secondary-color-change", SecondaryColor: "secondary-color-change",
HideLoginNameSuffix: false,
}, },
}, },
res: res{ res: res{
@ -300,6 +309,7 @@ func TestCommandSide_ChangeLabelPolicy(t *testing.T) {
}, },
PrimaryColor: "primary-color-change", PrimaryColor: "primary-color-change",
SecondaryColor: "secondary-color-change", SecondaryColor: "secondary-color-change",
HideLoginNameSuffix: false,
}, },
}, },
}, },
@ -381,6 +391,7 @@ func TestCommandSide_RemoveLabelPolicy(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate, &org.NewAggregate("org1", "org1").Aggregate,
"primary-color", "primary-color",
"secondary-color", "secondary-color",
true,
), ),
), ),
), ),
@ -406,7 +417,7 @@ func TestCommandSide_RemoveLabelPolicy(t *testing.T) {
r := &Commands{ r := &Commands{
eventstore: tt.fields.eventstore, eventstore: tt.fields.eventstore,
} }
err := r.RemoveLabelPolicy(tt.args.ctx, tt.args.orgID) _, err := r.RemoveLabelPolicy(tt.args.ctx, tt.args.orgID)
if tt.res.err == nil { if tt.res.err == nil {
assert.NoError(t, err) assert.NoError(t, err)
} }
@ -417,12 +428,13 @@ func TestCommandSide_RemoveLabelPolicy(t *testing.T) {
} }
} }
func newLabelPolicyChangedEvent(ctx context.Context, orgID, primaryColor, secondaryColor string) *org.LabelPolicyChangedEvent { func newLabelPolicyChangedEvent(ctx context.Context, orgID, primaryColor, secondaryColor string, hideLoginNameSuffix bool) *org.LabelPolicyChangedEvent {
event, _ := org.NewLabelPolicyChangedEvent(ctx, event, _ := org.NewLabelPolicyChangedEvent(ctx,
&org.NewAggregate(orgID, orgID).Aggregate, &org.NewAggregate(orgID, orgID).Aggregate,
[]policy.LabelPolicyChanges{ []policy.LabelPolicyChanges{
policy.ChangePrimaryColor(primaryColor), policy.ChangePrimaryColor(primaryColor),
policy.ChangeSecondaryColor(secondaryColor), policy.ChangeSecondaryColor(secondaryColor),
policy.ChangeHideLoginNameSuffix(hideLoginNameSuffix),
}, },
) )
return event return event

View File

@ -11,6 +11,7 @@ type LabelPolicyWriteModel struct {
PrimaryColor string PrimaryColor string
SecondaryColor string SecondaryColor string
HideLoginNameSuffix bool
State domain.PolicyState State domain.PolicyState
} }
@ -21,6 +22,7 @@ func (wm *LabelPolicyWriteModel) Reduce() error {
case *policy.LabelPolicyAddedEvent: case *policy.LabelPolicyAddedEvent:
wm.PrimaryColor = e.PrimaryColor wm.PrimaryColor = e.PrimaryColor
wm.SecondaryColor = e.SecondaryColor wm.SecondaryColor = e.SecondaryColor
wm.HideLoginNameSuffix = e.HideLoginNameSuffix
wm.State = domain.PolicyStateActive wm.State = domain.PolicyStateActive
case *policy.LabelPolicyChangedEvent: case *policy.LabelPolicyChangedEvent:
if e.PrimaryColor != nil { if e.PrimaryColor != nil {
@ -29,6 +31,9 @@ func (wm *LabelPolicyWriteModel) Reduce() error {
if e.SecondaryColor != nil { if e.SecondaryColor != nil {
wm.SecondaryColor = *e.SecondaryColor wm.SecondaryColor = *e.SecondaryColor
} }
if e.HideLoginNameSuffix != nil {
wm.HideLoginNameSuffix = *e.HideLoginNameSuffix
}
case *policy.LabelPolicyRemovedEvent: case *policy.LabelPolicyRemovedEvent:
wm.State = domain.PolicyStateRemoved wm.State = domain.PolicyStateRemoved
} }

View File

@ -50,7 +50,46 @@ func (c *Commands) AddHuman(ctx context.Context, orgID string, human *domain.Hum
return writeModelToHuman(addedHuman), nil return writeModelToHuman(addedHuman), nil
} }
func (c *Commands) ImportHuman(ctx context.Context, orgID string, human *domain.Human) (*domain.Human, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-5N8fs", "Errors.ResourceOwnerMissing")
}
orgIAMPolicy, err := c.getOrgIAMPolicy(ctx, orgID)
if err != nil {
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-2N9fs", "Errors.Org.OrgIAMPolicy.NotFound")
}
pwPolicy, err := c.getOrgPasswordComplexityPolicy(ctx, orgID)
if err != nil {
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-4N8gs", "Errors.Org.PasswordComplexity.NotFound")
}
events, addedHuman, err := c.importHuman(ctx, orgID, human, orgIAMPolicy, pwPolicy)
if err != nil {
return nil, err
}
pushedEvents, err := c.eventstore.PushEvents(ctx, events...)
if err != nil {
return nil, err
}
err = AppendAndReduce(addedHuman, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToHuman(addedHuman), nil
}
func (c *Commands) addHuman(ctx context.Context, orgID string, human *domain.Human, orgIAMPolicy *domain.OrgIAMPolicy, pwPolicy *domain.PasswordComplexityPolicy) ([]eventstore.EventPusher, *HumanWriteModel, error) { func (c *Commands) addHuman(ctx context.Context, orgID string, human *domain.Human, orgIAMPolicy *domain.OrgIAMPolicy, pwPolicy *domain.PasswordComplexityPolicy) ([]eventstore.EventPusher, *HumanWriteModel, error) {
if orgID == "" || !human.IsValid() {
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M90d", "Errors.User.Invalid")
}
if human.Password != nil && human.SecretString != "" {
human.ChangeRequired = true
}
return c.createHuman(ctx, orgID, human, nil, false, orgIAMPolicy, pwPolicy)
}
func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain.Human, orgIAMPolicy *domain.OrgIAMPolicy, pwPolicy *domain.PasswordComplexityPolicy) ([]eventstore.EventPusher, *HumanWriteModel, error) {
if orgID == "" || !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")
} }
@ -104,6 +143,9 @@ func (c *Commands) registerHuman(ctx context.Context, orgID string, human *domai
if err != nil { if err != nil {
return nil, nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.Org.PasswordComplexity.NotFound") return nil, nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.Org.PasswordComplexity.NotFound")
} }
if human.Password != nil && human.SecretString != "" {
human.ChangeRequired = false
}
return c.createHuman(ctx, orgID, human, externalIDP, true, orgIAMPolicy, pwPolicy) return c.createHuman(ctx, orgID, human, externalIDP, true, orgIAMPolicy, pwPolicy)
} }
@ -117,9 +159,12 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.
} }
human.AggregateID = userID human.AggregateID = userID
human.SetNamesAsDisplayname() human.SetNamesAsDisplayname()
if err := human.HashPasswordIfExisting(pwPolicy, c.userPasswordAlg, !selfregister); err != nil { if human.Password != nil {
if err := human.HashPasswordIfExisting(pwPolicy, c.userPasswordAlg, human.ChangeRequired); err != nil {
return nil, nil, err return nil, nil, err
} }
}
addedHuman := NewHumanWriteModel(human.AggregateID, orgID) addedHuman := NewHumanWriteModel(human.AggregateID, orgID)
//TODO: adlerhurst maybe we could simplify the code below //TODO: adlerhurst maybe we could simplify the code below
userAgg := UserAggregateFromWriteModel(&addedHuman.WriteModel) userAgg := UserAggregateFromWriteModel(&addedHuman.WriteModel)

View File

@ -171,51 +171,6 @@ func TestCommandSide_AddHuman(t *testing.T) {
err: caos_errs.IsErrorInvalidArgument, err: caos_errs.IsErrorInvalidArgument,
}, },
}, },
{
name: "org policy check failed, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewOrgIAMPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
true,
),
),
),
expectFilter(
eventFromEventPusher(
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
1,
false,
false,
false,
false,
),
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
human: &domain.Human{
Username: "email@test.ch",
Profile: &domain.Profile{
FirstName: "firstname",
LastName: "lastname",
},
Email: &domain.Email{
EmailAddress: "email@test.ch",
},
},
},
res: res{
err: caos_errs.IsPreconditionFailed,
},
},
{ {
name: "add human (with initial code), ok", name: "add human (with initial code), ok",
fields: fields{ fields: fields{
@ -695,6 +650,553 @@ func TestCommandSide_AddHuman(t *testing.T) {
} }
} }
func TestCommandSide_ImportHuman(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
idGenerator id.Generator
secretGenerator crypto.Generator
userPasswordAlg crypto.HashAlgorithm
}
type args struct {
ctx context.Context
orgID string
human *domain.Human
}
type res struct {
want *domain.Human
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "orgid missing, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: context.Background(),
orgID: "",
human: &domain.Human{
Username: "username",
Profile: &domain.Profile{
FirstName: "firstname",
LastName: "lastname",
},
Email: &domain.Email{
EmailAddress: "email@test.ch",
},
},
},
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",
human: &domain.Human{
Username: "username",
Profile: &domain.Profile{
FirstName: "firstname",
LastName: "lastname",
},
Email: &domain.Email{
EmailAddress: "email@test.ch",
},
},
},
res: res{
err: caos_errs.IsPreconditionFailed,
},
},
{
name: "password policy not found, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewOrgIAMPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
true,
),
),
),
expectFilter(),
expectFilter(),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
human: &domain.Human{
Username: "username",
Profile: &domain.Profile{
FirstName: "firstname",
LastName: "lastname",
},
Email: &domain.Email{
EmailAddress: "email@test.ch",
},
},
},
res: res{
err: caos_errs.IsPreconditionFailed,
},
},
{
name: "user invalid, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewOrgIAMPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
true,
),
),
),
expectFilter(
eventFromEventPusher(
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
1,
false,
false,
false,
false,
),
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
human: &domain.Human{
Username: "username",
Profile: &domain.Profile{
FirstName: "firstname",
},
},
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
},
},
{
name: "add human (with password and initial code), ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewOrgIAMPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
true,
),
),
),
expectFilter(
eventFromEventPusher(
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
1,
false,
false,
false,
false,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
newAddHumanEvent("password", 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,
),
),
},
uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)),
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
ctx: context.Background(),
orgID: "org1",
human: &domain.Human{
Username: "username",
Password: &domain.Password{
SecretString: "password",
ChangeRequired: true,
},
Profile: &domain.Profile{
FirstName: "firstname",
LastName: "lastname",
},
Email: &domain.Email{
EmailAddress: "email@test.ch",
},
},
},
res: res{
want: &domain.Human{
ObjectRoot: models.ObjectRoot{
AggregateID: "user1",
ResourceOwner: "org1",
},
Username: "username",
Profile: &domain.Profile{
FirstName: "firstname",
LastName: "lastname",
DisplayName: "firstname lastname",
PreferredLanguage: language.Und,
},
Email: &domain.Email{
EmailAddress: "email@test.ch",
},
State: domain.UserStateInitial,
},
},
},
{
name: "add human email verified password change not required, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewOrgIAMPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
true,
),
),
),
expectFilter(
eventFromEventPusher(
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
1,
false,
false,
false,
false,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
newAddHumanEvent("password", false, ""),
),
eventFromEventPusher(
user.NewHumanEmailVerifiedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate),
),
},
uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)),
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
ctx: context.Background(),
orgID: "org1",
human: &domain.Human{
Username: "username",
Password: &domain.Password{
SecretString: "password",
ChangeRequired: false,
},
Profile: &domain.Profile{
FirstName: "firstname",
LastName: "lastname",
},
Email: &domain.Email{
EmailAddress: "email@test.ch",
IsEmailVerified: true,
},
},
},
res: res{
want: &domain.Human{
ObjectRoot: models.ObjectRoot{
AggregateID: "user1",
ResourceOwner: "org1",
},
Username: "username",
Profile: &domain.Profile{
FirstName: "firstname",
LastName: "lastname",
DisplayName: "firstname lastname",
PreferredLanguage: language.Und,
},
Email: &domain.Email{
EmailAddress: "email@test.ch",
IsEmailVerified: true,
},
State: domain.UserStateActive,
},
},
},
{
name: "add human (with phone), ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewOrgIAMPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
true,
),
),
),
expectFilter(
eventFromEventPusher(
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
1,
false,
false,
false,
false,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
newAddHumanEvent("password", false, "+41711234567"),
),
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,
),
),
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)),
},
uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)),
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
ctx: context.Background(),
orgID: "org1",
human: &domain.Human{
Username: "username",
Profile: &domain.Profile{
FirstName: "firstname",
LastName: "lastname",
},
Password: &domain.Password{
SecretString: "password",
ChangeRequired: false,
},
Email: &domain.Email{
EmailAddress: "email@test.ch",
},
Phone: &domain.Phone{
PhoneNumber: "+41711234567",
},
},
},
res: res{
want: &domain.Human{
ObjectRoot: models.ObjectRoot{
AggregateID: "user1",
ResourceOwner: "org1",
},
Username: "username",
Profile: &domain.Profile{
FirstName: "firstname",
LastName: "lastname",
DisplayName: "firstname lastname",
PreferredLanguage: language.Und,
},
Email: &domain.Email{
EmailAddress: "email@test.ch",
},
Phone: &domain.Phone{
PhoneNumber: "+41711234567",
},
State: domain.UserStateInitial,
},
},
},
{
name: "add human (with verified phone), ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewOrgIAMPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
true,
),
),
),
expectFilter(
eventFromEventPusher(
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
1,
false,
false,
false,
false,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
newAddHumanEvent("password", false, "+41711234567"),
),
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,
),
),
eventFromEventPusher(
user.NewHumanPhoneVerifiedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate),
),
},
uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)),
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
ctx: context.Background(),
orgID: "org1",
human: &domain.Human{
Username: "username",
Profile: &domain.Profile{
FirstName: "firstname",
LastName: "lastname",
},
Password: &domain.Password{
SecretString: "password",
ChangeRequired: false,
},
Email: &domain.Email{
EmailAddress: "email@test.ch",
},
Phone: &domain.Phone{
PhoneNumber: "+41711234567",
IsPhoneVerified: true,
},
},
},
res: res{
want: &domain.Human{
ObjectRoot: models.ObjectRoot{
AggregateID: "user1",
ResourceOwner: "org1",
},
Username: "username",
Profile: &domain.Profile{
FirstName: "firstname",
LastName: "lastname",
DisplayName: "firstname lastname",
PreferredLanguage: language.Und,
},
Email: &domain.Email{
EmailAddress: "email@test.ch",
},
Phone: &domain.Phone{
PhoneNumber: "+41711234567",
},
State: domain.UserStateInitial,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
idGenerator: tt.fields.idGenerator,
initializeUserCode: tt.fields.secretGenerator,
phoneVerificationCode: tt.fields.secretGenerator,
userPasswordAlg: tt.fields.userPasswordAlg,
}
got, err := r.ImportHuman(tt.args.ctx, tt.args.orgID, tt.args.human)
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_RegisterHuman(t *testing.T) { func TestCommandSide_RegisterHuman(t *testing.T) {
type fields struct { type fields struct {
eventstore *eventstore.Eventstore eventstore *eventstore.Eventstore
@ -836,54 +1338,6 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
err: caos_errs.IsPreconditionFailed, err: caos_errs.IsPreconditionFailed,
}, },
}, },
{
name: "org policy check failed, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewOrgIAMPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate,
true,
),
),
),
expectFilter(
eventFromEventPusher(
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate,
1,
false,
false,
false,
false,
),
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
human: &domain.Human{
Username: "email@test.ch",
Profile: &domain.Profile{
FirstName: "firstname",
LastName: "lastname",
},
Email: &domain.Email{
EmailAddress: "email@test.ch",
},
Password: &domain.Password{
SecretString: "password",
},
},
},
res: res{
err: caos_errs.IsPreconditionFailed,
},
},
{ {
name: "add human (with password and initial code), ok", name: "add human (with password and initial code), ok",
fields: fields{ fields: fields{

View File

@ -1,10 +1,12 @@
package domain package domain
import ( import (
"github.com/caos/zitadel/internal/errors"
"golang.org/x/text/language"
"strings" "strings"
"time" "time"
"golang.org/x/text/language"
"github.com/caos/zitadel/internal/errors"
) )
type AuthRequest struct { type AuthRequest struct {
@ -25,11 +27,13 @@ type AuthRequest struct {
levelOfAssurance LevelOfAssurance levelOfAssurance LevelOfAssurance
UserID string UserID string
UserName string
LoginName string LoginName string
DisplayName string DisplayName string
UserOrgID string UserOrgID string
RequestedOrgID string RequestedOrgID string
RequestedOrgName string RequestedOrgName string
RequestedPrimaryDomain string
SelectedIDPConfigID string SelectedIDPConfigID string
LinkingUsers []*ExternalUser LinkingUsers []*ExternalUser
PossibleSteps []NextStep PossibleSteps []NextStep
@ -40,6 +44,7 @@ type AuthRequest struct {
Code string Code string
LoginPolicy *LoginPolicy LoginPolicy *LoginPolicy
AllowedExternalIDPs []*IDPProvider AllowedExternalIDPs []*IDPProvider
LabelPolicy *LabelPolicy
} }
type ExternalUser struct { type ExternalUser struct {
@ -103,8 +108,9 @@ func (a *AuthRequest) WithCurrentInfo(info *BrowserInfo) *AuthRequest {
return a return a
} }
func (a *AuthRequest) SetUserInfo(userID, loginName, displayName, userOrgID string) { func (a *AuthRequest) SetUserInfo(userID, userName, loginName, displayName, userOrgID string) {
a.UserID = userID a.UserID = userID
a.UserName = userName
a.LoginName = loginName a.LoginName = loginName
a.DisplayName = displayName a.DisplayName = displayName
a.UserOrgID = userOrgID a.UserOrgID = userOrgID

View File

@ -1,11 +1,11 @@
package domain package domain
import ( import (
"time"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
caos_errors "github.com/caos/zitadel/internal/errors" caos_errors "github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/v1/models" es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
"strings"
"time"
) )
type Human struct { type Human struct {
@ -59,9 +59,6 @@ func (u *Human) CheckOrgIAMPolicy(policy *OrgIAMPolicy) error {
if policy == nil { if policy == nil {
return caos_errors.ThrowPreconditionFailed(nil, "DOMAIN-zSH7j", "Errors.Users.OrgIamPolicyNil") return caos_errors.ThrowPreconditionFailed(nil, "DOMAIN-zSH7j", "Errors.Users.OrgIamPolicyNil")
} }
if policy.UserLoginMustBeDomain && strings.Contains(u.Username, "@") {
return caos_errors.ThrowPreconditionFailed(nil, "DOMAIN-se4sJ", "Errors.User.EmailAsUsernameNotAllowed")
}
if !policy.UserLoginMustBeDomain && u.Profile != nil && u.Username == "" && u.Email != nil { if !policy.UserLoginMustBeDomain && u.Profile != nil && u.Username == "" && u.Email != nil {
u.Username = u.EmailAddress u.Username = u.EmailAddress
} }

View File

@ -42,6 +42,7 @@ func (s *SelectUserStep) Type() NextStepType {
type UserSelection struct { type UserSelection struct {
UserID string UserID string
UserName string
DisplayName string DisplayName string
LoginName string LoginName string
UserSessionState UserSessionState UserSessionState UserSessionState

View File

@ -10,6 +10,7 @@ type LabelPolicy struct {
Default bool Default bool
PrimaryColor string PrimaryColor string
SecondaryColor string SecondaryColor string
HideLoginNameSuffix bool
} }
func (p *LabelPolicy) IsValid() bool { func (p *LabelPolicy) IsValid() bool {

View File

@ -11,6 +11,7 @@ type LabelPolicy struct {
Default bool Default bool
PrimaryColor string PrimaryColor string
SecondaryColor string SecondaryColor string
HideLoginNameSuffix bool
} }
func (p *LabelPolicy) IsValid() bool { func (p *LabelPolicy) IsValid() bool {

View File

@ -9,6 +9,7 @@ type LabelPolicyView struct {
AggregateID string AggregateID string
PrimaryColor string PrimaryColor string
SecondaryColor string SecondaryColor string
HideLoginNameSuffix bool
Default bool Default bool
CreationDate time.Time CreationDate time.Time

View File

@ -13,6 +13,7 @@ type LabelPolicy struct {
State int32 `json:"-"` State int32 `json:"-"`
PrimaryColor string `json:"primaryColor"` PrimaryColor string `json:"primaryColor"`
SecondaryColor string `json:"secondaryColor"` SecondaryColor string `json:"secondaryColor"`
HideLoginNameSuffix bool `json:"hideLoginNameSuffix"`
} }
func LabelPolicyToModel(policy *LabelPolicy) *iam_model.LabelPolicy { func LabelPolicyToModel(policy *LabelPolicy) *iam_model.LabelPolicy {
@ -21,6 +22,7 @@ func LabelPolicyToModel(policy *LabelPolicy) *iam_model.LabelPolicy {
State: iam_model.PolicyState(policy.State), State: iam_model.PolicyState(policy.State),
PrimaryColor: policy.PrimaryColor, PrimaryColor: policy.PrimaryColor,
SecondaryColor: policy.SecondaryColor, SecondaryColor: policy.SecondaryColor,
HideLoginNameSuffix: policy.HideLoginNameSuffix,
} }
} }
@ -30,6 +32,7 @@ func LabelPolicyFromModel(policy *iam_model.LabelPolicy) *LabelPolicy {
State: int32(policy.State), State: int32(policy.State),
PrimaryColor: policy.PrimaryColor, PrimaryColor: policy.PrimaryColor,
SecondaryColor: policy.SecondaryColor, SecondaryColor: policy.SecondaryColor,
HideLoginNameSuffix: policy.HideLoginNameSuffix,
} }
} }

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"time" "time"
"github.com/caos/zitadel/internal/domain"
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
@ -26,20 +27,24 @@ type LabelPolicyView struct {
PrimaryColor string `json:"primaryColor" gorm:"column:primary_color"` PrimaryColor string `json:"primaryColor" gorm:"column:primary_color"`
SecondaryColor string `json:"secondaryColor" gorm:"column:secondary_color"` SecondaryColor string `json:"secondaryColor" gorm:"column:secondary_color"`
HideLoginNameSuffix bool `json:"hideLoginNameSuffix" gorm:"column:hide_login_name_suffix"`
Default bool `json:"-" gorm:"-"` Default bool `json:"-" gorm:"-"`
Sequence uint64 `json:"-" gorm:"column:sequence"` Sequence uint64 `json:"-" gorm:"column:sequence"`
} }
func LabelPolicyViewFromModel(policy *model.LabelPolicyView) *LabelPolicyView { func (p *LabelPolicyView) ToDomain() *domain.LabelPolicy {
return &LabelPolicyView{ return &domain.LabelPolicy{
AggregateID: policy.AggregateID, ObjectRoot: models.ObjectRoot{
Sequence: policy.Sequence, AggregateID: p.AggregateID,
CreationDate: policy.CreationDate, CreationDate: p.CreationDate,
ChangeDate: policy.ChangeDate, ChangeDate: p.ChangeDate,
PrimaryColor: policy.PrimaryColor, Sequence: p.Sequence,
SecondaryColor: policy.SecondaryColor, },
Default: policy.Default, Default: p.Default,
PrimaryColor: p.PrimaryColor,
SecondaryColor: p.SecondaryColor,
HideLoginNameSuffix: p.HideLoginNameSuffix,
} }
} }
@ -51,6 +56,7 @@ func LabelPolicyViewToModel(policy *LabelPolicyView) *model.LabelPolicyView {
ChangeDate: policy.ChangeDate, ChangeDate: policy.ChangeDate,
PrimaryColor: policy.PrimaryColor, PrimaryColor: policy.PrimaryColor,
SecondaryColor: policy.SecondaryColor, SecondaryColor: policy.SecondaryColor,
HideLoginNameSuffix: policy.HideLoginNameSuffix,
Default: policy.Default, Default: policy.Default,
} }
} }

View File

@ -199,6 +199,32 @@ func (repo *OrgRepository) GetLabelPolicy(ctx context.Context) (*iam_model.Label
return iam_es_model.LabelPolicyViewToModel(policy), err return iam_es_model.LabelPolicyViewToModel(policy), err
} }
func (repo *OrgRepository) GetDefaultLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) {
policy, viewErr := repo.View.LabelPolicyByAggregateID(repo.SystemDefaults.IamID)
if viewErr != nil && !errors.IsNotFound(viewErr) {
return nil, viewErr
}
if errors.IsNotFound(viewErr) {
policy = new(iam_es_model.LabelPolicyView)
}
events, esErr := repo.getIAMEvents(ctx, policy.Sequence)
if errors.IsNotFound(viewErr) && len(events) == 0 {
return nil, errors.ThrowNotFound(nil, "EVENT-3Nf8sd", "Errors.IAM.LabelPolicy.NotFound")
}
if esErr != nil {
logging.Log("EVENT-28uLp").WithError(esErr).Debug("error retrieving new events")
return iam_es_model.LabelPolicyViewToModel(policy), nil
}
policyCopy := *policy
for _, event := range events {
if err := policyCopy.AppendEvent(event); err != nil {
return iam_es_model.LabelPolicyViewToModel(policy), nil
}
}
policy.Default = true
return iam_es_model.LabelPolicyViewToModel(policy), nil
}
func (repo *OrgRepository) GetLoginPolicy(ctx context.Context) (*iam_model.LoginPolicyView, error) { func (repo *OrgRepository) GetLoginPolicy(ctx context.Context) (*iam_model.LoginPolicyView, error) {
policy, viewErr := repo.View.LoginPolicyByAggregateID(authz.GetCtxData(ctx).OrgID) policy, viewErr := repo.View.LoginPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if viewErr != nil && !errors.IsNotFound(viewErr) { if viewErr != nil && !errors.IsNotFound(viewErr) {

View File

@ -45,4 +45,7 @@ type OrgRepository interface {
GetDefaultMailTexts(ctx context.Context) (*iam_model.MailTextsView, error) GetDefaultMailTexts(ctx context.Context) (*iam_model.MailTextsView, error)
GetMailTexts(ctx context.Context) (*iam_model.MailTextsView, error) GetMailTexts(ctx context.Context) (*iam_model.MailTextsView, error)
GetLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error)
GetDefaultLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error)
} }

View File

@ -22,6 +22,7 @@ func NewLabelPolicyAddedEvent(
aggregate *eventstore.Aggregate, aggregate *eventstore.Aggregate,
primaryColor, primaryColor,
secondaryColor string, secondaryColor string,
hideLoginNameSuffix bool,
) *LabelPolicyAddedEvent { ) *LabelPolicyAddedEvent {
return &LabelPolicyAddedEvent{ return &LabelPolicyAddedEvent{
LabelPolicyAddedEvent: *policy.NewLabelPolicyAddedEvent( LabelPolicyAddedEvent: *policy.NewLabelPolicyAddedEvent(
@ -30,7 +31,8 @@ func NewLabelPolicyAddedEvent(
aggregate, aggregate,
LabelPolicyAddedEventType), LabelPolicyAddedEventType),
primaryColor, primaryColor,
secondaryColor), secondaryColor,
hideLoginNameSuffix),
} }
} }

View File

@ -23,6 +23,7 @@ func NewLabelPolicyAddedEvent(
aggregate *eventstore.Aggregate, aggregate *eventstore.Aggregate,
primaryColor, primaryColor,
secondaryColor string, secondaryColor string,
hideLoginNameSuffix bool,
) *LabelPolicyAddedEvent { ) *LabelPolicyAddedEvent {
return &LabelPolicyAddedEvent{ return &LabelPolicyAddedEvent{
LabelPolicyAddedEvent: *policy.NewLabelPolicyAddedEvent( LabelPolicyAddedEvent: *policy.NewLabelPolicyAddedEvent(
@ -31,7 +32,8 @@ func NewLabelPolicyAddedEvent(
aggregate, aggregate,
LabelPolicyAddedEventType), LabelPolicyAddedEventType),
primaryColor, primaryColor,
secondaryColor), secondaryColor,
hideLoginNameSuffix),
} }
} }

View File

@ -18,6 +18,7 @@ type LabelPolicyAddedEvent struct {
PrimaryColor string `json:"primaryColor,omitempty"` PrimaryColor string `json:"primaryColor,omitempty"`
SecondaryColor string `json:"secondaryColor,omitempty"` SecondaryColor string `json:"secondaryColor,omitempty"`
HideLoginNameSuffix bool `json:"hideLoginNameSuffix,omitempty"`
} }
func (e *LabelPolicyAddedEvent) Data() interface{} { func (e *LabelPolicyAddedEvent) Data() interface{} {
@ -32,12 +33,14 @@ func NewLabelPolicyAddedEvent(
base *eventstore.BaseEvent, base *eventstore.BaseEvent,
primaryColor, primaryColor,
secondaryColor string, secondaryColor string,
hideLoginNameSuffix bool,
) *LabelPolicyAddedEvent { ) *LabelPolicyAddedEvent {
return &LabelPolicyAddedEvent{ return &LabelPolicyAddedEvent{
BaseEvent: *base, BaseEvent: *base,
PrimaryColor: primaryColor, PrimaryColor: primaryColor,
SecondaryColor: secondaryColor, SecondaryColor: secondaryColor,
HideLoginNameSuffix: hideLoginNameSuffix,
} }
} }
@ -59,6 +62,7 @@ type LabelPolicyChangedEvent struct {
PrimaryColor *string `json:"primaryColor,omitempty"` PrimaryColor *string `json:"primaryColor,omitempty"`
SecondaryColor *string `json:"secondaryColor,omitempty"` SecondaryColor *string `json:"secondaryColor,omitempty"`
HideLoginNameSuffix *bool `json:"hideLoginNameSuffix,omitempty"`
} }
func (e *LabelPolicyChangedEvent) Data() interface{} { func (e *LabelPolicyChangedEvent) Data() interface{} {
@ -99,6 +103,12 @@ func ChangeSecondaryColor(secondaryColor string) func(*LabelPolicyChangedEvent)
} }
} }
func ChangeHideLoginNameSuffix(hideLoginNameSuffix bool) func(*LabelPolicyChangedEvent) {
return func(e *LabelPolicyChangedEvent) {
e.HideLoginNameSuffix = &hideLoginNameSuffix
}
}
func LabelPolicyChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { func LabelPolicyChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e := &LabelPolicyChangedEvent{ e := &LabelPolicyChangedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event), BaseEvent: *eventstore.BaseEventFromRepo(event),

View File

@ -54,7 +54,8 @@ func (l *Login) handleLoginNameCheck(w http.ResponseWriter, r *http.Request) {
return return
} }
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context()) userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
err = l.authRepo.CheckLoginName(r.Context(), authReq.ID, data.LoginName, userAgentID) loginName := data.LoginName
err = l.authRepo.CheckLoginName(r.Context(), authReq.ID, loginName, userAgentID)
if err != nil { if err != nil {
l.renderLogin(w, r, authReq, err) l.renderLogin(w, r, authReq, err)
return return
@ -73,7 +74,7 @@ func (l *Login) renderLogin(w http.ResponseWriter, r *http.Request, authReq *dom
return authReq.LoginPolicy != nil && authReq.LoginPolicy.AllowUsernamePassword return authReq.LoginPolicy != nil && authReq.LoginPolicy.AllowUsernamePassword
}, },
"hasExternalLogin": func() bool { "hasExternalLogin": func() bool {
return authReq.LoginPolicy.AllowExternalIDP && authReq.AllowedExternalIDPs != nil && len(authReq.AllowedExternalIDPs) > 0 return authReq.LoginPolicy != nil && authReq.LoginPolicy.AllowExternalIDP && authReq.AllowedExternalIDPs != nil && len(authReq.AllowedExternalIDPs) > 0
}, },
} }
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplLogin], data, funcs) l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplLogin], data, funcs)

View File

@ -271,6 +271,8 @@ func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, title
ThemeMode: l.getThemeMode(r), ThemeMode: l.getThemeMode(r),
OrgID: l.getOrgID(authReq), OrgID: l.getOrgID(authReq),
OrgName: l.getOrgName(authReq), OrgName: l.getOrgName(authReq),
PrimaryDomain: l.getOrgPrimaryDomain(authReq),
DisplayLoginNameSuffix: l.isDisplayLoginNameSuffix(authReq),
AuthReqID: getRequestID(authReq, r), AuthReqID: getRequestID(authReq, r),
CSRF: csrf.TemplateField(r), CSRF: csrf.TemplateField(r),
Nonce: http_mw.GetNonce(r), Nonce: http_mw.GetNonce(r),
@ -283,12 +285,14 @@ func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, title
} }
func (l *Login) getProfileData(authReq *domain.AuthRequest) profileData { func (l *Login) getProfileData(authReq *domain.AuthRequest) profileData {
var loginName, displayName string var userName, loginName, displayName string
if authReq != nil { if authReq != nil {
userName = authReq.UserName
loginName = authReq.LoginName loginName = authReq.LoginName
displayName = authReq.DisplayName displayName = authReq.DisplayName
} }
return profileData{ return profileData{
UserName: userName,
LoginName: loginName, LoginName: loginName,
DisplayName: displayName, DisplayName: displayName,
} }
@ -329,6 +333,23 @@ func (l *Login) getOrgName(authReq *domain.AuthRequest) string {
return authReq.RequestedOrgName return authReq.RequestedOrgName
} }
func (l *Login) getOrgPrimaryDomain(authReq *domain.AuthRequest) string {
if authReq == nil {
return ""
}
return authReq.RequestedPrimaryDomain
}
func (l *Login) isDisplayLoginNameSuffix(authReq *domain.AuthRequest) bool {
if authReq == nil {
return false
}
if authReq.RequestedOrgID == "" {
return false
}
return authReq.LabelPolicy != nil && !authReq.LabelPolicy.HideLoginNameSuffix
}
func getRequestID(authReq *domain.AuthRequest, r *http.Request) string { func getRequestID(authReq *domain.AuthRequest, r *http.Request) string {
if authReq != nil { if authReq != nil {
return authReq.ID return authReq.ID
@ -357,6 +378,8 @@ type baseData struct {
ThemeMode string ThemeMode string
OrgID string OrgID string
OrgName string OrgName string
PrimaryDomain string
DisplayLoginNameSuffix bool
AuthReqID string AuthReqID string
CSRF template.HTML CSRF template.HTML
Nonce string Nonce string
@ -380,6 +403,7 @@ type userData struct {
type profileData struct { type profileData struct {
LoginName string LoginName string
UserName string
DisplayName string DisplayName string
} }

View File

@ -1,19 +1,10 @@
Password:
Title: Willkommen zurück!
Description: Gib deine Benutzerdaten ein.
Password: Passwort
MinLength: Mindestlänge
HasUppercase: Grossbuchstaben
HasLowercase: Kleinbuchstaben
HasNumber: Nummer
HasSymbol: Symbol
Login: Login:
Title: Anmeldung Title: Anmeldung
Description: Mit ZITADEL-Konto anmelden. Description: Mit ZITADEL-Konto anmelden.
TitleLinking: Anmeldung für Benutzer Linking TitleLinking: Anmeldung für Benutzer Linking
DescriptionLinking: Gib deine Benutzerdaten ein um den externen Benutzer mit einem ZITADEL Benutzer zu linken. DescriptionLinking: Gib deine Benutzerdaten ein um den externen Benutzer mit einem ZITADEL Benutzer zu linken.
Loginname: Loginname Loginname: Loginname
UsernamePlaceHolder: username
LoginnamePlaceHolder: username@domain LoginnamePlaceHolder: username@domain
ExternalLogin: Melde dich mit einem externen Benutzer an ExternalLogin: Melde dich mit einem externen Benutzer an
MustBeMemberOfOrg: Der Benutzer muss der Organisation {{.OrgName}} angehören. MustBeMemberOfOrg: Der Benutzer muss der Organisation {{.OrgName}} angehören.
@ -28,6 +19,16 @@ UserSelection:
SessionState1: inaktiv SessionState1: inaktiv
MustBeMemberOfOrg: Der Benutzer muss der Organisation {{.OrgName}} angehören. MustBeMemberOfOrg: Der Benutzer muss der Organisation {{.OrgName}} angehören.
Password:
Title: Willkommen zurück!
Description: Gib deine Benutzerdaten ein.
Password: Passwort
MinLength: Mindestlänge
HasUppercase: Grossbuchstaben
HasLowercase: Kleinbuchstaben
HasNumber: Nummer
HasSymbol: Symbol
UsernameChange: UsernameChange:
Title: Usernamen ändern Title: Usernamen ändern
Description: Wähle deinen neuen Benutzernamen Description: Wähle deinen neuen Benutzernamen

View File

@ -4,9 +4,10 @@ Login:
TitleLinking: Login for userlinking TitleLinking: Login for userlinking
DescriptionLinking: Enter your login data to link your external user with a ZITADEL user. DescriptionLinking: Enter your login data to link your external user with a ZITADEL user.
Loginname: Loginname Loginname: Loginname
UsernamePlaceHolder: username
LoginnamePlaceHolder: username@domain LoginnamePlaceHolder: username@domain
ExternalLogin: Login with an external user. ExternalLogin: Login with an external user.
MustBeMemberOfOrg: The user must be mermber of the {{.OrgDomain}} organisation. MustBeMemberOfOrg: The user must be mermber of the {{.OrgName}} organisation.
UserSelection: UserSelection:
Title: Select account Title: Select account
@ -16,7 +17,7 @@ UserSelection:
OtherUser: Other User OtherUser: Other User
SessionState0: active SessionState0: active
SessionState1: inactive SessionState1: inactive
MustBeMemberOfOrg: The user must be mermber of the {{.OrgDomain}} organisation. MustBeMemberOfOrg: The user must be mermber of the {{.OrgName}} organisation.
Password: Password:
Title: Password Title: Password

View File

@ -12,6 +12,11 @@ $lgn-container-margin: 0px auto 50px auto;
align-items: center; align-items: center;
border: none; border: none;
outline: none; outline: none;
cursor: pointer;
&:disabled {
cursor: not-allowed;
}
.left { .left {
padding: .5rem 1rem; padding: .5rem 1rem;

View File

@ -25,7 +25,6 @@
color: inherit; color: inherit;
background: transparent; background: transparent;
box-shadow: inset 0 -1px lgn-color($foreground, footer-line); box-shadow: inset 0 -1px lgn-color($foreground, footer-line);
cursor: pointer;
&:hover { &:hover {
$primary: map-get($config, primary); $primary: map-get($config, primary);

View File

@ -440,6 +440,10 @@ i {
align-items: center; align-items: center;
border: none; border: none;
outline: none; outline: none;
cursor: pointer;
}
.lgn-account-selection .lgn-account:disabled {
cursor: not-allowed;
} }
.lgn-account-selection .lgn-account .left { .lgn-account-selection .lgn-account .left {
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
@ -1298,6 +1302,10 @@ i {
align-items: center; align-items: center;
border: none; border: none;
outline: none; outline: none;
cursor: pointer;
}
.lgn-account-selection .lgn-account:disabled {
cursor: not-allowed;
} }
.lgn-account-selection .lgn-account .left { .lgn-account-selection .lgn-account .left {
padding: 0.5rem 1rem; padding: 0.5rem 1rem;

File diff suppressed because one or more lines are too long

View File

@ -440,6 +440,10 @@ i {
align-items: center; align-items: center;
border: none; border: none;
outline: none; outline: none;
cursor: pointer;
}
.lgn-account-selection .lgn-account:disabled {
cursor: not-allowed;
} }
.lgn-account-selection .lgn-account .left { .lgn-account-selection .lgn-account .left {
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
@ -1202,7 +1206,6 @@ a:hover, a:active {
color: inherit; color: inherit;
background: transparent; background: transparent;
box-shadow: inset 0 -1px #303131; box-shadow: inset 0 -1px #303131;
cursor: pointer;
} }
.lgn-account-selection .lgn-account:hover { .lgn-account-selection .lgn-account:hover {
background-color: rgba(255, 255, 255, 0.02); background-color: rgba(255, 255, 255, 0.02);
@ -1522,7 +1525,6 @@ a:hover, a:active {
color: inherit; color: inherit;
background: transparent; background: transparent;
box-shadow: inset 0 -1px #303131; box-shadow: inset 0 -1px #303131;
cursor: pointer;
} }
.lgn-dark-theme .lgn-account-selection .lgn-account:hover { .lgn-dark-theme .lgn-account-selection .lgn-account:hover {
background-color: rgba(255, 255, 255, 0.02); background-color: rgba(255, 255, 255, 0.02);
@ -1836,7 +1838,6 @@ a:hover, a:active {
color: inherit; color: inherit;
background: transparent; background: transparent;
box-shadow: inset 0 -1px #e3e8ee; box-shadow: inset 0 -1px #e3e8ee;
cursor: pointer;
} }
.lgn-light-theme .lgn-account-selection .lgn-account:hover { .lgn-light-theme .lgn-account-selection .lgn-account:hover {
background-color: rgba(0, 0, 0, 0.02); background-color: rgba(0, 0, 0, 0.02);

File diff suppressed because one or more lines are too long

View File

@ -20,8 +20,13 @@
{{if hasUsernamePasswordLogin }} {{if hasUsernamePasswordLogin }}
<div class="fields"> <div class="fields">
<label class="lgn-label" for="loginName">{{t "Login.Loginname"}}</label> <label class="lgn-label" for="loginName">{{t "Login.Loginname"}}</label>
<input class="lgn-input lgn-suffix-input" type="text" id="loginName" name="loginName" placeholder="{{t "Login.LoginnamePlaceHolder"}}" <div class="lgn-suffix-wrapper">
value="{{ .LoginName }}" {{if .ErrMessage}}shake {{end}} autocomplete="username" autofocus required> <input class="lgn-input lgn-suffix-input" type="text" id="loginName" name="loginName" placeholder="{{if .OrgID }}{{t "Login.UsernamePlaceHolder"}}{{else}}{{t "Login.LoginnamePlaceHolder"}}{{end}}"
value="{{ .UserName }}" {{if .ErrMessage}}shake {{end}} autocomplete="username" autofocus required>
{{if .DisplayLoginNameSuffix}}
<span id="default-login-suffix" lgnsuffix class="loginname-suffix">@{{.PrimaryDomain}}</span>
{{end}}
</div>
</div> </div>
{{end}} {{end}}
@ -53,5 +58,6 @@
<script src="{{ resourceUrl "scripts/form_submit.js" }}"></script> <script src="{{ resourceUrl "scripts/form_submit.js" }}"></script>
<script src="{{ resourceUrl "scripts/default_form_validation.js" }}"></script> <script src="{{ resourceUrl "scripts/default_form_validation.js" }}"></script>
<script src="{{ resourceUrl "scripts/input_suffix_offset.js" }}"></script>
{{template "main-bottom" .}} {{template "main-bottom" .}}

View File

@ -23,10 +23,11 @@
<div class="lgn-account-selection"> <div class="lgn-account-selection">
{{ if .Users }} {{ if .Users }}
{{ $displayLoginNameSuffix := and .OrgID (not .DisplayLoginNameSuffix)}}
{{ range $user := .Users }} {{ range $user := .Users }}
{{ $sessionState := (printf "UserSelection.SessionState%v" $user.UserSessionState) }} {{ $sessionState := (printf "UserSelection.SessionState%v" $user.UserSessionState) }}
<button type="submit" name="userID" value="{{$user.UserID}}" class="lgn-account" <button type="submit" name="userID" value="{{$user.UserID}}" class="lgn-account"
{{if not $user.SelectionPossible}}disabled title="{{t " Errors.User.NotAllowedOrg"}}"{{end}}> {{if not $user.SelectionPossible}}disabled title="{{t "Errors.User.NotAllowedOrg"}}"{{end}}>
<div class="left"> <div class="left">
<div class="lgn-avatar" displayname="{{$user.DisplayName}}"> <div class="lgn-avatar" displayname="{{$user.DisplayName}}">
<span class="initials">A</span> <span class="initials">A</span>
@ -34,7 +35,7 @@
</div> </div>
<div class="lgn-names"> <div class="lgn-names">
<p class="lgn-displayname">{{$user.DisplayName}}</p> <p class="lgn-displayname">{{$user.DisplayName}}</p>
<p class="lgn-loginname">{{$user.LoginName}}</p> <p class="lgn-loginname">{{if and $displayLoginNameSuffix $user.SelectionPossible}}{{$user.UserName}}{{else}}{{$user.LoginName}}{{end}}</p>
<p class="lgn-session-state i{{$user.UserSessionState}}">{{t $sessionState}}</p> <p class="lgn-session-state i{{$user.UserSessionState}}">{{t $sessionState}}</p>
</div> </div>
<span class="fill-space"></span> <span class="fill-space"></span>

View File

@ -1,5 +1,5 @@
{{define "user-profile"}} {{define "user-profile"}}
{{if .LoginName}} {{if or .LoginName .UserName}}
<div class="lgn-login-profile"> <div class="lgn-login-profile">
<div class="lgn-profile-image"></div> <div class="lgn-profile-image"></div>
<div class="lgn-names"> <div class="lgn-names">
@ -9,7 +9,7 @@
</div> </div>
</div> </div>
<div class="lgn-loginname"> <div class="lgn-loginname">
<p>{{.LoginName}}</p> <p>{{if .DisplayLoginNameSuffix}}{{.LoginName}}{{else}}{{.UserName}}{{end}}</p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,21 @@
ALTER TABLE management.label_policies ADD COLUMN hide_login_name_suffix BOOLEAN;
ALTER TABLE adminapi.label_policies ADD COLUMN hide_login_name_suffix BOOLEAN;
CREATE TABLE auth.label_policies (
aggregate_id TEXT,
creation_date TIMESTAMPTZ,
change_date TIMESTAMPTZ,
label_policy_state SMALLINT,
sequence BIGINT,
primary_color TEXT,
secondary_color TEXT,
hide_login_name_suffix BOOLEAN,
PRIMARY KEY (aggregate_id)
);
GRANT SELECT ON TABLE auth.label_policies TO notification;

View File

@ -1,7 +0,0 @@
CREATE TABLE eventstore.unique_constraints (
unique_type TEXT,
unique_field TEXT,
PRIMARY KEY (unique_type, unique_field)
);
GRANT DELETE ON TABLE eventstore.unique_constraints to eventstore;

View File

@ -0,0 +1,23 @@
CREATE TABLE eventstore.unique_constraints (
unique_type TEXT,
unique_field TEXT,
PRIMARY KEY (unique_type, unique_field)
);
GRANT DELETE ON TABLE eventstore.unique_constraints to eventstore;
ALTER TABLE management.login_policies ADD COLUMN default_policy BOOLEAN;
ALTER TABLE adminapi.login_policies ADD COLUMN default_policy BOOLEAN;
ALTER TABLE auth.login_policies ADD COLUMN default_policy BOOLEAN;
CREATE INDEX event_type ON eventstore.events (event_type);
CREATE INDEX resource_owner ON eventstore.events (resource_owner);
CREATE USER queries WITH PASSWORD ${queriespassword};
GRANT SELECT ON TABLE eventstore.events TO queries;
ALTER TABLE management.org_members ADD COLUMN preferred_login_name TEXT;
ALTER TABLE management.project_members ADD COLUMN preferred_login_name TEXT;
ALTER TABLE management.project_grant_members ADD COLUMN preferred_login_name TEXT;
ALTER TABLE adminapi.iam_members ADD COLUMN preferred_login_name TEXT;

View File

@ -1,3 +0,0 @@
ALTER TABLE management.login_policies ADD COLUMN default_policy BOOLEAN;
ALTER TABLE adminapi.login_policies ADD COLUMN default_policy BOOLEAN;
ALTER TABLE auth.login_policies ADD COLUMN default_policy BOOLEAN;

View File

@ -1,2 +0,0 @@
CREATE INDEX event_type ON eventstore.events (event_type);
CREATE INDEX resource_owner ON eventstore.events (resource_owner);

View File

@ -1,2 +0,0 @@
CREATE USER queries WITH PASSWORD ${queriespassword};
GRANT SELECT ON TABLE eventstore.events TO queries;

View File

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

View File

@ -755,6 +755,7 @@ message GetLabelPolicyResponse {
message UpdateLabelPolicyRequest { message UpdateLabelPolicyRequest {
string primary_color = 1 [(validate.rules).string = {min_len: 1, max_len: 50}]; string primary_color = 1 [(validate.rules).string = {min_len: 1, max_len: 50}];
string secondary_color = 2 [(validate.rules).string = {min_len: 1, max_len: 50}]; string secondary_color = 2 [(validate.rules).string = {min_len: 1, max_len: 50}];
bool hide_login_name_suffix = 3;
} }
message UpdateLabelPolicyResponse { message UpdateLabelPolicyResponse {

View File

@ -162,6 +162,17 @@ service ManagementService {
}; };
} }
rpc ImportHumanUser(ImportHumanUserRequest) returns (ImportHumanUserResponse) {
option (google.api.http) = {
post: "/users/human/_import"
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "user.write"
};
}
rpc AddMachineUser(AddMachineUserRequest) returns (AddMachineUserResponse) { rpc AddMachineUser(AddMachineUserRequest) returns (AddMachineUserResponse) {
option (google.api.http) = { option (google.api.http) = {
post: "/users/machine" post: "/users/machine"
@ -1641,6 +1652,58 @@ service ManagementService {
}; };
} }
rpc GetLabelPolicy(GetLabelPolicyRequest) returns (GetLabelPolicyResponse) {
option (google.api.http) = {
get: "/policies/label"
};
option (zitadel.v1.auth_option) = {
permission: "policy.read"
};
}
rpc GetDefaultLabelPolicy(GetDefaultLabelPolicyRequest) returns (GetDefaultLabelPolicyResponse) {
option (google.api.http) = {
get: "/policies/default/label"
};
option (zitadel.v1.auth_option) = {
permission: "policy.read"
};
}
rpc AddCustomLabelPolicy(AddCustomLabelPolicyRequest) returns (AddCustomLabelPolicyResponse) {
option (google.api.http) = {
post: "/policies/label"
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "policy.write"
};
}
rpc UpdateCustomLabelPolicy(UpdateCustomLabelPolicyRequest) returns (UpdateCustomLabelPolicyResponse) {
option (google.api.http) = {
put: "/policies/label"
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "policy.write"
};
}
rpc ResetLabelPolicyToDefault(ResetLabelPolicyToDefaultRequest) returns (ResetLabelPolicyToDefaultResponse) {
option (google.api.http) = {
delete: "/policies/label"
};
option (zitadel.v1.auth_option) = {
permission: "policy.delete"
};
}
rpc GetOrgIDPByID(GetOrgIDPByIDRequest) returns (GetOrgIDPByIDResponse) { rpc GetOrgIDPByID(GetOrgIDPByIDRequest) returns (GetOrgIDPByIDResponse) {
option (google.api.http) = { option (google.api.http) = {
get: "/idps/{id}" get: "/idps/{id}"
@ -1825,6 +1888,39 @@ message AddHumanUserResponse {
zitadel.v1.ObjectDetails details = 2; zitadel.v1.ObjectDetails details = 2;
} }
message ImportHumanUserRequest {
message Profile {
string first_name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
string last_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
string nick_name = 3 [(validate.rules).string = {max_len: 200}];
string display_name = 4 [(validate.rules).string = {max_len: 200}];
string preferred_language = 5 [(validate.rules).string = {max_len: 10}];
zitadel.user.v1.Gender gender = 6;
}
message Email {
string email = 1 [(validate.rules).string.email = true]; //TODO: check if no value is allowed
bool is_email_verified = 2;
}
message Phone {
// has to be a global number
string phone = 1 [(validate.rules).string = {min_len: 1, max_len: 50, prefix: "+"}];
bool is_phone_verified = 2;
}
string user_name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
Profile profile = 2 [(validate.rules).message.required = true];
Email email = 3 [(validate.rules).message.required = true];
Phone phone = 4;
string password = 5;
bool password_change_required = 6;
}
message ImportHumanUserResponse {
string user_id = 1;
zitadel.v1.ObjectDetails details = 2;
}
message AddMachineUserRequest { message AddMachineUserRequest {
string user_name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; string user_name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
@ -3097,6 +3193,45 @@ message ResetPasswordLockoutPolicyToDefaultResponse {
zitadel.v1.ObjectDetails details = 1; zitadel.v1.ObjectDetails details = 1;
} }
message GetLabelPolicyRequest {}
message GetLabelPolicyResponse {
zitadel.policy.v1.LabelPolicy policy = 1;
bool is_default = 2;
}
message GetDefaultLabelPolicyRequest {}
message GetDefaultLabelPolicyResponse {
zitadel.policy.v1.LabelPolicy policy = 1;
}
message AddCustomLabelPolicyRequest {
string primary_color = 1 [(validate.rules).string = {min_len: 1, max_len: 50}];
string secondary_color = 2 [(validate.rules).string = {min_len: 1, max_len: 50}];
bool hide_login_name_suffix = 3;
}
message AddCustomLabelPolicyResponse {
zitadel.v1.ObjectDetails details = 1;
}
message UpdateCustomLabelPolicyRequest {
string primary_color = 1 [(validate.rules).string = {min_len: 1, max_len: 50}];
string secondary_color = 2 [(validate.rules).string = {min_len: 1, max_len: 50}];
bool hide_login_name_suffix = 3;
}
message UpdateCustomLabelPolicyResponse {
zitadel.v1.ObjectDetails details = 1;
}
message ResetLabelPolicyToDefaultRequest {}
message ResetLabelPolicyToDefaultResponse {
zitadel.v1.ObjectDetails details = 1;
}
message GetOrgIDPByIDRequest { message GetOrgIDPByIDRequest {
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
} }

View File

@ -17,6 +17,7 @@ message LabelPolicy {
string primary_color = 2; string primary_color = 2;
string secondary_color = 3; string secondary_color = 3;
bool is_default = 4; bool is_default = 4;
bool hide_login_name_suffix = 5;
} }
message LoginPolicy { message LoginPolicy {