mirror of
https://github.com/zitadel/zitadel.git
synced 2025-07-31 08:03:44 +00:00
fix: new es testing (#1411)
* 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: clock skew validation * revert crypto changes * fix: tests * fix project grant member commands Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
parent
e9eb5b7848
commit
2bd255106a
@ -5,3 +5,8 @@ import "context"
|
||||
func NewMockContext(orgID, userID string) context.Context {
|
||||
return context.WithValue(context.Background(), dataKey, CtxData{UserID: userID, OrgID: orgID})
|
||||
}
|
||||
|
||||
func NewMockContextWithPermissions(orgID, userID string, permissions []string) context.Context {
|
||||
ctx := context.WithValue(context.Background(), dataKey, CtxData{UserID: userID, OrgID: orgID})
|
||||
return context.WithValue(ctx, requestPermissionsKey, permissions)
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ func (s *Server) ListProjectGrantMembers(ctx context.Context, req *mgmt_pb.ListP
|
||||
}
|
||||
|
||||
func (s *Server) AddProjectGrantMember(ctx context.Context, req *mgmt_pb.AddProjectGrantMemberRequest) (*mgmt_pb.AddProjectGrantMemberResponse, error) {
|
||||
member, err := s.command.AddProjectGrantMember(ctx, AddProjectGrantMemberRequestToDomain(req), authz.GetCtxData(ctx).OrgID)
|
||||
member, err := s.command.AddProjectGrantMember(ctx, AddProjectGrantMemberRequestToDomain(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -140,7 +140,7 @@ func (s *Server) AddProjectGrantMember(ctx context.Context, req *mgmt_pb.AddProj
|
||||
}
|
||||
|
||||
func (s *Server) UpdateProjectGrantMember(ctx context.Context, req *mgmt_pb.UpdateProjectGrantMemberRequest) (*mgmt_pb.UpdateProjectGrantMemberResponse, error) {
|
||||
member, err := s.command.ChangeProjectGrantMember(ctx, UpdateProjectGrantMemberRequestToDomain(req), authz.GetCtxData(ctx).OrgID)
|
||||
member, err := s.command.ChangeProjectGrantMember(ctx, UpdateProjectGrantMemberRequestToDomain(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -154,7 +154,7 @@ func (s *Server) UpdateProjectGrantMember(ctx context.Context, req *mgmt_pb.Upda
|
||||
}
|
||||
|
||||
func (s *Server) RemoveProjectGrantMember(ctx context.Context, req *mgmt_pb.RemoveProjectGrantMemberRequest) (*mgmt_pb.RemoveProjectGrantMemberResponse, error) {
|
||||
details, err := s.command.RemoveProjectGrantMember(ctx, req.ProjectId, req.UserId, req.GrantId, authz.GetCtxData(ctx).OrgID)
|
||||
details, err := s.command.RemoveProjectGrantMember(ctx, req.ProjectId, req.UserId, req.GrantId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -32,14 +32,17 @@ func (c *Commands) AddIAMMember(ctx context.Context, member *domain.Member) (*do
|
||||
}
|
||||
|
||||
func (c *Commands) addIAMMember(ctx context.Context, iamAgg *eventstore.Aggregate, addedMember *IAMMemberWriteModel, member *domain.Member) (eventstore.EventPusher, error) {
|
||||
if !member.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-GR34U", "Errors.IAM.MemberInvalid")
|
||||
if !member.IsIAMValid() {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-GR34U", "Errors.IAM.MemberInvalid")
|
||||
}
|
||||
if len(domain.CheckForInvalidRoles(member.Roles, domain.IAMRolePrefix, c.zitadelRoles)) > 0 {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4m0fS", "Errors.IAM.MemberInvalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-4m0fS", "Errors.IAM.MemberInvalid")
|
||||
}
|
||||
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, addedMember)
|
||||
err := c.checkUserExists(ctx, addedMember.UserID, "")
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "IAM-5N9vs", "Errors.User.NotFound")
|
||||
}
|
||||
err = c.eventstore.FilterToQueryReducer(ctx, addedMember)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -52,11 +55,11 @@ func (c *Commands) addIAMMember(ctx context.Context, iamAgg *eventstore.Aggregat
|
||||
|
||||
//ChangeIAMMember updates an existing member
|
||||
func (c *Commands) ChangeIAMMember(ctx context.Context, member *domain.Member) (*domain.Member, error) {
|
||||
if !member.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-LiaZi", "Errors.IAM.MemberInvalid")
|
||||
if !member.IsIAMValid() {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-LiaZi", "Errors.IAM.MemberInvalid")
|
||||
}
|
||||
if len(domain.CheckForInvalidRoles(member.Roles, domain.IAMRolePrefix, c.zitadelRoles)) > 0 {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-3m9fs", "Errors.IAM.MemberInvalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-3m9fs", "Errors.IAM.MemberInvalid")
|
||||
}
|
||||
|
||||
existingMember, err := c.iamMemberWriteModelByID(ctx, member.UserID)
|
||||
@ -81,6 +84,9 @@ func (c *Commands) ChangeIAMMember(ctx context.Context, member *domain.Member) (
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveIAMMember(ctx context.Context, userID string) (*domain.ObjectDetails, error) {
|
||||
if userID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-LiaZi", "Errors.IDMissing")
|
||||
}
|
||||
memberWriteModel, err := c.iamMemberWriteModelByID(ctx, userID)
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
return nil, err
|
||||
|
552
internal/command/iam_member_test.go
Normal file
552
internal/command/iam_member_test.go
Normal file
@ -0,0 +1,552 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"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/member"
|
||||
"github.com/caos/zitadel/internal/repository/user"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/text/language"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddIAMMember(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
zitadelRoles []authz.RoleMapping
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
member *domain.Member
|
||||
}
|
||||
type res struct {
|
||||
want *domain.Member
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid member, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid roles, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
UserID: "user1",
|
||||
Roles: []string{"IAM_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user not existing, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "IAM_OWNER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
UserID: "user1",
|
||||
Roles: []string{"IAM_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member already exists, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewMemberAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"user1",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "IAM_OWNER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
UserID: "user1",
|
||||
Roles: []string{"IAM_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member add uniqueconstraint err, already exists",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(),
|
||||
expectPushFailed(caos_errs.ThrowAlreadyExists(nil, "ERROR", "internal"),
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(iam.NewMemberAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"user1",
|
||||
[]string{"IAM_OWNER"}...,
|
||||
)),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("IAM", "user1")),
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "IAM_OWNER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
UserID: "user1",
|
||||
Roles: []string{"IAM_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member add, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(iam.NewMemberAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"user1",
|
||||
[]string{"IAM_OWNER"}...,
|
||||
)),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("IAM", "user1")),
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "IAM_OWNER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
UserID: "user1",
|
||||
Roles: []string{"IAM_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
ResourceOwner: "IAM",
|
||||
AggregateID: "IAM",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"IAM_OWNER"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
zitadelRoles: tt.fields.zitadelRoles,
|
||||
}
|
||||
got, err := r.AddIAMMember(tt.args.ctx, tt.args.member)
|
||||
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_ChangeIAMMember(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
zitadelRoles []authz.RoleMapping
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
member *domain.Member
|
||||
}
|
||||
type res struct {
|
||||
want *domain.Member
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid member, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid roles, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
UserID: "user1",
|
||||
Roles: []string{"IAM_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "IAM_OWNER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
UserID: "user1",
|
||||
Roles: []string{"IAM_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member not changed, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewMemberAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"user1",
|
||||
[]string{"IAM_OWNER"}...,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: domain.RoleIAMOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
UserID: "user1",
|
||||
Roles: []string{"IAM_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member change, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewMemberAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"user1",
|
||||
[]string{"IAM_OWNER"}...,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(iam.NewMemberChangedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"user1",
|
||||
[]string{"IAM_OWNER", "IAM_OWNER_VIEWER"}...,
|
||||
)),
|
||||
},
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "IAM_OWNER",
|
||||
},
|
||||
{
|
||||
Role: "IAM_OWNER_VIEWER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
UserID: "user1",
|
||||
Roles: []string{"IAM_OWNER", "IAM_OWNER_VIEWER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
ResourceOwner: "IAM",
|
||||
AggregateID: "IAM",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"IAM_OWNER", "IAM_OWNER_VIEWER"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
zitadelRoles: tt.fields.zitadelRoles,
|
||||
}
|
||||
got, err := r.ChangeIAMMember(tt.args.ctx, tt.args.member)
|
||||
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_RemoveIAMMember(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
userID string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid member userid missing, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
userID: "",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member not existing, nil result",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
userID: "user1",
|
||||
},
|
||||
res: res{
|
||||
want: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member remove, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewMemberAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"user1",
|
||||
[]string{"IAM_OWNER"}...,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(iam.NewMemberRemovedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"user1",
|
||||
)),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(member.NewRemoveMemberUniqueConstraint("IAM", "user1")),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
userID: "user1",
|
||||
},
|
||||
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.RemoveIAMMember(tt.args.ctx, 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)
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assert.Equal(t, tt.res.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -29,6 +29,9 @@ func (c *Commands) AddDefaultLabelPolicy(ctx context.Context, policy *domain.Lab
|
||||
}
|
||||
|
||||
func (c *Commands) addDefaultLabelPolicy(ctx context.Context, iamAgg *eventstore.Aggregate, addedPolicy *IAMLabelPolicyWriteModel, policy *domain.LabelPolicy) (eventstore.EventPusher, error) {
|
||||
if !policy.IsValid() {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-3m9fo", "Errors.IAM.LabelPolicy.Invalid")
|
||||
}
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -42,6 +45,9 @@ func (c *Commands) addDefaultLabelPolicy(ctx context.Context, iamAgg *eventstore
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeDefaultLabelPolicy(ctx context.Context, policy *domain.LabelPolicy) (*domain.LabelPolicy, error) {
|
||||
if !policy.IsValid() {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-33m8f", "Errors.IAM.LabelPolicy.Invalid")
|
||||
}
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
288
internal/command/iam_policy_label_test.go
Normal file
288
internal/command/iam_policy_label_test.go
Normal file
@ -0,0 +1,288 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/policy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddDefaultLabelPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.LabelPolicy
|
||||
}
|
||||
type res struct {
|
||||
want *domain.LabelPolicy
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "labelpolicy invalid, 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 already existing, already exists error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewLabelPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"primary-color",
|
||||
"secondary-color",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
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(
|
||||
iam.NewLabelPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"primary-color",
|
||||
"secondary-color",
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.LabelPolicy{
|
||||
PrimaryColor: "primary-color",
|
||||
SecondaryColor: "secondary-color",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.LabelPolicy{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
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.AddDefaultLabelPolicy(tt.args.ctx, 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_ChangeDefaultLabelPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.LabelPolicy
|
||||
}
|
||||
type res struct {
|
||||
want *domain.LabelPolicy
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "labelpolicy invalid, 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 not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
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(
|
||||
iam.NewLabelPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"primary-color",
|
||||
"secondary-color",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
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(
|
||||
iam.NewLabelPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"primary-color",
|
||||
"secondary-color",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
newDefaultLabelPolicyChangedEvent(context.Background(), "primary-color-change", "secondary-color-change"),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.LabelPolicy{
|
||||
PrimaryColor: "primary-color-change",
|
||||
SecondaryColor: "secondary-color-change",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.LabelPolicy{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
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.ChangeDefaultLabelPolicy(tt.args.ctx, 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 newDefaultLabelPolicyChangedEvent(ctx context.Context, primaryColor, secondaryColor string) *iam.LabelPolicyChangedEvent {
|
||||
event, _ := iam.NewLabelPolicyChangedEvent(ctx,
|
||||
&iam.NewAggregate().Aggregate,
|
||||
[]policy.LabelPolicyChanges{
|
||||
policy.ChangePrimaryColor(primaryColor),
|
||||
policy.ChangeSecondaryColor(secondaryColor),
|
||||
},
|
||||
)
|
||||
return event
|
||||
}
|
@ -28,11 +28,14 @@ func (c *Commands) AddDefaultLoginPolicy(ctx context.Context, policy *domain.Log
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = c.eventstore.PushEvents(ctx, event)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(addedPolicy, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return writeModelToLoginPolicy(&addedPolicy.LoginPolicyWriteModel), nil
|
||||
}
|
||||
|
||||
|
283
internal/command/iam_policy_login_test.go
Normal file
283
internal/command/iam_policy_login_test.go
Normal file
@ -0,0 +1,283 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/policy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.LoginPolicy
|
||||
}
|
||||
type res struct {
|
||||
want *domain.LoginPolicy
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "loginpolicy already existing, already exists error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewLoginPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.LoginPolicy{
|
||||
AllowRegister: true,
|
||||
AllowUsernamePassword: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add policy,ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
iam.NewLoginPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.LoginPolicy{
|
||||
AllowRegister: true,
|
||||
AllowUsernamePassword: true,
|
||||
AllowExternalIDP: true,
|
||||
ForceMFA: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.LoginPolicy{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
AllowRegister: true,
|
||||
AllowUsernamePassword: true,
|
||||
AllowExternalIDP: true,
|
||||
ForceMFA: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.AddDefaultLoginPolicy(tt.args.ctx, 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_ChangeDefaultLoginPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.LoginPolicy
|
||||
}
|
||||
type res struct {
|
||||
want *domain.LoginPolicy
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "loginpolicy not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.LoginPolicy{
|
||||
AllowRegister: true,
|
||||
AllowExternalIDP: true,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewLoginPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.LoginPolicy{
|
||||
AllowRegister: true,
|
||||
AllowUsernamePassword: true,
|
||||
AllowExternalIDP: true,
|
||||
ForceMFA: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "change, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewLoginPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
newDefaultLoginPolicyChangedEvent(context.Background(), false, false, false, false, domain.PasswordlessTypeNotAllowed),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.LoginPolicy{
|
||||
AllowRegister: false,
|
||||
AllowUsernamePassword: false,
|
||||
AllowExternalIDP: false,
|
||||
ForceMFA: false,
|
||||
PasswordlessType: domain.PasswordlessTypeNotAllowed,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.LoginPolicy{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
AllowRegister: false,
|
||||
AllowUsernamePassword: false,
|
||||
AllowExternalIDP: false,
|
||||
ForceMFA: false,
|
||||
PasswordlessType: domain.PasswordlessTypeNotAllowed,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.ChangeDefaultLoginPolicy(tt.args.ctx, 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 newDefaultLoginPolicyChangedEvent(ctx context.Context, allowRegister, allowUsernamePassword, allowExternalIDP, forceMFA bool, passwordlessType domain.PasswordlessType) *iam.LoginPolicyChangedEvent {
|
||||
event, _ := iam.NewLoginPolicyChangedEvent(ctx,
|
||||
&iam.NewAggregate().Aggregate,
|
||||
[]policy.LoginPolicyChanges{
|
||||
policy.ChangeAllowRegister(allowRegister),
|
||||
policy.ChangeAllowExternalIDP(allowExternalIDP),
|
||||
policy.ChangeForceMFA(forceMFA),
|
||||
policy.ChangeAllowUserNamePassword(allowUsernamePassword),
|
||||
policy.ChangePasswordlessType(passwordlessType),
|
||||
},
|
||||
)
|
||||
return event
|
||||
}
|
@ -30,7 +30,7 @@ func (c *Commands) AddDefaultMailTemplate(ctx context.Context, policy *domain.Ma
|
||||
|
||||
func (c *Commands) addDefaultMailTemplate(ctx context.Context, iamAgg *eventstore.Aggregate, addedPolicy *IAMMailTemplateWriteModel, policy *domain.MailTemplate) (eventstore.EventPusher, error) {
|
||||
if !policy.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-fm9sd", "Errors.IAM.MailTemplate.Invalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-fm9sd", "Errors.IAM.MailTemplate.Invalid")
|
||||
}
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
||||
if err != nil {
|
||||
@ -45,7 +45,7 @@ func (c *Commands) addDefaultMailTemplate(ctx context.Context, iamAgg *eventstor
|
||||
|
||||
func (c *Commands) ChangeDefaultMailTemplate(ctx context.Context, policy *domain.MailTemplate) (*domain.MailTemplate, error) {
|
||||
if !policy.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4m9ds", "Errors.IAM.MailTemplate.Invalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-4m9ds", "Errors.IAM.MailTemplate.Invalid")
|
||||
}
|
||||
existingPolicy, err := c.defaultMailTemplateWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
|
270
internal/command/iam_policy_mail_template_test.go
Normal file
270
internal/command/iam_policy_mail_template_test.go
Normal file
@ -0,0 +1,270 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/policy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddDefaultMailTemplatePolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.MailTemplate
|
||||
}
|
||||
type res struct {
|
||||
want *domain.MailTemplate
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "mailtemplate invalid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.MailTemplate{},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mailtemplate already existing, already exists error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewMailTemplateAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
[]byte("template"),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.MailTemplate{
|
||||
Template: []byte("template"),
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add mail template,ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
iam.NewMailTemplateAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
[]byte("template"),
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.MailTemplate{
|
||||
Template: []byte("template"),
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.MailTemplate{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
Template: []byte("template"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.AddDefaultMailTemplate(tt.args.ctx, 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_ChangeDefaultMailTemplatePolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.MailTemplate
|
||||
}
|
||||
type res struct {
|
||||
want *domain.MailTemplate
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "mailtemplate invalid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.MailTemplate{},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mailtempalte not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.MailTemplate{
|
||||
Template: []byte("template-change"),
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewMailTemplateAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
[]byte("template"),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.MailTemplate{
|
||||
Template: []byte("template"),
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "change, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewMailTemplateAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
[]byte("template"),
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
newDefaultMailTemplatePolicyChangedEvent(context.Background(), []byte("template-change")),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.MailTemplate{
|
||||
Template: []byte("template-change"),
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.MailTemplate{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
Template: []byte("template-change"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.ChangeDefaultMailTemplate(tt.args.ctx, 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 newDefaultMailTemplatePolicyChangedEvent(ctx context.Context, template []byte) *iam.MailTemplateChangedEvent {
|
||||
event, _ := iam.NewMailTemplateChangedEvent(ctx,
|
||||
&iam.NewAggregate().Aggregate,
|
||||
[]policy.MailTemplateChanges{
|
||||
policy.ChangeTemplate(template),
|
||||
},
|
||||
)
|
||||
return event
|
||||
}
|
@ -30,7 +30,7 @@ func (c *Commands) AddDefaultMailText(ctx context.Context, policy *domain.MailTe
|
||||
|
||||
func (c *Commands) addDefaultMailText(ctx context.Context, iamAgg *eventstore.Aggregate, addedPolicy *IAMMailTextWriteModel, mailText *domain.MailText) (eventstore.EventPusher, error) {
|
||||
if !mailText.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-3n8fs", "Errors.IAM.MailText.Invalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-3n8fs", "Errors.IAM.MailText.Invalid")
|
||||
}
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
||||
if err != nil {
|
||||
@ -55,7 +55,7 @@ func (c *Commands) addDefaultMailText(ctx context.Context, iamAgg *eventstore.Ag
|
||||
|
||||
func (c *Commands) ChangeDefaultMailText(ctx context.Context, mailText *domain.MailText) (*domain.MailText, error) {
|
||||
if !mailText.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-kd9fs", "Errors.IAM.MailText.Invalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-kd9fs", "Errors.IAM.MailText.Invalid")
|
||||
}
|
||||
existingPolicy, err := c.defaultMailTextWriteModelByID(ctx, mailText.MailTextType, mailText.Language)
|
||||
if err != nil {
|
||||
|
366
internal/command/iam_policy_mail_text_test.go
Normal file
366
internal/command/iam_policy_mail_text_test.go
Normal file
@ -0,0 +1,366 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/policy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddDefaultMailTextPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.MailText
|
||||
}
|
||||
type res struct {
|
||||
want *domain.MailText
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "mail text 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 text already existing, already exists error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewMailTextAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"mail-text-type",
|
||||
"de",
|
||||
"title",
|
||||
"pre-header",
|
||||
"subject",
|
||||
"greeting",
|
||||
"text",
|
||||
"button-text",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
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.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add mail template,ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
iam.NewMailTextAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"mail-text-type",
|
||||
"de",
|
||||
"title",
|
||||
"pre-header",
|
||||
"subject",
|
||||
"greeting",
|
||||
"text",
|
||||
"button-text",
|
||||
),
|
||||
),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(policy.NewAddMailTextUniqueConstraint("IAM", "mail-text-type", "de")),
|
||||
),
|
||||
),
|
||||
},
|
||||
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{
|
||||
want: &domain.MailText{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
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.AddDefaultMailText(tt.args.ctx, 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_ChangeDefaultMailTextPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.MailText
|
||||
}
|
||||
type res struct {
|
||||
want *domain.MailText
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
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 text not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
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.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewMailTextAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"mail-text-type",
|
||||
"de",
|
||||
"title",
|
||||
"pre-header",
|
||||
"subject",
|
||||
"greeting",
|
||||
"text",
|
||||
"button-text",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
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.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "change, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewMailTextAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"mail-text-type",
|
||||
"de",
|
||||
"title",
|
||||
"pre-header",
|
||||
"subject",
|
||||
"greeting",
|
||||
"text",
|
||||
"button-text",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
newDefaultMailTextPolicyChangedEvent(
|
||||
context.Background(),
|
||||
"mail-text-type",
|
||||
"de",
|
||||
"title-change",
|
||||
"pre-header-change",
|
||||
"subject-change",
|
||||
"greeting-change",
|
||||
"text-change",
|
||||
"button-text-change"),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
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: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
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.ChangeDefaultMailText(tt.args.ctx, 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 newDefaultMailTextPolicyChangedEvent(ctx context.Context, mailTextType, language, title, preHeader, subject, greeting, text, buttonText string) *iam.MailTextChangedEvent {
|
||||
event, _ := iam.NewMailTextChangedEvent(ctx,
|
||||
&iam.NewAggregate().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
|
||||
}
|
240
internal/command/iam_policy_org_iam_test.go
Normal file
240
internal/command/iam_policy_org_iam_test.go
Normal file
@ -0,0 +1,240 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/policy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddDefaultOrgIAMPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.OrgIAMPolicy
|
||||
}
|
||||
type res struct {
|
||||
want *domain.OrgIAMPolicy
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "orgiam policy already existing, already exists error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewOrgIAMPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
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(
|
||||
iam.NewOrgIAMPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
true,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.OrgIAMPolicy{
|
||||
UserLoginMustBeDomain: true,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.OrgIAMPolicy{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
UserLoginMustBeDomain: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.AddDefaultOrgIAMPolicy(tt.args.ctx, 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_ChangeDefaultOrgIAMPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.OrgIAMPolicy
|
||||
}
|
||||
type res struct {
|
||||
want *domain.OrgIAMPolicy
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "orgiampolicy not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.OrgIAMPolicy{
|
||||
UserLoginMustBeDomain: true,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewOrgIAMPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.OrgIAMPolicy{
|
||||
UserLoginMustBeDomain: true,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "change, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewOrgIAMPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
newDefaultOrgIAMPolicyChangedEvent(context.Background(), false),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.OrgIAMPolicy{
|
||||
UserLoginMustBeDomain: false,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.OrgIAMPolicy{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
UserLoginMustBeDomain: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.ChangeDefaultOrgIAMPolicy(tt.args.ctx, 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 newDefaultOrgIAMPolicyChangedEvent(ctx context.Context, userLoginMustBeDomain bool) *iam.OrgIAMPolicyChangedEvent {
|
||||
event, _ := iam.NewOrgIAMPolicyChangedEvent(ctx,
|
||||
&iam.NewAggregate().Aggregate,
|
||||
[]policy.OrgIAMPolicyChanges{
|
||||
policy.ChangeUserLoginMustBeDomain(userLoginMustBeDomain),
|
||||
},
|
||||
)
|
||||
return event
|
||||
}
|
252
internal/command/iam_policy_password_age_test.go
Normal file
252
internal/command/iam_policy_password_age_test.go
Normal file
@ -0,0 +1,252 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/policy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddDefaultPasswordAgePolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.PasswordAgePolicy
|
||||
}
|
||||
type res struct {
|
||||
want *domain.PasswordAgePolicy
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "password age policy already existing, already exists error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewPasswordAgePolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
365,
|
||||
10,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
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(
|
||||
iam.NewPasswordAgePolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
365,
|
||||
10,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.PasswordAgePolicy{
|
||||
ExpireWarnDays: 365,
|
||||
MaxAgeDays: 10,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.PasswordAgePolicy{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
ExpireWarnDays: 365,
|
||||
MaxAgeDays: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.AddDefaultPasswordAgePolicy(tt.args.ctx, 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_ChangeDefaultPasswordAgePolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.PasswordAgePolicy
|
||||
}
|
||||
type res struct {
|
||||
want *domain.PasswordAgePolicy
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "password age policy not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
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(
|
||||
iam.NewPasswordAgePolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
365,
|
||||
10,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.PasswordAgePolicy{
|
||||
ExpireWarnDays: 365,
|
||||
MaxAgeDays: 10,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "change, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewPasswordAgePolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
365,
|
||||
10,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
newDefaultPasswordAgePolicyChangedEvent(context.Background(), 125, 5),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.PasswordAgePolicy{
|
||||
MaxAgeDays: 125,
|
||||
ExpireWarnDays: 5,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.PasswordAgePolicy{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
MaxAgeDays: 125,
|
||||
ExpireWarnDays: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.ChangeDefaultPasswordAgePolicy(tt.args.ctx, 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 newDefaultPasswordAgePolicyChangedEvent(ctx context.Context, maxAgeDays, expiryWarnDays uint64) *iam.PasswordAgePolicyChangedEvent {
|
||||
event, _ := iam.NewPasswordAgePolicyChangedEvent(ctx,
|
||||
&iam.NewAggregate().Aggregate,
|
||||
[]policy.PasswordAgePolicyChanges{
|
||||
policy.ChangeExpireWarnDays(expiryWarnDays),
|
||||
policy.ChangeMaxAgeDays(maxAgeDays),
|
||||
},
|
||||
)
|
||||
return event
|
||||
}
|
318
internal/command/iam_policy_password_complexity_test.go
Normal file
318
internal/command/iam_policy_password_complexity_test.go
Normal file
@ -0,0 +1,318 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/policy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddDefaultPasswordComplexityPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.PasswordComplexityPolicy
|
||||
}
|
||||
type res struct {
|
||||
want *domain.PasswordComplexityPolicy
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid policy, 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: "password complexity policy already existing, already exists error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
8,
|
||||
true, true, true, true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
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(
|
||||
iam.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
8,
|
||||
true, true, true, true,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.PasswordComplexityPolicy{
|
||||
MinLength: 8,
|
||||
HasUppercase: true,
|
||||
HasLowercase: true,
|
||||
HasNumber: true,
|
||||
HasSymbol: true,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.PasswordComplexityPolicy{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
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.AddDefaultPasswordComplexityPolicy(tt.args.ctx, 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_ChangeDefaultPasswordComplexityPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.PasswordComplexityPolicy
|
||||
}
|
||||
type res struct {
|
||||
want *domain.PasswordComplexityPolicy
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid policy, 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: "password complexity policy not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
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(
|
||||
iam.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
8,
|
||||
true, true, true, true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
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(
|
||||
iam.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
8,
|
||||
true, true, true, true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
newDefaultPasswordComplexityPolicyChangedEvent(context.Background(), 10, false, false, false, false),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.PasswordComplexityPolicy{
|
||||
MinLength: 10,
|
||||
HasUppercase: false,
|
||||
HasLowercase: false,
|
||||
HasNumber: false,
|
||||
HasSymbol: false,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.PasswordComplexityPolicy{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
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.ChangeDefaultPasswordComplexityPolicy(tt.args.ctx, 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 newDefaultPasswordComplexityPolicyChangedEvent(ctx context.Context, minLength uint64, hasUpper, hasLower, hasNumber, hasSymbol bool) *iam.PasswordComplexityPolicyChangedEvent {
|
||||
event, _ := iam.NewPasswordComplexityPolicyChangedEvent(ctx,
|
||||
&iam.NewAggregate().Aggregate,
|
||||
[]policy.PasswordComplexityPolicyChanges{
|
||||
policy.ChangeMinLength(minLength),
|
||||
policy.ChangeHasUppercase(hasUpper),
|
||||
policy.ChangeHasLowercase(hasLower),
|
||||
policy.ChangeHasSymbol(hasNumber),
|
||||
policy.ChangeHasNumber(hasSymbol),
|
||||
},
|
||||
)
|
||||
return event
|
||||
}
|
252
internal/command/iam_policy_password_lockout_test.go
Normal file
252
internal/command/iam_policy_password_lockout_test.go
Normal file
@ -0,0 +1,252 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/policy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.PasswordLockoutPolicy
|
||||
}
|
||||
type res struct {
|
||||
want *domain.PasswordLockoutPolicy
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "password lockout policy already existing, already exists error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
10,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
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(
|
||||
iam.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
10,
|
||||
true,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.PasswordLockoutPolicy{
|
||||
MaxAttempts: 10,
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.PasswordLockoutPolicy{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
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.AddDefaultPasswordLockoutPolicy(tt.args.ctx, 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_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.PasswordLockoutPolicy
|
||||
}
|
||||
type res struct {
|
||||
want *domain.PasswordLockoutPolicy
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "password lockout policy not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
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(
|
||||
iam.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
10,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.PasswordLockoutPolicy{
|
||||
MaxAttempts: 10,
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "change, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
10,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
newDefaultPasswordLockoutPolicyChangedEvent(context.Background(), 20, false),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.PasswordLockoutPolicy{
|
||||
MaxAttempts: 20,
|
||||
ShowLockOutFailures: false,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.PasswordLockoutPolicy{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
MaxAttempts: 20,
|
||||
ShowLockOutFailures: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.ChangeDefaultPasswordLockoutPolicy(tt.args.ctx, 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 newDefaultPasswordLockoutPolicyChangedEvent(ctx context.Context, maxAttempts uint64, showLockoutFailure bool) *iam.PasswordLockoutPolicyChangedEvent {
|
||||
event, _ := iam.NewPasswordLockoutPolicyChangedEvent(ctx,
|
||||
&iam.NewAggregate().Aggregate,
|
||||
[]policy.PasswordLockoutPolicyChanges{
|
||||
policy.ChangeMaxAttempts(maxAttempts),
|
||||
policy.ChangeShowLockOutFailures(showLockoutFailure),
|
||||
},
|
||||
)
|
||||
return event
|
||||
}
|
168
internal/command/main_test.go
Normal file
168
internal/command/main_test.go
Normal file
@ -0,0 +1,168 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||
"github.com/caos/zitadel/internal/eventstore/repository/mock"
|
||||
iam_repo "github.com/caos/zitadel/internal/repository/iam"
|
||||
key_repo "github.com/caos/zitadel/internal/repository/keypair"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
proj_repo "github.com/caos/zitadel/internal/repository/project"
|
||||
usr_repo "github.com/caos/zitadel/internal/repository/user"
|
||||
"github.com/caos/zitadel/internal/repository/usergrant"
|
||||
"github.com/golang/mock/gomock"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type expect func(mockRepository *mock.MockRepository)
|
||||
|
||||
func eventstoreExpect(t *testing.T, expects ...expect) *eventstore.Eventstore {
|
||||
m := mock.NewRepo(t)
|
||||
for _, e := range expects {
|
||||
e(m)
|
||||
}
|
||||
es := eventstore.NewEventstore(m)
|
||||
iam_repo.RegisterEventMappers(es)
|
||||
org.RegisterEventMappers(es)
|
||||
usr_repo.RegisterEventMappers(es)
|
||||
proj_repo.RegisterEventMappers(es)
|
||||
usergrant.RegisterEventMappers(es)
|
||||
key_repo.RegisterEventMappers(es)
|
||||
return es
|
||||
}
|
||||
|
||||
func eventPusherToEvents(eventsPushes ...eventstore.EventPusher) []*repository.Event {
|
||||
events := make([]*repository.Event, len(eventsPushes))
|
||||
for i, event := range eventsPushes {
|
||||
data, err := eventstore.EventData(event)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
events[i] = &repository.Event{
|
||||
AggregateID: event.Aggregate().ID,
|
||||
AggregateType: repository.AggregateType(event.Aggregate().Typ),
|
||||
ResourceOwner: event.Aggregate().ResourceOwner,
|
||||
EditorService: event.EditorService(),
|
||||
EditorUser: event.EditorUser(),
|
||||
Type: repository.EventType(event.Type()),
|
||||
Version: repository.Version(event.Aggregate().Version),
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
type testRepo struct {
|
||||
events []*repository.Event
|
||||
uniqueConstraints []*repository.UniqueConstraint
|
||||
sequence uint64
|
||||
err error
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (repo *testRepo) Health(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *testRepo) Push(ctx context.Context, events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) error {
|
||||
repo.events = append(repo.events, events...)
|
||||
repo.uniqueConstraints = append(repo.uniqueConstraints, uniqueConstraints...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *testRepo) Filter(ctx context.Context, searchQuery *repository.SearchQuery) ([]*repository.Event, error) {
|
||||
events := make([]*repository.Event, 0, len(repo.events))
|
||||
for _, event := range repo.events {
|
||||
for _, filter := range searchQuery.Filters {
|
||||
if filter.Field == repository.FieldAggregateType {
|
||||
if event.AggregateType != filter.Value {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
events = append(events, event)
|
||||
}
|
||||
return repo.events, nil
|
||||
}
|
||||
|
||||
func filterAggregateType(aggregateType string) {
|
||||
|
||||
}
|
||||
|
||||
func (repo *testRepo) LatestSequence(ctx context.Context, queryFactory *repository.SearchQuery) (uint64, error) {
|
||||
if repo.err != nil {
|
||||
return 0, repo.err
|
||||
}
|
||||
return repo.sequence, nil
|
||||
}
|
||||
|
||||
func expectPush(events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) expect {
|
||||
return func(m *mock.MockRepository) {
|
||||
m.ExpectPush(events, uniqueConstraints...)
|
||||
}
|
||||
}
|
||||
|
||||
func expectPushFailed(err error, events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) expect {
|
||||
return func(m *mock.MockRepository) {
|
||||
m.ExpectPushFailed(err, events, uniqueConstraints...)
|
||||
}
|
||||
}
|
||||
|
||||
func expectFilter(events ...*repository.Event) expect {
|
||||
return func(m *mock.MockRepository) {
|
||||
m.ExpectFilterEvents(events...)
|
||||
}
|
||||
}
|
||||
|
||||
func expectFilterOrgDomainNotFound() expect {
|
||||
return func(m *mock.MockRepository) {
|
||||
m.ExpectFilterNoEventsNoError()
|
||||
}
|
||||
}
|
||||
|
||||
func expectFilterOrgMemberNotFound() expect {
|
||||
return func(m *mock.MockRepository) {
|
||||
m.ExpectFilterNoEventsNoError()
|
||||
}
|
||||
}
|
||||
|
||||
func eventFromEventPusher(event eventstore.EventPusher) *repository.Event {
|
||||
data, _ := eventstore.EventData(event)
|
||||
return &repository.Event{
|
||||
ID: "",
|
||||
Sequence: 0,
|
||||
PreviousSequence: 0,
|
||||
CreationDate: time.Time{},
|
||||
Type: repository.EventType(event.Type()),
|
||||
Data: data,
|
||||
EditorService: event.EditorService(),
|
||||
EditorUser: event.EditorUser(),
|
||||
Version: repository.Version(event.Aggregate().Version),
|
||||
AggregateID: event.Aggregate().ID,
|
||||
AggregateType: repository.AggregateType(event.Aggregate().Typ),
|
||||
ResourceOwner: event.Aggregate().ResourceOwner,
|
||||
}
|
||||
}
|
||||
|
||||
func uniqueConstraintsFromEventConstraint(constraint *eventstore.EventUniqueConstraint) *repository.UniqueConstraint {
|
||||
return &repository.UniqueConstraint{
|
||||
UniqueType: constraint.UniqueType,
|
||||
UniqueField: constraint.UniqueField,
|
||||
ErrorMessage: constraint.ErrorMessage,
|
||||
Action: repository.UniqueConstraintAction(constraint.Action)}
|
||||
}
|
||||
|
||||
func GetMockSecretGenerator(t *testing.T) crypto.Generator {
|
||||
ctrl := gomock.NewController(t)
|
||||
alg := crypto.CreateMockEncryptionAlg(ctrl)
|
||||
generator := crypto.NewMockGenerator(ctrl)
|
||||
generator.EXPECT().Length().Return(uint(1)).AnyTimes()
|
||||
generator.EXPECT().Runes().Return([]rune("aa")).AnyTimes()
|
||||
generator.EXPECT().Alg().Return(alg).AnyTimes()
|
||||
generator.EXPECT().Expiry().Return(time.Hour * 1).AnyTimes()
|
||||
|
||||
return generator
|
||||
}
|
@ -82,7 +82,7 @@ func (c *Commands) DeactivateOrg(ctx context.Context, orgID string) (*domain.Obj
|
||||
return nil, caos_errs.ThrowNotFound(nil, "ORG-oL9nT", "Errors.Org.NotFound")
|
||||
}
|
||||
if orgWriteModel.State == domain.OrgStateInactive {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "EVENT-Dbs2g", "Errors.Org.AlreadyDeactivated")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-Dbs2g", "Errors.Org.AlreadyDeactivated")
|
||||
}
|
||||
orgAgg := OrgAggregateFromWriteModel(&orgWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewOrgDeactivatedEvent(ctx, orgAgg))
|
||||
@ -105,7 +105,7 @@ func (c *Commands) ReactivateOrg(ctx context.Context, orgID string) (*domain.Obj
|
||||
return nil, caos_errs.ThrowNotFound(nil, "ORG-Dgf3g", "Errors.Org.NotFound")
|
||||
}
|
||||
if orgWriteModel.State == domain.OrgStateActive {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "EVENT-bfnrh", "Errors.Org.AlreadyActive")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-bfnrh", "Errors.Org.AlreadyActive")
|
||||
}
|
||||
orgAgg := OrgAggregateFromWriteModel(&orgWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewOrgReactivatedEvent(ctx, orgAgg))
|
||||
|
@ -19,7 +19,6 @@ func (c *Commands) AddOrgMember(ctx context.Context, member *domain.Member) (*do
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -33,13 +32,16 @@ func (c *Commands) AddOrgMember(ctx context.Context, member *domain.Member) (*do
|
||||
|
||||
func (c *Commands) addOrgMember(ctx context.Context, orgAgg *eventstore.Aggregate, addedMember *OrgMemberWriteModel, member *domain.Member) (eventstore.EventPusher, error) {
|
||||
if !member.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-W8m4l", "Errors.Org.MemberInvalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-W8m4l", "Errors.Org.MemberInvalid")
|
||||
}
|
||||
if len(domain.CheckForInvalidRoles(member.Roles, domain.OrgRolePrefix, c.zitadelRoles)) > 0 {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-3m9fs", "Errors.Org.MemberInvalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-3m9fs", "Errors.Org.MemberInvalid")
|
||||
}
|
||||
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, addedMember)
|
||||
err := c.checkUserExists(ctx, addedMember.UserID, "")
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "ORG-9cmsd", "Errors.User.NotFound")
|
||||
}
|
||||
err = c.eventstore.FilterToQueryReducer(ctx, addedMember)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -53,10 +55,10 @@ func (c *Commands) addOrgMember(ctx context.Context, orgAgg *eventstore.Aggregat
|
||||
//ChangeOrgMember updates an existing member
|
||||
func (c *Commands) ChangeOrgMember(ctx context.Context, member *domain.Member) (*domain.Member, error) {
|
||||
if !member.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-LiaZi", "Errors.Org.MemberInvalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-LiaZi", "Errors.Org.MemberInvalid")
|
||||
}
|
||||
if len(domain.CheckForInvalidRoles(member.Roles, domain.OrgRolePrefix, c.zitadelRoles)) > 0 {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-m9fG8", "Errors.Org.MemberInvalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-m9fG8", "Errors.Org.MemberInvalid")
|
||||
}
|
||||
|
||||
existingMember, err := c.orgMemberWriteModelByID(ctx, member.AggregateID, member.UserID)
|
||||
|
615
internal/command/org_member_test.go
Normal file
615
internal/command/org_member_test.go
Normal file
@ -0,0 +1,615 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"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/member"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
"github.com/caos/zitadel/internal/repository/project"
|
||||
"github.com/caos/zitadel/internal/repository/user"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddOrgMember(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
zitadelRoles []authz.RoleMapping
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
member *domain.Member
|
||||
}
|
||||
type res struct {
|
||||
want *domain.Member
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid member, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid roles, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "org1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"ORG_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user not existing, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: domain.RoleOrgOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "org1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{domain.RoleOrgOwner},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member already exists, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewMemberAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"user1",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: domain.RoleOrgOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "org1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"ORG_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member add uniqueconstraint err, already exists",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(),
|
||||
expectPushFailed(caos_errs.ThrowAlreadyExists(nil, "ERROR", "internal"),
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(org.NewMemberAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"user1",
|
||||
[]string{"ORG_OWNER"}...,
|
||||
)),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("org1", "user1")),
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: domain.RoleOrgOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "org1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"ORG_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member add, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(org.NewMemberAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"user1",
|
||||
[]string{"ORG_OWNER"}...,
|
||||
)),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("org1", "user1")),
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: domain.RoleOrgOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "org1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"ORG_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
ResourceOwner: "org1",
|
||||
AggregateID: "org1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{domain.RoleOrgOwner},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
zitadelRoles: tt.fields.zitadelRoles,
|
||||
}
|
||||
got, err := r.AddOrgMember(tt.args.ctx, tt.args.member)
|
||||
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_ChangeOrgMember(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
zitadelRoles []authz.RoleMapping
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
member *domain.Member
|
||||
}
|
||||
type res struct {
|
||||
want *domain.Member
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid member, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid roles, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "org1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: domain.RoleOrgOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "org1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"ORG_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member not changed, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewMemberAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"user1",
|
||||
[]string{"ORG_OWNER"}...,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: domain.RoleOrgOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "org1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"ORG_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member change, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewMemberAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"user1",
|
||||
[]string{"ORG_OWNER"}...,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(org.NewMemberChangedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"user1",
|
||||
[]string{"ORG_OWNER", "ORG_OWNER_VIEWER"}...,
|
||||
)),
|
||||
},
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "ORG_OWNER",
|
||||
},
|
||||
{
|
||||
Role: "ORG_OWNER_VIEWER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "org1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"ORG_OWNER", "ORG_OWNER_VIEWER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
ResourceOwner: "org1",
|
||||
AggregateID: "org1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"ORG_OWNER", "ORG_OWNER_VIEWER"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
zitadelRoles: tt.fields.zitadelRoles,
|
||||
}
|
||||
got, err := r.ChangeOrgMember(tt.args.ctx, tt.args.member)
|
||||
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_RemoveOrgMember(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
projectID string
|
||||
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: "invalid member projectid missing, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "",
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid member userid missing, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
userID: "",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member not existing, nil result",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member remove, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectMemberAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"user1",
|
||||
[]string{"PROJECT_OWNER"}...,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(project.NewProjectMemberRemovedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"user1",
|
||||
)),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(member.NewRemoveMemberUniqueConstraint("project1", "user1")),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
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.RemoveProjectMember(tt.args.ctx, tt.args.projectID, 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -29,6 +29,8 @@ func (wm *OrgWriteModel) Reduce() error {
|
||||
case *org.OrgAddedEvent:
|
||||
wm.Name = e.Name
|
||||
wm.State = domain.OrgStateActive
|
||||
case *org.OrgDeactivatedEvent:
|
||||
wm.State = domain.OrgStateInactive
|
||||
case *org.OrgChangedEvent:
|
||||
wm.Name = e.Name
|
||||
case *org.DomainPrimarySetEvent:
|
||||
|
663
internal/command/org_test.go
Normal file
663
internal/command/org_test.go
Normal file
@ -0,0 +1,663 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"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/member"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
"github.com/caos/zitadel/internal/repository/user"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/text/language"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddOrg(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
iamDomain string
|
||||
zitadelRoles []authz.RoleMapping
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
name string
|
||||
userID string
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.Org
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid org, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user removed, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilterOrgDomainNotFound(),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewUserRemovedEvent(
|
||||
context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "org2"),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
name: "Org",
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "push failed unique constraint, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilterOrgDomainNotFound(),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrgMemberNotFound(),
|
||||
expectPushFailed(caos_errs.ThrowAlreadyExists(nil, "id", "internal"),
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(org.NewOrgAddedEvent(
|
||||
context.Background(),
|
||||
&org.NewAggregate("org2", "org2").Aggregate,
|
||||
"Org")),
|
||||
eventFromEventPusher(org.NewDomainAddedEvent(
|
||||
context.Background(),
|
||||
&org.NewAggregate("org2", "org2").Aggregate,
|
||||
"org.iam-domain")),
|
||||
eventFromEventPusher(org.NewDomainVerifiedEvent(
|
||||
context.Background(),
|
||||
&org.NewAggregate("org2", "org2").Aggregate,
|
||||
"org.iam-domain")),
|
||||
eventFromEventPusher(org.NewDomainPrimarySetEvent(
|
||||
context.Background(),
|
||||
&org.NewAggregate("org2", "org2").Aggregate,
|
||||
"org.iam-domain")),
|
||||
eventFromEventPusher(org.NewMemberAddedEvent(
|
||||
context.Background(),
|
||||
&org.NewAggregate("org2", "org2").Aggregate,
|
||||
"user1", domain.RoleOrgOwner)),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(org.NewAddOrgNameUniqueConstraint("Org")),
|
||||
uniqueConstraintsFromEventConstraint(org.NewAddOrgDomainUniqueConstraint("org.iam-domain")),
|
||||
uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("org2", "user1")),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "org2"),
|
||||
iamDomain: "iam-domain",
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "ORG_OWNER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
name: "Org",
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "push failed, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilterOrgDomainNotFound(),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrgMemberNotFound(),
|
||||
expectPushFailed(caos_errs.ThrowInternal(nil, "id", "internal"),
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(org.NewOrgAddedEvent(
|
||||
context.Background(),
|
||||
&org.NewAggregate("org2", "org2").Aggregate,
|
||||
"Org")),
|
||||
eventFromEventPusher(org.NewDomainAddedEvent(
|
||||
context.Background(),
|
||||
&org.NewAggregate("org2", "org2").Aggregate,
|
||||
"org.iam-domain")),
|
||||
eventFromEventPusher(org.NewDomainVerifiedEvent(
|
||||
context.Background(),
|
||||
&org.NewAggregate("org2", "org2").Aggregate,
|
||||
"org.iam-domain")),
|
||||
eventFromEventPusher(org.NewDomainPrimarySetEvent(
|
||||
context.Background(),
|
||||
&org.NewAggregate("org2", "org2").Aggregate,
|
||||
"org.iam-domain")),
|
||||
eventFromEventPusher(org.NewMemberAddedEvent(
|
||||
context.Background(),
|
||||
&org.NewAggregate("org2", "org2").Aggregate,
|
||||
"user1", domain.RoleOrgOwner)),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(org.NewAddOrgNameUniqueConstraint("Org")),
|
||||
uniqueConstraintsFromEventConstraint(org.NewAddOrgDomainUniqueConstraint("org.iam-domain")),
|
||||
uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("org2", "user1")),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "org2"),
|
||||
iamDomain: "iam-domain",
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "ORG_OWNER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
name: "Org",
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsInternal,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add org, no error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilterOrgDomainNotFound(),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrgMemberNotFound(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(org.NewOrgAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org2", "org2").Aggregate,
|
||||
"Org",
|
||||
)),
|
||||
eventFromEventPusher(org.NewDomainAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org2", "org2").Aggregate, "org.iam-domain",
|
||||
)),
|
||||
eventFromEventPusher(org.NewDomainVerifiedEvent(context.Background(),
|
||||
&org.NewAggregate("org2", "org2").Aggregate,
|
||||
"org.iam-domain",
|
||||
)),
|
||||
eventFromEventPusher(org.NewDomainPrimarySetEvent(context.Background(),
|
||||
&org.NewAggregate("org2", "org2").Aggregate,
|
||||
"org.iam-domain",
|
||||
)),
|
||||
eventFromEventPusher(org.NewMemberAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org2", "org2").Aggregate,
|
||||
"user1",
|
||||
domain.RoleOrgOwner,
|
||||
)),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(org.NewAddOrgNameUniqueConstraint("Org")),
|
||||
uniqueConstraintsFromEventConstraint(org.NewAddOrgDomainUniqueConstraint("org.iam-domain")),
|
||||
uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("org2", "user1")),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "org2"),
|
||||
iamDomain: "iam-domain",
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "ORG_OWNER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
name: "Org",
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.Org{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "org2",
|
||||
ResourceOwner: "org2",
|
||||
},
|
||||
Name: "Org",
|
||||
State: domain.OrgStateActive,
|
||||
PrimaryDomain: "org.iam-domain",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
iamDomain: tt.fields.iamDomain,
|
||||
zitadelRoles: tt.fields.zitadelRoles,
|
||||
}
|
||||
got, err := r.AddOrg(tt.args.ctx, tt.args.name, 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_DeactivateOrg(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
iamDomain string
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
orgID string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.Org
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "org not found, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org already inactive, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"org"),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDeactivatedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "push failed, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"org"),
|
||||
),
|
||||
),
|
||||
expectPushFailed(
|
||||
caos_errs.ThrowInternal(nil, "id", "message"),
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(org.NewOrgDeactivatedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate)),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsInternal,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deactivate org",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"org"),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(org.NewOrgDeactivatedEvent(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,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
}
|
||||
_, err := r.DeactivateOrg(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 TestCommandSide_ReactivateOrg(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
iamDomain string
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
orgID string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.Org
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "org not found, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org already active, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"org"),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "push failed, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"org"),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDeactivatedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPushFailed(
|
||||
caos_errs.ThrowInternal(nil, "id", "message"),
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(org.NewOrgReactivatedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
)),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsInternal,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reactivate org",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"org"),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDeactivatedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(org.NewOrgReactivatedEvent(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,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
}
|
||||
_, err := r.ReactivateOrg(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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -27,7 +27,7 @@ func (c *Commands) AddProject(ctx context.Context, project *domain.Project, reso
|
||||
|
||||
func (c *Commands) addProject(ctx context.Context, projectAdd *domain.Project, resourceOwner, ownerUserID string) (_ []eventstore.EventPusher, _ *ProjectWriteModel, err error) {
|
||||
if !projectAdd.IsValid() {
|
||||
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-IOVCC", "Errors.Project.Invalid")
|
||||
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-IOVCC", "Errors.Project.Invalid")
|
||||
}
|
||||
projectAdd.AggregateID, err = c.idGenerator.Next()
|
||||
if err != nil {
|
||||
@ -74,8 +74,8 @@ func (c *Commands) checkProjectExists(ctx context.Context, projectID, resourceOw
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeProject(ctx context.Context, projectChange *domain.Project, resourceOwner string) (*domain.Project, error) {
|
||||
if !projectChange.IsValid() && projectChange.AggregateID != "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4m9vS", "Errors.Project.Invalid")
|
||||
if !projectChange.IsValid() || projectChange.AggregateID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4m9vS", "Errors.Project.Invalid")
|
||||
}
|
||||
|
||||
existingProject, err := c.getProjectWriteModelByID(ctx, projectChange.AggregateID, resourceOwner)
|
||||
@ -107,7 +107,7 @@ func (c *Commands) ChangeProject(ctx context.Context, projectChange *domain.Proj
|
||||
|
||||
func (c *Commands) DeactivateProject(ctx context.Context, projectID string, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||
if projectID == "" || resourceOwner == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-88iF0", "Errors.Project.ProjectIDMissing")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-88iF0", "Errors.Project.ProjectIDMissing")
|
||||
}
|
||||
|
||||
existingProject, err := c.getProjectWriteModelByID(ctx, projectID, resourceOwner)
|
||||
@ -135,7 +135,7 @@ func (c *Commands) DeactivateProject(ctx context.Context, projectID string, reso
|
||||
|
||||
func (c *Commands) ReactivateProject(ctx context.Context, projectID string, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||
if projectID == "" || resourceOwner == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4m9vS", "Errors.Project.ProjectIDMissing")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4m9vS", "Errors.Project.ProjectIDMissing")
|
||||
}
|
||||
|
||||
existingProject, err := c.getProjectWriteModelByID(ctx, projectID, resourceOwner)
|
||||
@ -146,7 +146,7 @@ func (c *Commands) ReactivateProject(ctx context.Context, projectID string, reso
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-3M9sd", "Errors.Project.NotFound")
|
||||
}
|
||||
if existingProject.State != domain.ProjectStateInactive {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-5M9bs", "Errors.Project.NotInctive")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-5M9bs", "Errors.Project.NotInactive")
|
||||
}
|
||||
|
||||
projectAgg := ProjectAggregateFromWriteModel(&existingProject.WriteModel)
|
||||
@ -163,7 +163,7 @@ func (c *Commands) ReactivateProject(ctx context.Context, projectID string, reso
|
||||
|
||||
func (c *Commands) RemoveProject(ctx context.Context, projectID, resourceOwner string, cascadingUserGrantIDs ...string) (*domain.ObjectDetails, error) {
|
||||
if projectID == "" || resourceOwner == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-66hM9", "Errors.Project.ProjectIDMissing")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-66hM9", "Errors.Project.ProjectIDMissing")
|
||||
}
|
||||
|
||||
existingProject, err := c.getProjectWriteModelByID(ctx, projectID, resourceOwner)
|
||||
|
@ -8,8 +8,8 @@ import (
|
||||
)
|
||||
|
||||
func (c *Commands) ChangeApplication(ctx context.Context, projectID string, appChange domain.Application, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||
if appChange.GetAppID() == "" || appChange.GetApplicationName() == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4m9vS", "Errors.Project.App.Invalid")
|
||||
if projectID == "" || appChange.GetAppID() == "" || appChange.GetApplicationName() == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4m9vS", "Errors.Project.App.Invalid")
|
||||
}
|
||||
|
||||
existingApp, err := c.getApplicationWriteModel(ctx, projectID, appChange.GetAppID(), resourceOwner)
|
||||
@ -25,7 +25,7 @@ func (c *Commands) ChangeApplication(ctx context.Context, projectID string, appC
|
||||
projectAgg := ProjectAggregateFromWriteModel(&existingApp.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(
|
||||
ctx,
|
||||
project.NewApplicationChangedEvent(ctx, projectAgg, appChange.GetAppID(), existingApp.Name, appChange.GetApplicationName(), projectID))
|
||||
project.NewApplicationChangedEvent(ctx, projectAgg, appChange.GetAppID(), existingApp.Name, appChange.GetApplicationName()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -38,7 +38,7 @@ func (c *Commands) ChangeApplication(ctx context.Context, projectID string, appC
|
||||
|
||||
func (c *Commands) DeactivateApplication(ctx context.Context, projectID, appID, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||
if projectID == "" || appID == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-88fi0", "Errors.IDMissing")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-88fi0", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
existingApp, err := c.getApplicationWriteModel(ctx, projectID, appID, resourceOwner)
|
||||
@ -65,7 +65,7 @@ func (c *Commands) DeactivateApplication(ctx context.Context, projectID, appID,
|
||||
|
||||
func (c *Commands) ReactivateApplication(ctx context.Context, projectID, appID, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||
if projectID == "" || appID == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-983dF", "Errors.IDMissing")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-983dF", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
existingApp, err := c.getApplicationWriteModel(ctx, projectID, appID, resourceOwner)
|
||||
@ -93,7 +93,7 @@ func (c *Commands) ReactivateApplication(ctx context.Context, projectID, appID,
|
||||
|
||||
func (c *Commands) RemoveApplication(ctx context.Context, projectID, appID, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||
if projectID == "" || appID == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-1b7Jf", "Errors.IDMissing")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-1b7Jf", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
existingApp, err := c.getApplicationWriteModel(ctx, projectID, appID, resourceOwner)
|
||||
@ -105,7 +105,7 @@ func (c *Commands) RemoveApplication(ctx context.Context, projectID, appID, reso
|
||||
}
|
||||
projectAgg := ProjectAggregateFromWriteModel(&existingApp.WriteModel)
|
||||
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, project.NewApplicationRemovedEvent(ctx, projectAgg, appID, existingApp.Name, projectID))
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, project.NewApplicationRemovedEvent(ctx, projectAgg, appID, existingApp.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -9,9 +9,12 @@ import (
|
||||
)
|
||||
|
||||
func (c *Commands) AddAPIApplication(ctx context.Context, application *domain.APIApp, resourceOwner string) (_ *domain.APIApp, err error) {
|
||||
if application == nil || application.AggregateID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-5m9E", "Errors.Application.Invalid")
|
||||
}
|
||||
project, err := c.getProjectByID(ctx, application.AggregateID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "PROJECT-9fnsf", "Errors.Project.NotFound")
|
||||
}
|
||||
addedApplication := NewAPIApplicationWriteModel(application.AggregateID, resourceOwner)
|
||||
projectAgg := ProjectAggregateFromWriteModel(&addedApplication.WriteModel)
|
||||
@ -35,7 +38,7 @@ func (c *Commands) AddAPIApplication(ctx context.Context, application *domain.AP
|
||||
|
||||
func (c *Commands) addAPIApplication(ctx context.Context, projectAgg *eventstore.Aggregate, proj *domain.Project, apiAppApp *domain.APIApp, resourceOwner string) (events []eventstore.EventPusher, stringPW string, err error) {
|
||||
if !apiAppApp.IsValid() {
|
||||
return nil, "", caos_errs.ThrowPreconditionFailed(nil, "PROJECT-Bff2g", "Errors.Application.Invalid")
|
||||
return nil, "", caos_errs.ThrowInvalidArgument(nil, "PROJECT-Bff2g", "Errors.Application.Invalid")
|
||||
}
|
||||
apiAppApp.AppID, err = c.idGenerator.Next()
|
||||
if err != nil {
|
||||
@ -43,7 +46,7 @@ func (c *Commands) addAPIApplication(ctx context.Context, projectAgg *eventstore
|
||||
}
|
||||
|
||||
events = []eventstore.EventPusher{
|
||||
project.NewApplicationAddedEvent(ctx, projectAgg, apiAppApp.AppID, apiAppApp.AppName, resourceOwner),
|
||||
project.NewApplicationAddedEvent(ctx, projectAgg, apiAppApp.AppID, apiAppApp.AppName),
|
||||
}
|
||||
|
||||
var stringPw string
|
||||
@ -66,8 +69,8 @@ func (c *Commands) addAPIApplication(ctx context.Context, projectAgg *eventstore
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeAPIApplication(ctx context.Context, apiApp *domain.APIApp, resourceOwner string) (*domain.APIApp, error) {
|
||||
if !apiApp.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-1m900", "Errors.Project.App.APIConfigInvalid")
|
||||
if !apiApp.IsValid() || apiApp.AppID == "" || apiApp.AggregateID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-1m900", "Errors.Project.App.APIConfigInvalid")
|
||||
}
|
||||
|
||||
existingAPI, err := c.getAPIAppWriteModel(ctx, apiApp.AggregateID, apiApp.AppID, resourceOwner)
|
||||
@ -99,13 +102,12 @@ func (c *Commands) ChangeAPIApplication(ctx context.Context, apiApp *domain.APIA
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := apiWriteModelToAPIConfig(existingAPI)
|
||||
return result, nil
|
||||
return apiWriteModelToAPIConfig(existingAPI), nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeAPIApplicationSecret(ctx context.Context, projectID, appID, resourceOwner string) (*domain.APIApp, error) {
|
||||
if projectID == "" || appID == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-99i83", "Errors.IDMissing")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-99i83", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
existingAPI, err := c.getAPIAppWriteModel(ctx, projectID, appID, resourceOwner)
|
||||
|
660
internal/command/project_application_api_test.go
Normal file
660
internal/command/project_application_api_test.go
Normal file
@ -0,0 +1,660 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/project"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddAPIApplication(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
secretGenerator crypto.Generator
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
apiApp *domain.APIApp
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.APIApp
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "no aggregate id, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
apiApp: &domain.APIApp{},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
apiApp: &domain.APIApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid app, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"project"),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
apiApp: &domain.APIApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
AppID: "app1",
|
||||
AppName: "",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create api app basic, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"project"),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
project.NewAPIConfigAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
domain.APIAuthMethodTypeBasic),
|
||||
),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(project.NewAddApplicationUniqueConstraint("app", "project1")),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "app1", "client1"),
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
apiApp: &domain.APIApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
AppName: "app",
|
||||
AuthMethodType: domain.APIAuthMethodTypeBasic,
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.APIApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
ClientID: "client1@project",
|
||||
ClientSecretString: "a",
|
||||
AuthMethodType: domain.APIAuthMethodTypeBasic,
|
||||
State: domain.AppStateActive,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create api app jwt, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"project"),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
project.NewAPIConfigAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"client1@project",
|
||||
nil,
|
||||
domain.APIAuthMethodTypePrivateKeyJWT),
|
||||
),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(project.NewAddApplicationUniqueConstraint("app", "project1")),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "app1", "client1"),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
apiApp: &domain.APIApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
AppName: "app",
|
||||
AuthMethodType: domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.APIApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
ClientID: "client1@project",
|
||||
AuthMethodType: domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
State: domain.AppStateActive,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
applicationSecretGenerator: tt.fields.secretGenerator,
|
||||
}
|
||||
got, err := r.AddAPIApplication(tt.args.ctx, tt.args.apiApp, 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_ChangeAPIApplication(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
apiApp *domain.APIApp
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.APIApp
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid app, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
apiApp: &domain.APIApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
AppID: "app1",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing appid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
apiApp: &domain.APIApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
AppID: "",
|
||||
AppName: "app",
|
||||
AuthMethodType: domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing aggregateid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
apiApp: &domain.APIApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "",
|
||||
},
|
||||
AppID: "appid",
|
||||
AppName: "app",
|
||||
AuthMethodType: domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "app not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
apiApp: &domain.APIApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
project.NewAPIConfigAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"client1@project",
|
||||
nil,
|
||||
domain.APIAuthMethodTypePrivateKeyJWT),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
apiApp: &domain.APIApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
AuthMethodType: domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "change api app, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
project.NewAPIConfigAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
domain.APIAuthMethodTypeBasic),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
newAPIAppChangedEvent(context.Background(),
|
||||
"app1",
|
||||
"project1",
|
||||
"org1",
|
||||
domain.APIAuthMethodTypePrivateKeyJWT),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
apiApp: &domain.APIApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
AuthMethodType: domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.APIApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
ClientID: "client1@project",
|
||||
AuthMethodType: domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
State: domain.AppStateActive,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.ChangeAPIApplication(tt.args.ctx, tt.args.apiApp, 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_ChangeAPIApplicationSecret(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
secretGenerator crypto.Generator
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
appID string
|
||||
projectID string
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.APIApp
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "no projectid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no appid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "app not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "change secret, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
project.NewAPIConfigAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
domain.APIAuthMethodTypeBasic),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
project.NewAPIConfigSecretChangedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
}),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.APIApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
ClientID: "client1@project",
|
||||
ClientSecretString: "a",
|
||||
AuthMethodType: domain.APIAuthMethodTypeBasic,
|
||||
State: domain.AppStateActive,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
applicationSecretGenerator: tt.fields.secretGenerator,
|
||||
}
|
||||
got, err := r.ChangeAPIApplicationSecret(tt.args.ctx, tt.args.projectID, tt.args.appID, 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 newAPIAppChangedEvent(ctx context.Context, appID, projectID, resourceOwner string, authMethodType domain.APIAuthMethodType) *project.APIConfigChangedEvent {
|
||||
changes := []project.APIConfigChanges{
|
||||
project.ChangeAPIAuthMethodType(authMethodType),
|
||||
}
|
||||
event, _ := project.NewAPIConfigChangedEvent(ctx,
|
||||
&project.NewAggregate(projectID, resourceOwner).Aggregate,
|
||||
appID,
|
||||
changes,
|
||||
)
|
||||
return event
|
||||
}
|
@ -9,6 +9,9 @@ import (
|
||||
)
|
||||
|
||||
func (c *Commands) AddApplicationKey(ctx context.Context, key *domain.ApplicationKey, resourceOwner string) (_ *domain.ApplicationKey, err error) {
|
||||
if key.AggregateID == "" || key.ApplicationID == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "COMMAND-55m9fs", "Errors.IDMissing")
|
||||
}
|
||||
application, err := c.getApplicationWriteModel(ctx, key.AggregateID, key.ApplicationID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
215
internal/command/project_application_key_test.go
Normal file
215
internal/command/project_application_key_test.go
Normal file
@ -0,0 +1,215 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/v1/models"
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
id_mock "github.com/caos/zitadel/internal/id/mock"
|
||||
"github.com/caos/zitadel/internal/repository/project"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddAPIApplicationKey(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
secretGenerator crypto.Generator
|
||||
keySize int
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
key *domain.ApplicationKey
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.APIApp
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "no aggregateid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
key: &domain.ApplicationKey{
|
||||
ApplicationID: "app1",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no appid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
key: &domain.ApplicationKey{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "app not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
key: &domain.ApplicationKey{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
ApplicationID: "app1",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create key not allowed, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewAPIConfigAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
domain.APIAuthMethodTypeBasic),
|
||||
),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "key1"),
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
key: &domain.ApplicationKey{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
ApplicationID: "app1",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create key not allowed, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewAPIConfigAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
domain.APIAuthMethodTypeBasic),
|
||||
),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "key1"),
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
keySize: 10,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
key: &domain.ApplicationKey{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
ApplicationID: "app1",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
applicationSecretGenerator: tt.fields.secretGenerator,
|
||||
applicationKeySize: tt.fields.keySize,
|
||||
}
|
||||
got, err := r.AddApplicationKey(tt.args.ctx, tt.args.key, 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -14,9 +14,12 @@ import (
|
||||
)
|
||||
|
||||
func (c *Commands) AddOIDCApplication(ctx context.Context, application *domain.OIDCApp, resourceOwner string) (_ *domain.OIDCApp, err error) {
|
||||
if application == nil || application.AggregateID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-34Fm0", "Errors.Application.Invalid")
|
||||
}
|
||||
project, err := c.getProjectByID(ctx, application.AggregateID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "PROJECT-3m9ss", "Errors.Project.NotFound")
|
||||
}
|
||||
addedApplication := NewOIDCApplicationWriteModel(application.AggregateID, resourceOwner)
|
||||
projectAgg := ProjectAggregateFromWriteModel(&addedApplication.WriteModel)
|
||||
@ -41,7 +44,7 @@ func (c *Commands) AddOIDCApplication(ctx context.Context, application *domain.O
|
||||
|
||||
func (c *Commands) addOIDCApplication(ctx context.Context, projectAgg *eventstore.Aggregate, proj *domain.Project, oidcApp *domain.OIDCApp, resourceOwner string) (events []eventstore.EventPusher, stringPW string, err error) {
|
||||
if !oidcApp.IsValid() {
|
||||
return nil, "", caos_errs.ThrowPreconditionFailed(nil, "PROJECT-Bff2g", "Errors.Application.Invalid")
|
||||
return nil, "", caos_errs.ThrowInvalidArgument(nil, "PROJECT-Bff2g", "Errors.Application.Invalid")
|
||||
}
|
||||
oidcApp.AppID, err = c.idGenerator.Next()
|
||||
if err != nil {
|
||||
@ -49,7 +52,7 @@ func (c *Commands) addOIDCApplication(ctx context.Context, projectAgg *eventstor
|
||||
}
|
||||
|
||||
events = []eventstore.EventPusher{
|
||||
project.NewApplicationAddedEvent(ctx, projectAgg, oidcApp.AppID, oidcApp.AppName, resourceOwner),
|
||||
project.NewApplicationAddedEvent(ctx, projectAgg, oidcApp.AppID, oidcApp.AppName),
|
||||
}
|
||||
|
||||
var stringPw string
|
||||
@ -84,8 +87,8 @@ func (c *Commands) addOIDCApplication(ctx context.Context, projectAgg *eventstor
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeOIDCApplication(ctx context.Context, oidc *domain.OIDCApp, resourceOwner string) (*domain.OIDCApp, error) {
|
||||
if !oidc.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-1m900", "Errors.Project.App.OIDCConfigInvalid")
|
||||
if !oidc.IsValid() || oidc.AppID == "" || oidc.AggregateID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-5m9fs", "Errors.Project.App.OIDCConfigInvalid")
|
||||
}
|
||||
|
||||
existingOIDC, err := c.getOIDCAppWriteModel(ctx, oidc.AggregateID, oidc.AppID, resourceOwner)
|
||||
@ -136,7 +139,7 @@ func (c *Commands) ChangeOIDCApplication(ctx context.Context, oidc *domain.OIDCA
|
||||
|
||||
func (c *Commands) ChangeOIDCApplicationSecret(ctx context.Context, projectID, appID, resourceOwner string) (*domain.OIDCApp, error) {
|
||||
if projectID == "" || appID == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-99i83", "Errors.IDMissing")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-99i83", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
existingOIDC, err := c.getOIDCAppWriteModel(ctx, projectID, appID, resourceOwner)
|
||||
|
734
internal/command/project_application_oidc_test.go
Normal file
734
internal/command/project_application_oidc_test.go
Normal file
@ -0,0 +1,734 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/project"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddOIDCApplication(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
secretGenerator crypto.Generator
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
oidcApp *domain.OIDCApp
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.OIDCApp
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "no aggregate id, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
oidcApp: &domain.OIDCApp{},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
oidcApp: &domain.OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid app, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"project"),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
oidcApp: &domain.OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
AppID: "app1",
|
||||
AppName: "",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create oidc app basic, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"project"),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
project.NewOIDCConfigAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
domain.OIDCVersionV1,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
[]string{"https://test.ch"},
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
domain.OIDCApplicationTypeWeb,
|
||||
domain.OIDCAuthMethodTypePost,
|
||||
[]string{"https://test.ch/logout"},
|
||||
true,
|
||||
domain.OIDCTokenTypeBearer,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
time.Second*1),
|
||||
),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(project.NewAddApplicationUniqueConstraint("app", "project1")),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "app1", "client1"),
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
oidcApp: &domain.OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
AppName: "app",
|
||||
AuthMethodType: domain.OIDCAuthMethodTypePost,
|
||||
OIDCVersion: domain.OIDCVersionV1,
|
||||
RedirectUris: []string{"https://test.ch"},
|
||||
ResponseTypes: []domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
GrantTypes: []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
ApplicationType: domain.OIDCApplicationTypeWeb,
|
||||
PostLogoutRedirectUris: []string{"https://test.ch/logout"},
|
||||
DevMode: true,
|
||||
AccessTokenType: domain.OIDCTokenTypeBearer,
|
||||
AccessTokenRoleAssertion: true,
|
||||
IDTokenRoleAssertion: true,
|
||||
IDTokenUserinfoAssertion: true,
|
||||
ClockSkew: time.Second * 1,
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
ClientID: "client1@project",
|
||||
ClientSecretString: "a",
|
||||
AuthMethodType: domain.OIDCAuthMethodTypePost,
|
||||
OIDCVersion: domain.OIDCVersionV1,
|
||||
RedirectUris: []string{"https://test.ch"},
|
||||
ResponseTypes: []domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
GrantTypes: []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
ApplicationType: domain.OIDCApplicationTypeWeb,
|
||||
PostLogoutRedirectUris: []string{"https://test.ch/logout"},
|
||||
DevMode: true,
|
||||
AccessTokenType: domain.OIDCTokenTypeBearer,
|
||||
AccessTokenRoleAssertion: true,
|
||||
IDTokenRoleAssertion: true,
|
||||
IDTokenUserinfoAssertion: true,
|
||||
ClockSkew: time.Second * 1,
|
||||
State: domain.AppStateActive,
|
||||
Compliance: &domain.Compliance{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
applicationSecretGenerator: tt.fields.secretGenerator,
|
||||
}
|
||||
got, err := r.AddOIDCApplication(tt.args.ctx, tt.args.oidcApp, 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_ChangeOIDCApplication(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
oidcApp *domain.OIDCApp
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.OIDCApp
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid app, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
oidcApp: &domain.OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
AppID: "app1",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing appid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
oidcApp: &domain.OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
AppID: "",
|
||||
AppName: "app",
|
||||
AuthMethodType: domain.OIDCAuthMethodTypePost,
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing aggregateid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
oidcApp: &domain.OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "",
|
||||
},
|
||||
AppID: "appid",
|
||||
AppName: "app",
|
||||
AuthMethodType: domain.OIDCAuthMethodTypePost,
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "app not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
oidcApp: &domain.OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
project.NewOIDCConfigAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
domain.OIDCVersionV1,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
[]string{"https://test.ch"},
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
domain.OIDCApplicationTypeWeb,
|
||||
domain.OIDCAuthMethodTypePost,
|
||||
[]string{"https://test.ch/logout"},
|
||||
true,
|
||||
domain.OIDCTokenTypeBearer,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
time.Second*1),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
oidcApp: &domain.OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
AuthMethodType: domain.OIDCAuthMethodTypePost,
|
||||
OIDCVersion: domain.OIDCVersionV1,
|
||||
RedirectUris: []string{"https://test.ch"},
|
||||
ResponseTypes: []domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
GrantTypes: []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
ApplicationType: domain.OIDCApplicationTypeWeb,
|
||||
PostLogoutRedirectUris: []string{"https://test.ch/logout"},
|
||||
DevMode: true,
|
||||
AccessTokenType: domain.OIDCTokenTypeBearer,
|
||||
AccessTokenRoleAssertion: true,
|
||||
IDTokenRoleAssertion: true,
|
||||
IDTokenUserinfoAssertion: true,
|
||||
ClockSkew: time.Second * 1,
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "change oidc app, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
project.NewOIDCConfigAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
domain.OIDCVersionV1,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
[]string{"https://test.ch"},
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
domain.OIDCApplicationTypeWeb,
|
||||
domain.OIDCAuthMethodTypePost,
|
||||
[]string{"https://test.ch/logout"},
|
||||
false,
|
||||
domain.OIDCTokenTypeBearer,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
time.Second*1),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
newOIDCAppChangedEvent(context.Background(),
|
||||
"app1",
|
||||
"project1",
|
||||
"org1"),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
oidcApp: &domain.OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
AuthMethodType: domain.OIDCAuthMethodTypePost,
|
||||
OIDCVersion: domain.OIDCVersionV1,
|
||||
RedirectUris: []string{"https://test-change.ch"},
|
||||
ResponseTypes: []domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
GrantTypes: []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
ApplicationType: domain.OIDCApplicationTypeWeb,
|
||||
PostLogoutRedirectUris: []string{"https://test-change.ch/logout"},
|
||||
DevMode: true,
|
||||
AccessTokenType: domain.OIDCTokenTypeJWT,
|
||||
AccessTokenRoleAssertion: false,
|
||||
IDTokenRoleAssertion: false,
|
||||
IDTokenUserinfoAssertion: false,
|
||||
ClockSkew: time.Second * 2,
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
AppID: "app1",
|
||||
ClientID: "client1@project",
|
||||
AppName: "app",
|
||||
AuthMethodType: domain.OIDCAuthMethodTypePost,
|
||||
OIDCVersion: domain.OIDCVersionV1,
|
||||
RedirectUris: []string{"https://test-change.ch"},
|
||||
ResponseTypes: []domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
GrantTypes: []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
ApplicationType: domain.OIDCApplicationTypeWeb,
|
||||
PostLogoutRedirectUris: []string{"https://test-change.ch/logout"},
|
||||
DevMode: true,
|
||||
AccessTokenType: domain.OIDCTokenTypeJWT,
|
||||
AccessTokenRoleAssertion: false,
|
||||
IDTokenRoleAssertion: false,
|
||||
IDTokenUserinfoAssertion: false,
|
||||
ClockSkew: time.Second * 2,
|
||||
Compliance: &domain.Compliance{},
|
||||
State: domain.AppStateActive,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.ChangeOIDCApplication(tt.args.ctx, tt.args.oidcApp, 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_ChangeOIDCApplicationSecret(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
secretGenerator crypto.Generator
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
appID string
|
||||
projectID string
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.OIDCApp
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "no projectid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no appid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "app not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "change secret, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
project.NewOIDCConfigAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
domain.OIDCVersionV1,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
[]string{"https://test.ch"},
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
domain.OIDCApplicationTypeWeb,
|
||||
domain.OIDCAuthMethodTypePost,
|
||||
[]string{"https://test.ch/logout"},
|
||||
true,
|
||||
domain.OIDCTokenTypeBearer,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
time.Second*1),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
project.NewOIDCConfigSecretChangedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
}),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
ClientID: "client1@project",
|
||||
ClientSecretString: "a",
|
||||
AuthMethodType: domain.OIDCAuthMethodTypePost,
|
||||
OIDCVersion: domain.OIDCVersionV1,
|
||||
RedirectUris: []string{"https://test.ch"},
|
||||
ResponseTypes: []domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
GrantTypes: []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
ApplicationType: domain.OIDCApplicationTypeWeb,
|
||||
PostLogoutRedirectUris: []string{"https://test.ch/logout"},
|
||||
DevMode: true,
|
||||
AccessTokenType: domain.OIDCTokenTypeBearer,
|
||||
AccessTokenRoleAssertion: true,
|
||||
IDTokenRoleAssertion: true,
|
||||
IDTokenUserinfoAssertion: true,
|
||||
ClockSkew: time.Second * 1,
|
||||
State: domain.AppStateActive,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
applicationSecretGenerator: tt.fields.secretGenerator,
|
||||
}
|
||||
got, err := r.ChangeOIDCApplicationSecret(tt.args.ctx, tt.args.projectID, tt.args.appID, 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 newOIDCAppChangedEvent(ctx context.Context, appID, projectID, resourceOwner string) *project.OIDCConfigChangedEvent {
|
||||
changes := []project.OIDCConfigChanges{
|
||||
project.ChangeRedirectURIs([]string{"https://test-change.ch"}),
|
||||
project.ChangePostLogoutRedirectURIs([]string{"https://test-change.ch/logout"}),
|
||||
project.ChangeDevMode(true),
|
||||
project.ChangeAccessTokenType(domain.OIDCTokenTypeJWT),
|
||||
project.ChangeAccessTokenRoleAssertion(false),
|
||||
project.ChangeIDTokenRoleAssertion(false),
|
||||
project.ChangeIDTokenUserinfoAssertion(false),
|
||||
project.ChangeClockSkew(time.Second * 2),
|
||||
}
|
||||
event, _ := project.NewOIDCConfigChangedEvent(ctx,
|
||||
&project.NewAggregate(projectID, resourceOwner).Aggregate,
|
||||
appID,
|
||||
changes,
|
||||
)
|
||||
return event
|
||||
}
|
636
internal/command/project_application_test.go
Normal file
636
internal/command/project_application_test.go
Normal file
@ -0,0 +1,636 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/project"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandSide_ChangeApplication(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
projectID string
|
||||
app *domain.ChangeApp
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid app missing projectid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "",
|
||||
app: &domain.ChangeApp{
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid app missing appid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
app: &domain.ChangeApp{
|
||||
AppName: "app",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid app missing name, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
app: &domain.ChangeApp{
|
||||
AppID: "app1",
|
||||
AppName: "",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "app not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
app: &domain.ChangeApp{
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "app name not changed, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
)),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
app: &domain.ChangeApp{
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "app changed, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
)),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(project.NewApplicationChangedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
"app changed",
|
||||
)),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(project.NewRemoveApplicationUniqueConstraint("app", "project1")),
|
||||
uniqueConstraintsFromEventConstraint(project.NewAddApplicationUniqueConstraint("app changed", "project1")),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
app: &domain.ChangeApp{
|
||||
AppID: "app1",
|
||||
AppName: "app changed",
|
||||
},
|
||||
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.ChangeApplication(tt.args.ctx, tt.args.projectID, tt.args.app, 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_DeactivateApplication(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
projectID string
|
||||
appID string
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "missing projectid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "",
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing appid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "app not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "app already inactive, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
)),
|
||||
eventFromEventPusher(project.NewApplicationDeactivatedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
)),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "app deactivate, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
)),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(project.NewApplicationDeactivatedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
)),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "app1",
|
||||
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.DeactivateApplication(tt.args.ctx, tt.args.projectID, tt.args.appID, 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_ReactivateApplication(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
projectID string
|
||||
appID string
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "missing projectid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "",
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing appid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "app not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "app already active, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
)),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "app reactivate, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
)),
|
||||
eventFromEventPusher(project.NewApplicationDeactivatedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
)),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(project.NewApplicationReactivatedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
)),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "app1",
|
||||
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.ReactivateApplication(tt.args.ctx, tt.args.projectID, tt.args.appID, 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_RemoveApplication(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
projectID string
|
||||
appID string
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "missing projectid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "",
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing appid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "app not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "app remove, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
)),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(project.NewApplicationRemovedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"app",
|
||||
)),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(project.NewRemoveApplicationUniqueConstraint("app", "project1")),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "app1",
|
||||
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.RemoveApplication(tt.args.ctx, tt.args.projectID, tt.args.appID, 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -38,7 +38,6 @@ func oidcWriteModelToOIDCConfig(writeModel *OIDCApplicationWriteModel) *domain.O
|
||||
AppName: writeModel.AppName,
|
||||
State: writeModel.State,
|
||||
ClientID: writeModel.ClientID,
|
||||
ClientSecret: writeModel.ClientSecret,
|
||||
RedirectUris: writeModel.RedirectUris,
|
||||
ResponseTypes: writeModel.ResponseTypes,
|
||||
GrantTypes: writeModel.GrantTypes,
|
||||
@ -62,7 +61,6 @@ func apiWriteModelToAPIConfig(writeModel *APIApplicationWriteModel) *domain.APIA
|
||||
AppName: writeModel.AppName,
|
||||
State: writeModel.State,
|
||||
ClientID: writeModel.ClientID,
|
||||
ClientSecret: writeModel.ClientSecret,
|
||||
AuthMethodType: writeModel.AuthMethodType,
|
||||
}
|
||||
}
|
||||
|
@ -13,25 +13,21 @@ import (
|
||||
|
||||
func (c *Commands) AddProjectGrant(ctx context.Context, grant *domain.ProjectGrant, resourceOwner string) (_ *domain.ProjectGrant, err error) {
|
||||
if !grant.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-Bff2g", "Errors.Project.Grant.Invalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-Bff2g", "Errors.Project.Grant.Invalid")
|
||||
}
|
||||
err = c.checkProjectGrantPreCondition(ctx, grant)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grant.GrantID, err = c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = c.checkProjectExists(ctx, grant.AggregateID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = c.checkOrgExists(ctx, grant.GrantedOrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addedGrant := NewProjectGrantWriteModel(grant.GrantID, grant.AggregateID, resourceOwner)
|
||||
projectAgg := ProjectAggregateFromWriteModel(&addedGrant.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(
|
||||
ctx,
|
||||
project.NewGrantAddedEvent(ctx, projectAgg, grant.GrantID, grant.GrantedOrgID, grant.AggregateID, grant.RoleKeys))
|
||||
project.NewGrantAddedEvent(ctx, projectAgg, grant.GrantID, grant.GrantedOrgID, grant.RoleKeys))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -44,13 +40,14 @@ func (c *Commands) AddProjectGrant(ctx context.Context, grant *domain.ProjectGra
|
||||
|
||||
func (c *Commands) ChangeProjectGrant(ctx context.Context, grant *domain.ProjectGrant, resourceOwner string, cascadeUserGrantIDs ...string) (_ *domain.ProjectGrant, err error) {
|
||||
if grant.GrantID == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-1j83s", "Errors.IDMissing")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-1j83s", "Errors.IDMissing")
|
||||
}
|
||||
err = c.checkProjectExists(ctx, grant.AggregateID, resourceOwner)
|
||||
existingGrant, err := c.projectGrantWriteModelByID(ctx, grant.GrantID, grant.AggregateID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
existingGrant, err := c.projectGrantWriteModelByID(ctx, grant.GrantID, grant.AggregateID, resourceOwner)
|
||||
grant.GrantedOrgID = existingGrant.GrantedOrgID
|
||||
err = c.checkProjectGrantPreCondition(ctx, grant)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -96,7 +93,7 @@ func (c *Commands) ChangeProjectGrant(ctx context.Context, grant *domain.Project
|
||||
}
|
||||
|
||||
func (c *Commands) removeRoleFromProjectGrant(ctx context.Context, projectAgg *eventstore.Aggregate, projectID, projectGrantID, roleKey string, cascade bool) (_ eventstore.EventPusher, _ *ProjectGrantWriteModel, err error) {
|
||||
existingProjectGrant, err := c.projectGrantWriteModelByID(ctx, projectID, projectGrantID, "")
|
||||
existingProjectGrant, err := c.projectGrantWriteModelByID(ctx, projectGrantID, projectID, "")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -127,7 +124,7 @@ func (c *Commands) removeRoleFromProjectGrant(ctx context.Context, projectAgg *e
|
||||
|
||||
func (c *Commands) DeactivateProjectGrant(ctx context.Context, projectID, grantID, resourceOwner string) (details *domain.ObjectDetails, err error) {
|
||||
if grantID == "" || projectID == "" {
|
||||
return details, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-p0s4V", "Errors.IDMissing")
|
||||
return details, caos_errs.ThrowInvalidArgument(nil, "PROJECT-p0s4V", "Errors.IDMissing")
|
||||
}
|
||||
err = c.checkProjectExists(ctx, projectID, resourceOwner)
|
||||
if err != nil {
|
||||
@ -155,7 +152,7 @@ func (c *Commands) DeactivateProjectGrant(ctx context.Context, projectID, grantI
|
||||
|
||||
func (c *Commands) ReactivateProjectGrant(ctx context.Context, projectID, grantID, resourceOwner string) (details *domain.ObjectDetails, err error) {
|
||||
if grantID == "" || projectID == "" {
|
||||
return details, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-p0s4V", "Errors.IDMissing")
|
||||
return details, caos_errs.ThrowInvalidArgument(nil, "PROJECT-p0s4V", "Errors.IDMissing")
|
||||
}
|
||||
err = c.checkProjectExists(ctx, projectID, resourceOwner)
|
||||
if err != nil {
|
||||
@ -182,11 +179,11 @@ func (c *Commands) ReactivateProjectGrant(ctx context.Context, projectID, grantI
|
||||
|
||||
func (c *Commands) RemoveProjectGrant(ctx context.Context, projectID, grantID, resourceOwner string, cascadeUserGrantIDs ...string) (details *domain.ObjectDetails, err error) {
|
||||
if grantID == "" || projectID == "" {
|
||||
return details, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-1m9fJ", "Errors.IDMissing")
|
||||
return details, caos_errs.ThrowInvalidArgument(nil, "PROJECT-1m9fJ", "Errors.IDMissing")
|
||||
}
|
||||
err = c.checkProjectExists(ctx, projectID, resourceOwner)
|
||||
if err != nil {
|
||||
return details, err
|
||||
return details, caos_errs.ThrowPreconditionFailed(err, "PROJECT-6mf9s", "Errors.Project.NotFound")
|
||||
}
|
||||
existingGrant, err := c.projectGrantWriteModelByID(ctx, grantID, projectID, resourceOwner)
|
||||
if err != nil {
|
||||
@ -194,7 +191,7 @@ func (c *Commands) RemoveProjectGrant(ctx context.Context, projectID, grantID, r
|
||||
}
|
||||
events := make([]eventstore.EventPusher, 0)
|
||||
projectAgg := ProjectAggregateFromWriteModel(&existingGrant.WriteModel)
|
||||
events = append(events, project.NewGrantRemovedEvent(ctx, projectAgg, grantID, existingGrant.GrantedOrgID, projectID))
|
||||
events = append(events, project.NewGrantRemovedEvent(ctx, projectAgg, grantID, existingGrant.GrantedOrgID))
|
||||
|
||||
for _, userGrantID := range cascadeUserGrantIDs {
|
||||
event, _, err := c.removeUserGrant(ctx, userGrantID, "", true)
|
||||
@ -231,3 +228,21 @@ func (c *Commands) projectGrantWriteModelByID(ctx context.Context, grantID, proj
|
||||
|
||||
return writeModel, nil
|
||||
}
|
||||
|
||||
func (c *Commands) checkProjectGrantPreCondition(ctx context.Context, projectGrant *domain.ProjectGrant) error {
|
||||
preConditions := NewProjectGrantPreConditionReadModel(projectGrant.AggregateID, projectGrant.GrantedOrgID)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, preConditions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !preConditions.ProjectExists {
|
||||
return caos_errs.ThrowPreconditionFailed(err, "COMMAND-m9gsd", "Errors.Project.NotFound")
|
||||
}
|
||||
if !preConditions.GrantedOrgExists {
|
||||
return caos_errs.ThrowPreconditionFailed(err, "COMMAND-3m9gg", "Errors.Org.NotFound")
|
||||
}
|
||||
if projectGrant.HasInvalidRoles(preConditions.ExistingRoleKeys) {
|
||||
return caos_errs.ThrowPreconditionFailed(err, "COMMAND-6m9gd", "Errors.Project.Role.NotFound")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -11,9 +11,12 @@ import (
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
func (c *Commands) AddProjectGrantMember(ctx context.Context, member *domain.ProjectGrantMember, resourceOwner string) (*domain.ProjectGrantMember, error) {
|
||||
func (c *Commands) AddProjectGrantMember(ctx context.Context, member *domain.ProjectGrantMember) (*domain.ProjectGrantMember, error) {
|
||||
if !member.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-8fi7G", "Errors.Project.Member.Invalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-8fi7G", "Errors.Project.Grant.Member.Invalid")
|
||||
}
|
||||
if len(domain.CheckForInvalidRoles(member.Roles, domain.ProjectGrantRolePrefix, c.zitadelRoles)) > 0 {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-m9gKK", "Errors.Project.Grant.Member.Invalid")
|
||||
}
|
||||
err := c.checkUserExists(ctx, member.UserID, "")
|
||||
if err != nil {
|
||||
@ -25,12 +28,12 @@ func (c *Commands) AddProjectGrantMember(ctx context.Context, member *domain.Pro
|
||||
return nil, err
|
||||
}
|
||||
if addedMember.State == domain.MemberStateActive {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-16dVN", "Errors.Project.Member.AlreadyExists")
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "PROJECT-16dVN", "Errors.Project.Member.AlreadyExists")
|
||||
}
|
||||
projectAgg := ProjectAggregateFromWriteModel(&addedMember.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(
|
||||
ctx,
|
||||
project.NewProjectGrantMemberAddedEvent(ctx, projectAgg, member.AggregateID, member.UserID, member.GrantID, member.Roles...))
|
||||
project.NewProjectGrantMemberAddedEvent(ctx, projectAgg, member.UserID, member.GrantID, member.Roles...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -43,12 +46,12 @@ func (c *Commands) AddProjectGrantMember(ctx context.Context, member *domain.Pro
|
||||
}
|
||||
|
||||
//ChangeProjectGrantMember updates an existing member
|
||||
func (c *Commands) ChangeProjectGrantMember(ctx context.Context, member *domain.ProjectGrantMember, resourceOwner string) (*domain.ProjectGrantMember, error) {
|
||||
func (c *Commands) ChangeProjectGrantMember(ctx context.Context, member *domain.ProjectGrantMember) (*domain.ProjectGrantMember, error) {
|
||||
if !member.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-109fs", "Errors.Project.Member.Invalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-109fs", "Errors.Project.Member.Invalid")
|
||||
}
|
||||
if len(domain.CheckForInvalidRoles(member.Roles, domain.ProjectGrantRolePrefix, c.zitadelRoles)) > 0 {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-m0sDf", "Errors.Project.Member.Invalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-m0sDf", "Errors.Project.Member.Invalid")
|
||||
}
|
||||
|
||||
existingMember, err := c.projectGrantMemberWriteModelByID(ctx, member.AggregateID, member.UserID, member.GrantID)
|
||||
@ -74,14 +77,17 @@ func (c *Commands) ChangeProjectGrantMember(ctx context.Context, member *domain.
|
||||
return memberWriteModelToProjectGrantMember(existingMember), nil
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveProjectGrantMember(ctx context.Context, projectID, userID, grantID, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||
func (c *Commands) RemoveProjectGrantMember(ctx context.Context, projectID, userID, grantID string) (*domain.ObjectDetails, error) {
|
||||
if projectID == "" || userID == "" || grantID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-66mHd", "Errors.Project.Member.Invalid")
|
||||
}
|
||||
m, err := c.projectGrantMemberWriteModelByID(ctx, projectID, userID, grantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
projectAgg := ProjectAggregateFromWriteModel(&m.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, project.NewProjectGrantMemberRemovedEvent(ctx, projectAgg, projectID, userID, grantID))
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, project.NewProjectGrantMemberRemovedEvent(ctx, projectAgg, userID, grantID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
646
internal/command/project_grant_member_test.go
Normal file
646
internal/command/project_grant_member_test.go
Normal file
@ -0,0 +1,646 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"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/project"
|
||||
"github.com/caos/zitadel/internal/repository/user"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/text/language"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddProjectGrantMember(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
zitadelRoles []authz.RoleMapping
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
member *domain.ProjectGrantMember
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ProjectGrantMember
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid member, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.ProjectGrantMember{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid roles, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.ProjectGrantMember{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
GrantID: "projectgrant1",
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_GRANT_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user not existing, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "PROJECT_GRANT_OWNER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.ProjectGrantMember{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
GrantID: "projectgrant1",
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_GRANT_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member already exists, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectGrantMemberAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"user1",
|
||||
"projectgrant1",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "PROJECT_GRANT_OWNER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.ProjectGrantMember{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
GrantID: "projectgrant1",
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_GRANT_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member add uniqueconstraint err, already exists",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(),
|
||||
expectPushFailed(caos_errs.ThrowAlreadyExists(nil, "ERROR", "internal"),
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(project.NewProjectGrantMemberAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "").Aggregate,
|
||||
"user1",
|
||||
"projectgrant1",
|
||||
[]string{"PROJECT_GRANT_OWNER"}...,
|
||||
)),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(project.NewAddProjectGrantMemberUniqueConstraint("project1", "user1", "projectgrant1")),
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "PROJECT_GRANT_OWNER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.ProjectGrantMember{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
GrantID: "projectgrant1",
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_GRANT_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member add, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(project.NewProjectGrantMemberAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "").Aggregate,
|
||||
"user1",
|
||||
"projectgrant1",
|
||||
[]string{"PROJECT_GRANT_OWNER"}...,
|
||||
)),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(project.NewAddProjectGrantMemberUniqueConstraint("project1", "user1", "projectgrant1")),
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "PROJECT_GRANT_OWNER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.ProjectGrantMember{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
UserID: "user1",
|
||||
GrantID: "projectgrant1",
|
||||
Roles: []string{"PROJECT_GRANT_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ProjectGrantMember{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
GrantID: "projectgrant1",
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_GRANT_OWNER"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
zitadelRoles: tt.fields.zitadelRoles,
|
||||
}
|
||||
got, err := r.AddProjectGrantMember(tt.args.ctx, tt.args.member)
|
||||
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_ChangeProjectGrantMember(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
zitadelRoles []authz.RoleMapping
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
member *domain.ProjectGrantMember
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ProjectGrantMember
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid member, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.ProjectGrantMember{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid roles, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.ProjectGrantMember{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
GrantID: "projectgrant1",
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "PROJECT_GRANT_OWNER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.ProjectGrantMember{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
GrantID: "projectgrant1",
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_GRANT_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member not changed, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectGrantMemberAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"user1",
|
||||
"projectgrant1",
|
||||
[]string{"PROJECT_GRANT_OWNER"}...,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "PROJECT_GRANT_OWNER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.ProjectGrantMember{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
GrantID: "projectgrant1",
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_GRANT_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member change, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectGrantMemberAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"user1",
|
||||
"projectgrant1",
|
||||
[]string{"PROJECT_GRANT_OWNER"}...,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(project.NewProjectGrantMemberChangedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"user1",
|
||||
"projectgrant1",
|
||||
[]string{"PROJECT_GRANT_OWNER", "PROJECT_GRANT_VIEWER"}...,
|
||||
)),
|
||||
},
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "PROJECT_GRANT_OWNER",
|
||||
},
|
||||
{
|
||||
Role: "PROJECT_GRANT_VIEWER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.ProjectGrantMember{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
GrantID: "projectgrant1",
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_GRANT_OWNER", "PROJECT_GRANT_VIEWER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ProjectGrantMember{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
ResourceOwner: "org1",
|
||||
AggregateID: "project1",
|
||||
},
|
||||
GrantID: "projectgrant1",
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_GRANT_OWNER", "PROJECT_GRANT_VIEWER"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
zitadelRoles: tt.fields.zitadelRoles,
|
||||
}
|
||||
got, err := r.ChangeProjectGrantMember(tt.args.ctx, tt.args.member)
|
||||
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_RemoveProjectGrantMember(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
projectID string
|
||||
grantID string
|
||||
userID string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid member projectid missing, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "",
|
||||
userID: "user1",
|
||||
grantID: "projectgrant1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid member userid missing, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
userID: "",
|
||||
grantID: "projectgrant1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid member grantid missing, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
userID: "user1",
|
||||
grantID: "",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member not existing, not found err",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
userID: "user1",
|
||||
grantID: "projectgrant1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member remove, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectGrantMemberAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"user1",
|
||||
"projectgrant1",
|
||||
[]string{"PROJECT_OWNER"}...,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(project.NewProjectGrantMemberRemovedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"user1",
|
||||
"projectgrant1",
|
||||
)),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(project.NewRemoveProjectGrantMemberUniqueConstraint("project1", "user1", "projectgrant1")),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
userID: "user1",
|
||||
grantID: "projectgrant1",
|
||||
},
|
||||
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.RemoveProjectGrantMember(tt.args.ctx, tt.args.projectID, tt.args.userID, tt.args.grantID)
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package command
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
"github.com/caos/zitadel/internal/repository/project"
|
||||
)
|
||||
|
||||
@ -105,3 +106,60 @@ func (wm *ProjectGrantWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
type ProjectGrantPreConditionReadModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
ProjectID string
|
||||
GrantedOrgID string
|
||||
ProjectExists bool
|
||||
GrantedOrgExists bool
|
||||
ExistingRoleKeys []string
|
||||
}
|
||||
|
||||
func NewProjectGrantPreConditionReadModel(projectID, grantedOrgID string) *ProjectGrantPreConditionReadModel {
|
||||
return &ProjectGrantPreConditionReadModel{
|
||||
ProjectID: projectID,
|
||||
GrantedOrgID: grantedOrgID,
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *ProjectGrantPreConditionReadModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *project.ProjectAddedEvent:
|
||||
wm.ProjectExists = true
|
||||
case *project.ProjectRemovedEvent:
|
||||
wm.ProjectExists = false
|
||||
case *project.RoleAddedEvent:
|
||||
wm.ExistingRoleKeys = append(wm.ExistingRoleKeys, e.Key)
|
||||
case *project.RoleRemovedEvent:
|
||||
for i, key := range wm.ExistingRoleKeys {
|
||||
if key == e.Key {
|
||||
copy(wm.ExistingRoleKeys[i:], wm.ExistingRoleKeys[i+1:])
|
||||
wm.ExistingRoleKeys[len(wm.ExistingRoleKeys)-1] = ""
|
||||
wm.ExistingRoleKeys = wm.ExistingRoleKeys[:len(wm.ExistingRoleKeys)-1]
|
||||
continue
|
||||
}
|
||||
}
|
||||
case *org.OrgAddedEvent:
|
||||
wm.GrantedOrgExists = true
|
||||
case *org.OrgRemovedEvent:
|
||||
wm.GrantedOrgExists = false
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *ProjectGrantPreConditionReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, org.AggregateType, project.AggregateType).
|
||||
AggregateIDs(wm.ProjectID, wm.GrantedOrgID).
|
||||
EventTypes(
|
||||
org.OrgAddedEventType,
|
||||
org.OrgRemovedEventType,
|
||||
project.ProjectAddedType,
|
||||
project.ProjectRemovedType,
|
||||
project.RoleAddedType,
|
||||
project.RoleRemovedType)
|
||||
return query
|
||||
}
|
||||
|
1396
internal/command/project_grant_test.go
Normal file
1396
internal/command/project_grant_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -34,10 +34,10 @@ func (c *Commands) AddProjectMember(ctx context.Context, member *domain.Member,
|
||||
|
||||
func (c *Commands) addProjectMember(ctx context.Context, projectAgg *eventstore.Aggregate, addedMember *ProjectMemberWriteModel, member *domain.Member) (eventstore.EventPusher, error) {
|
||||
if !member.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-W8m4l", "Errors.Project.Member.Invalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-W8m4l", "Errors.Project.Member.Invalid")
|
||||
}
|
||||
if len(domain.CheckForInvalidRoles(member.Roles, domain.ProjectRolePrefix, c.zitadelRoles)) > 0 {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-3m9ds", "Errors.Project.Member.Invalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-3m9ds", "Errors.Project.Member.Invalid")
|
||||
}
|
||||
|
||||
err := c.checkUserExists(ctx, addedMember.UserID, "")
|
||||
@ -58,10 +58,10 @@ func (c *Commands) addProjectMember(ctx context.Context, projectAgg *eventstore.
|
||||
//ChangeProjectMember updates an existing member
|
||||
func (c *Commands) ChangeProjectMember(ctx context.Context, member *domain.Member, resourceOwner string) (*domain.Member, error) {
|
||||
if !member.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-LiaZi", "Errors.Project.Member.Invalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-LiaZi", "Errors.Project.Member.Invalid")
|
||||
}
|
||||
if len(domain.CheckForInvalidRoles(member.Roles, domain.ProjectRolePrefix, c.zitadelRoles)) > 0 {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-3m9d", "Errors.Project.Member.Invalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-3m9d", "Errors.Project.Member.Invalid")
|
||||
}
|
||||
|
||||
existingMember, err := c.projectMemberWriteModelByID(ctx, member.AggregateID, member.UserID, resourceOwner)
|
||||
@ -87,6 +87,9 @@ func (c *Commands) ChangeProjectMember(ctx context.Context, member *domain.Membe
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveProjectMember(ctx context.Context, projectID, userID, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||
if projectID == "" || userID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-66mHd", "Errors.Project.Member.Invalid")
|
||||
}
|
||||
m, err := c.projectMemberWriteModelByID(ctx, projectID, userID, resourceOwner)
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
return nil, err
|
||||
|
625
internal/command/project_member_test.go
Normal file
625
internal/command/project_member_test.go
Normal file
@ -0,0 +1,625 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"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/member"
|
||||
"github.com/caos/zitadel/internal/repository/project"
|
||||
"github.com/caos/zitadel/internal/repository/user"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/text/language"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddProjectMember(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
zitadelRoles []authz.RoleMapping
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
member *domain.Member
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.Member
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid member, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid roles, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_OWNER"},
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user not existing, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: domain.RoleProjectOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_OWNER"},
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member already exists, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectMemberAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"user1",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: domain.RoleProjectOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_OWNER"},
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member add uniqueconstraint err, already exists",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(),
|
||||
expectPushFailed(caos_errs.ThrowAlreadyExists(nil, "ERROR", "internal"),
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(project.NewProjectMemberAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"user1",
|
||||
[]string{"PROJECT_OWNER"}...,
|
||||
)),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("project1", "user1")),
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: domain.RoleProjectOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_OWNER"},
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member add, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(project.NewProjectMemberAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"user1",
|
||||
[]string{"PROJECT_OWNER"}...,
|
||||
)),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("project1", "user1")),
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: domain.RoleProjectOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_OWNER"},
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
ResourceOwner: "org1",
|
||||
AggregateID: "project1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{domain.RoleProjectOwner},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
zitadelRoles: tt.fields.zitadelRoles,
|
||||
}
|
||||
got, err := r.AddProjectMember(tt.args.ctx, tt.args.member, 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_ChangeProjectMember(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
zitadelRoles []authz.RoleMapping
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
member *domain.Member
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.Member
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid member, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid roles, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_OWNER"},
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: domain.RoleProjectOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_OWNER"},
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member not changed, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectMemberAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"user1",
|
||||
[]string{"PROJECT_OWNER"}...,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: domain.RoleProjectOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_OWNER"},
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member change, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectMemberAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"user1",
|
||||
[]string{"PROJECT_OWNER"}...,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(project.NewProjectMemberChangedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"user1",
|
||||
[]string{"PROJECT_OWNER", "PROJECT_VIEWER"}...,
|
||||
)),
|
||||
},
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: domain.RoleProjectOwner,
|
||||
},
|
||||
{
|
||||
Role: "PROJECT_VIEWER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_OWNER", "PROJECT_VIEWER"},
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.Member{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
ResourceOwner: "org1",
|
||||
AggregateID: "project1",
|
||||
},
|
||||
UserID: "user1",
|
||||
Roles: []string{domain.RoleProjectOwner, "PROJECT_VIEWER"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
zitadelRoles: tt.fields.zitadelRoles,
|
||||
}
|
||||
got, err := r.ChangeProjectMember(tt.args.ctx, tt.args.member, 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_RemoveProjectMember(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
projectID string
|
||||
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: "invalid member projectid missing, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "",
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid member userid missing, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
userID: "",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member not existing, nil result",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member remove, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectMemberAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"user1",
|
||||
[]string{"PROJECT_OWNER"}...,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(project.NewProjectMemberRemovedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"user1",
|
||||
)),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(member.NewRemoveMemberUniqueConstraint("project1", "user1")),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
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.RemoveProjectMember(tt.args.ctx, tt.args.projectID, 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ func (c *Commands) AddProjectRole(ctx context.Context, projectRole *domain.Proje
|
||||
|
||||
roleWriteModel := NewProjectRoleWriteModelWithKey(projectRole.Key, projectRole.AggregateID, resourceOwner)
|
||||
projectAgg := ProjectAggregateFromWriteModel(&roleWriteModel.WriteModel)
|
||||
events, err := c.addProjectRoles(ctx, projectAgg, projectRole.AggregateID, projectRole)
|
||||
events, err := c.addProjectRoles(ctx, projectAgg, projectRole)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -40,7 +40,7 @@ func (c *Commands) BulkAddProjectRole(ctx context.Context, projectID, resourceOw
|
||||
|
||||
roleWriteModel := NewProjectRoleWriteModel(projectID, resourceOwner)
|
||||
projectAgg := ProjectAggregateFromWriteModel(&roleWriteModel.WriteModel)
|
||||
events, err := c.addProjectRoles(ctx, projectAgg, projectID, projectRoles...)
|
||||
events, err := c.addProjectRoles(ctx, projectAgg, projectRoles...)
|
||||
if err != nil {
|
||||
return details, err
|
||||
}
|
||||
@ -56,11 +56,12 @@ func (c *Commands) BulkAddProjectRole(ctx context.Context, projectID, resourceOw
|
||||
return writeModelToObjectDetails(&roleWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) addProjectRoles(ctx context.Context, projectAgg *eventstore.Aggregate, projectID string, projectRoles ...*domain.ProjectRole) ([]eventstore.EventPusher, error) {
|
||||
func (c *Commands) addProjectRoles(ctx context.Context, projectAgg *eventstore.Aggregate, projectRoles ...*domain.ProjectRole) ([]eventstore.EventPusher, error) {
|
||||
var events []eventstore.EventPusher
|
||||
for _, projectRole := range projectRoles {
|
||||
projectRole.AggregateID = projectAgg.ID
|
||||
if !projectRole.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4m9vS", "Errors.Project.Invalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4m9vS", "Errors.Project.Role.Invalid")
|
||||
}
|
||||
events = append(events, project.NewRoleAddedEvent(
|
||||
ctx,
|
||||
@ -68,7 +69,6 @@ func (c *Commands) addProjectRoles(ctx context.Context, projectAgg *eventstore.A
|
||||
projectRole.Key,
|
||||
projectRole.DisplayName,
|
||||
projectRole.Group,
|
||||
projectID,
|
||||
))
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ func (c *Commands) addProjectRoles(ctx context.Context, projectAgg *eventstore.A
|
||||
|
||||
func (c *Commands) ChangeProjectRole(ctx context.Context, projectRole *domain.ProjectRole, resourceOwner string) (_ *domain.ProjectRole, err error) {
|
||||
if !projectRole.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4m9vS", "Errors.Project.Invalid")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4m9vS", "Errors.Project.Invalid")
|
||||
}
|
||||
err = c.checkProjectExists(ctx, projectRole.AggregateID, resourceOwner)
|
||||
if err != nil {
|
||||
@ -115,7 +115,7 @@ func (c *Commands) ChangeProjectRole(ctx context.Context, projectRole *domain.Pr
|
||||
|
||||
func (c *Commands) RemoveProjectRole(ctx context.Context, projectID, key, resourceOwner string, cascadingProjectGrantIds []string, cascadeUserGrantIDs ...string) (details *domain.ObjectDetails, err error) {
|
||||
if projectID == "" || key == "" {
|
||||
return details, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4m9vS", "Errors.Project.Role.Invalid")
|
||||
return details, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4m9vS", "Errors.Project.Role.Invalid")
|
||||
}
|
||||
existingRole, err := c.getProjectRoleWriteModelByID(ctx, key, projectID, resourceOwner)
|
||||
if err != nil {
|
||||
@ -126,7 +126,7 @@ func (c *Commands) RemoveProjectRole(ctx context.Context, projectID, key, resour
|
||||
}
|
||||
projectAgg := ProjectAggregateFromWriteModel(&existingRole.WriteModel)
|
||||
events := []eventstore.EventPusher{
|
||||
project.NewRoleRemovedEvent(ctx, projectAgg, key, projectID),
|
||||
project.NewRoleRemovedEvent(ctx, projectAgg, key),
|
||||
}
|
||||
|
||||
for _, projectGrantID := range cascadingProjectGrantIds {
|
||||
|
@ -101,7 +101,6 @@ func (wm *ProjectRoleWriteModel) NewProjectRoleChangedEvent(
|
||||
) (*project.RoleChangedEvent, bool, error) {
|
||||
changes := make([]project.RoleChanges, 0)
|
||||
var err error
|
||||
changes = append(changes, project.ChangeKey(key))
|
||||
|
||||
if wm.DisplayName != displayName {
|
||||
changes = append(changes, project.ChangeDisplayName(displayName))
|
||||
@ -112,7 +111,7 @@ func (wm *ProjectRoleWriteModel) NewProjectRoleChangedEvent(
|
||||
if len(changes) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
changeEvent, err := project.NewRoleChangedEvent(ctx, aggregate, changes)
|
||||
changeEvent, err := project.NewRoleChangedEvent(ctx, aggregate, key, changes)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
1053
internal/command/project_role_test.go
Normal file
1053
internal/command/project_role_test.go
Normal file
File diff suppressed because it is too large
Load Diff
1011
internal/command/project_test.go
Normal file
1011
internal/command/project_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -34,13 +34,9 @@ func (c *Commands) addUserGrant(ctx context.Context, userGrant *domain.UserGrant
|
||||
return nil, nil, err
|
||||
}
|
||||
if !userGrant.IsValid() {
|
||||
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M0fs", "Errors.UserGrant.Invalid")
|
||||
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M0fs", "Errors.UserGrant.Invalid")
|
||||
}
|
||||
err = c.checkUserExists(ctx, userGrant.UserID, "")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
err = c.checkProjectExists(ctx, userGrant.ProjectID, resourceOwner)
|
||||
err = c.checkUserGrantPreCondition(ctx, userGrant)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -83,10 +79,14 @@ func (c *Commands) changeUserGrant(ctx context.Context, userGrant *domain.UserGr
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if userGrant.IsValid() {
|
||||
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3M0sd", "Errors.UserGrant.Invalid")
|
||||
if !userGrant.IsValid() || userGrant.AggregateID == "" {
|
||||
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-3M0sd", "Errors.UserGrant.Invalid")
|
||||
}
|
||||
|
||||
err = c.checkUserGrantPreCondition(ctx, userGrant)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
existingUserGrant, err := c.userGrantWriteModelByID(ctx, userGrant.AggregateID, userGrant.ResourceOwner)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@ -130,7 +130,7 @@ func (c *Commands) removeRoleFromUserGrant(ctx context.Context, userGrantID stri
|
||||
if !keyExists {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-5m8g9", "Errors.UserGrant.RoleKeyNotFound")
|
||||
}
|
||||
changedUserGrant := NewUserGrantWriteModel(userGrantID, "")
|
||||
changedUserGrant := NewUserGrantWriteModel(userGrantID, existingUserGrant.ResourceOwner)
|
||||
userGrantAgg := UserGrantAggregateFromWriteModel(&changedUserGrant.WriteModel)
|
||||
|
||||
if cascade {
|
||||
@ -142,22 +142,22 @@ func (c *Commands) removeRoleFromUserGrant(ctx context.Context, userGrantID stri
|
||||
|
||||
func (c *Commands) DeactivateUserGrant(ctx context.Context, grantID, resourceOwner string) (objectDetails *domain.ObjectDetails, err error) {
|
||||
if grantID == "" || resourceOwner == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-M0dsf", "Errors.UserGrant.IDMissing")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-M0dsf", "Errors.UserGrant.IDMissing")
|
||||
}
|
||||
|
||||
existingUserGrant, err := c.userGrantWriteModelByID(ctx, grantID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = checkExplicitProjectPermission(ctx, existingUserGrant.ProjectGrantID, existingUserGrant.ProjectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingUserGrant.State == domain.UserGrantStateUnspecified || existingUserGrant.State == domain.UserGrantStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-3M9sd", "Errors.UserGrant.NotFound")
|
||||
}
|
||||
if existingUserGrant.State != domain.UserGrantStateActive {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-1S9gx", "Errors.UserGrant.NotActive")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-1S9gx", "Errors.UserGrant.NotActive")
|
||||
}
|
||||
err = checkExplicitProjectPermission(ctx, existingUserGrant.ProjectGrantID, existingUserGrant.ProjectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deactivateUserGrant := NewUserGrantWriteModel(grantID, resourceOwner)
|
||||
@ -175,24 +175,23 @@ func (c *Commands) DeactivateUserGrant(ctx context.Context, grantID, resourceOwn
|
||||
|
||||
func (c *Commands) ReactivateUserGrant(ctx context.Context, grantID, resourceOwner string) (objectDetails *domain.ObjectDetails, err error) {
|
||||
if grantID == "" || resourceOwner == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Qxy8v", "Errors.UserGrant.IDMissing")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-Qxy8v", "Errors.UserGrant.IDMissing")
|
||||
}
|
||||
|
||||
existingUserGrant, err := c.userGrantWriteModelByID(ctx, grantID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = checkExplicitProjectPermission(ctx, existingUserGrant.ProjectGrantID, existingUserGrant.ProjectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingUserGrant.State == domain.UserGrantStateUnspecified || existingUserGrant.State == domain.UserGrantStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-Lp0gs", "Errors.UserGrant.NotFound")
|
||||
}
|
||||
if existingUserGrant.State != domain.UserGrantStateInactive {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-1ML0v", "Errors.UserGrant.NotInactive")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-1ML0v", "Errors.UserGrant.NotInactive")
|
||||
}
|
||||
err = checkExplicitProjectPermission(ctx, existingUserGrant.ProjectGrantID, existingUserGrant.ProjectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deactivateUserGrant := NewUserGrantWriteModel(grantID, resourceOwner)
|
||||
userGrantAgg := UserGrantAggregateFromWriteModel(&deactivateUserGrant.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, usergrant.NewUserGrantReactivatedEvent(ctx, userGrantAgg))
|
||||
@ -224,11 +223,14 @@ func (c *Commands) RemoveUserGrant(ctx context.Context, grantID, resourceOwner s
|
||||
}
|
||||
|
||||
func (c *Commands) BulkRemoveUserGrant(ctx context.Context, grantIDs []string, resourceOwner string) (err error) {
|
||||
if len(grantIDs) == 0 {
|
||||
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-5M0sd", "Errors.UserGrant.IDMissing")
|
||||
}
|
||||
events := make([]eventstore.EventPusher, len(grantIDs))
|
||||
for i, grantID := range grantIDs {
|
||||
event, _, err := c.removeUserGrant(ctx, grantID, resourceOwner, false)
|
||||
if err != nil {
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
events[i] = event
|
||||
}
|
||||
@ -238,13 +240,16 @@ func (c *Commands) BulkRemoveUserGrant(ctx context.Context, grantIDs []string, r
|
||||
|
||||
func (c *Commands) removeUserGrant(ctx context.Context, grantID, resourceOwner string, cascade bool) (_ eventstore.EventPusher, writeModel *UserGrantWriteModel, err error) {
|
||||
if grantID == "" {
|
||||
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-J9sc5", "Errors.UserGrant.IDMissing")
|
||||
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-J9sc5", "Errors.UserGrant.IDMissing")
|
||||
}
|
||||
|
||||
existingUserGrant, err := c.userGrantWriteModelByID(ctx, grantID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if existingUserGrant.State == domain.UserGrantStateUnspecified || existingUserGrant.State == domain.UserGrantStateRemoved {
|
||||
return nil, nil, caos_errs.ThrowNotFound(nil, "COMMAND-1My0t", "Errors.UserGrant.NotFound")
|
||||
}
|
||||
if !cascade {
|
||||
err = checkExplicitProjectPermission(ctx, existingUserGrant.ProjectGrantID, existingUserGrant.ProjectID)
|
||||
if err != nil {
|
||||
@ -252,11 +257,7 @@ func (c *Commands) removeUserGrant(ctx context.Context, grantID, resourceOwner s
|
||||
}
|
||||
}
|
||||
|
||||
if existingUserGrant.State == domain.UserGrantStateUnspecified || existingUserGrant.State == domain.UserGrantStateRemoved {
|
||||
return nil, nil, caos_errs.ThrowNotFound(nil, "COMMAND-1My0t", "Errors.UserGrant.NotFound")
|
||||
}
|
||||
|
||||
removeUserGrant := NewUserGrantWriteModel(grantID, resourceOwner)
|
||||
removeUserGrant := NewUserGrantWriteModel(grantID, existingUserGrant.ResourceOwner)
|
||||
userGrantAgg := UserGrantAggregateFromWriteModel(&removeUserGrant.WriteModel)
|
||||
if cascade {
|
||||
return usergrant.NewUserGrantCascadeRemovedEvent(
|
||||
@ -273,6 +274,7 @@ func (c *Commands) removeUserGrant(ctx context.Context, grantID, resourceOwner s
|
||||
existingUserGrant.ProjectID,
|
||||
existingUserGrant.ProjectGrantID), existingUserGrant, nil
|
||||
}
|
||||
|
||||
func (c *Commands) userGrantWriteModelByID(ctx context.Context, userGrantID, resourceOwner string) (writeModel *UserGrantWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
@ -284,3 +286,24 @@ func (c *Commands) userGrantWriteModelByID(ctx context.Context, userGrantID, res
|
||||
}
|
||||
return writeModel, nil
|
||||
}
|
||||
|
||||
func (c *Commands) checkUserGrantPreCondition(ctx context.Context, usergrant *domain.UserGrant) error {
|
||||
preConditions := NewUserGrantPreConditionReadModel(usergrant.UserID, usergrant.ProjectID, usergrant.ProjectGrantID)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, preConditions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !preConditions.UserExists {
|
||||
return caos_errs.ThrowPreconditionFailed(err, "COMMAND-4f8sg", "Errors.User.NotFound")
|
||||
}
|
||||
if !preConditions.ProjectExists {
|
||||
return caos_errs.ThrowPreconditionFailed(err, "COMMAND-3n77S", "Errors.Project.NotFound")
|
||||
}
|
||||
if usergrant.ProjectGrantID != "" && !preConditions.ProjectGrantExists {
|
||||
return caos_errs.ThrowPreconditionFailed(err, "COMMAND-4m9ff", "Errors.Project.Grant.NotFound")
|
||||
}
|
||||
if usergrant.HasInvalidRoles(preConditions.ExistingRoleKeys) {
|
||||
return caos_errs.ThrowPreconditionFailed(err, "COMMAND-mm9F4", "Errors.Project.Role.NotFound")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ package command
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/project"
|
||||
"github.com/caos/zitadel/internal/repository/user"
|
||||
"github.com/caos/zitadel/internal/repository/usergrant"
|
||||
)
|
||||
|
||||
@ -76,3 +78,85 @@ func (wm *UserGrantWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
func UserGrantAggregateFromWriteModel(wm *eventstore.WriteModel) *eventstore.Aggregate {
|
||||
return eventstore.AggregateFromWriteModel(wm, usergrant.AggregateType, usergrant.AggregateVersion)
|
||||
}
|
||||
|
||||
type UserGrantPreConditionReadModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
UserID string
|
||||
ProjectID string
|
||||
ProjectGrantID string
|
||||
UserExists bool
|
||||
ProjectExists bool
|
||||
ProjectGrantExists bool
|
||||
ExistingRoleKeys []string
|
||||
}
|
||||
|
||||
func NewUserGrantPreConditionReadModel(userID, projectID, projectGrantID string) *UserGrantPreConditionReadModel {
|
||||
return &UserGrantPreConditionReadModel{
|
||||
UserID: userID,
|
||||
ProjectID: projectID,
|
||||
ProjectGrantID: projectGrantID,
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *UserGrantPreConditionReadModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanAddedEvent:
|
||||
wm.UserExists = true
|
||||
case *user.MachineAddedEvent:
|
||||
wm.UserExists = true
|
||||
case *user.UserRemovedEvent:
|
||||
wm.UserExists = false
|
||||
case *project.ProjectAddedEvent:
|
||||
wm.ProjectExists = true
|
||||
case *project.ProjectRemovedEvent:
|
||||
wm.ProjectExists = false
|
||||
case *project.GrantAddedEvent:
|
||||
if wm.ProjectGrantID == e.GrantID {
|
||||
wm.ProjectGrantExists = true
|
||||
}
|
||||
wm.ExistingRoleKeys = e.RoleKeys
|
||||
case *project.GrantChangedEvent:
|
||||
wm.ExistingRoleKeys = e.RoleKeys
|
||||
case *project.GrantRemovedEvent:
|
||||
if wm.ProjectGrantID == e.GrantID {
|
||||
wm.ProjectGrantExists = false
|
||||
}
|
||||
wm.ExistingRoleKeys = []string{}
|
||||
case *project.RoleAddedEvent:
|
||||
if wm.ProjectGrantID != "" {
|
||||
continue
|
||||
}
|
||||
wm.ExistingRoleKeys = append(wm.ExistingRoleKeys, e.Key)
|
||||
case *project.RoleRemovedEvent:
|
||||
if wm.ProjectGrantID != "" {
|
||||
continue
|
||||
}
|
||||
for i, key := range wm.ExistingRoleKeys {
|
||||
if key == e.Key {
|
||||
copy(wm.ExistingRoleKeys[i:], wm.ExistingRoleKeys[i+1:])
|
||||
wm.ExistingRoleKeys[len(wm.ExistingRoleKeys)-1] = ""
|
||||
wm.ExistingRoleKeys = wm.ExistingRoleKeys[:len(wm.ExistingRoleKeys)-1]
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *UserGrantPreConditionReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType, project.AggregateType).
|
||||
AggregateIDs(wm.UserID, wm.ProjectID).
|
||||
EventTypes(user.HumanAddedType,
|
||||
user.MachineAddedEventType,
|
||||
user.UserRemovedType,
|
||||
project.ProjectAddedType,
|
||||
project.ProjectRemovedType,
|
||||
project.GrantAddedType,
|
||||
project.GrantRemovedType,
|
||||
project.RoleAddedType,
|
||||
project.RoleRemovedType)
|
||||
return query
|
||||
}
|
||||
|
2027
internal/command/user_grant_test.go
Normal file
2027
internal/command/user_grant_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -34,7 +34,7 @@ const (
|
||||
)
|
||||
|
||||
func (a *APIApp) IsValid() bool {
|
||||
return true
|
||||
return a.AppName != ""
|
||||
}
|
||||
|
||||
func (a *APIApp) setClientID(clientID string) {
|
||||
|
@ -113,6 +113,9 @@ const (
|
||||
)
|
||||
|
||||
func (a *OIDCApp) IsValid() bool {
|
||||
if a.AppName == "" || a.ClockSkew > time.Second*5 || a.ClockSkew < time.Second*0 {
|
||||
return false
|
||||
}
|
||||
grantTypes := a.getRequiredGrantTypes()
|
||||
for _, grantType := range grantTypes {
|
||||
ok := containsOIDCGrantType(a.GrantTypes, grantType)
|
||||
|
185
internal/domain/application_oidc_test.go
Normal file
185
internal/domain/application_oidc_test.go
Normal file
@ -0,0 +1,185 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestApplicationValid(t *testing.T) {
|
||||
type args struct {
|
||||
app *OIDCApp
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result bool
|
||||
}{
|
||||
{
|
||||
name: "no app name",
|
||||
args: args{
|
||||
app: &OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"},
|
||||
AppID: "AppID",
|
||||
AppName: "",
|
||||
ResponseTypes: []OIDCResponseType{OIDCResponseTypeCode},
|
||||
GrantTypes: []OIDCGrantType{OIDCGrantTypeAuthorizationCode},
|
||||
},
|
||||
},
|
||||
result: false,
|
||||
},
|
||||
{
|
||||
name: "invalid clock skew",
|
||||
args: args{
|
||||
app: &OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"},
|
||||
AppID: "AppID",
|
||||
AppName: "AppName",
|
||||
ClockSkew: time.Minute * 1,
|
||||
ResponseTypes: []OIDCResponseType{OIDCResponseTypeCode},
|
||||
GrantTypes: []OIDCGrantType{OIDCGrantTypeAuthorizationCode},
|
||||
},
|
||||
},
|
||||
result: false,
|
||||
},
|
||||
{
|
||||
name: "invalid clock skew minus",
|
||||
args: args{
|
||||
app: &OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"},
|
||||
AppID: "AppID",
|
||||
AppName: "AppName",
|
||||
ClockSkew: time.Minute * -1,
|
||||
ResponseTypes: []OIDCResponseType{OIDCResponseTypeCode},
|
||||
GrantTypes: []OIDCGrantType{OIDCGrantTypeAuthorizationCode},
|
||||
},
|
||||
},
|
||||
result: false,
|
||||
},
|
||||
{
|
||||
name: "valid oidc application: responsetype code",
|
||||
args: args{
|
||||
app: &OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"},
|
||||
AppID: "AppID",
|
||||
AppName: "Name",
|
||||
ResponseTypes: []OIDCResponseType{OIDCResponseTypeCode},
|
||||
GrantTypes: []OIDCGrantType{OIDCGrantTypeAuthorizationCode},
|
||||
},
|
||||
},
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
name: "invalid oidc application: responsetype code",
|
||||
args: args{
|
||||
app: &OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"},
|
||||
AppID: "AppID",
|
||||
AppName: "Name",
|
||||
ResponseTypes: []OIDCResponseType{OIDCResponseTypeCode},
|
||||
GrantTypes: []OIDCGrantType{OIDCGrantTypeImplicit},
|
||||
},
|
||||
},
|
||||
result: false,
|
||||
},
|
||||
{
|
||||
name: "valid oidc application: responsetype id_token",
|
||||
args: args{
|
||||
app: &OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"},
|
||||
AppID: "AppID",
|
||||
AppName: "Name",
|
||||
ResponseTypes: []OIDCResponseType{OIDCResponseTypeIDToken},
|
||||
GrantTypes: []OIDCGrantType{OIDCGrantTypeImplicit},
|
||||
},
|
||||
},
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
name: "invalid oidc application: responsetype id_token",
|
||||
args: args{
|
||||
app: &OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"},
|
||||
AppID: "AppID",
|
||||
AppName: "Name",
|
||||
ResponseTypes: []OIDCResponseType{OIDCResponseTypeIDToken},
|
||||
GrantTypes: []OIDCGrantType{OIDCGrantTypeAuthorizationCode},
|
||||
},
|
||||
},
|
||||
result: false,
|
||||
},
|
||||
{
|
||||
name: "valid oidc application: responsetype token_id_token",
|
||||
args: args{
|
||||
app: &OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"},
|
||||
AppID: "AppID",
|
||||
AppName: "Name",
|
||||
ResponseTypes: []OIDCResponseType{OIDCResponseTypeIDTokenToken},
|
||||
GrantTypes: []OIDCGrantType{OIDCGrantTypeImplicit},
|
||||
},
|
||||
},
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
name: "invalid oidc application: responsetype token_id_token",
|
||||
args: args{
|
||||
app: &OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"},
|
||||
AppID: "AppID",
|
||||
AppName: "Name",
|
||||
ResponseTypes: []OIDCResponseType{OIDCResponseTypeIDTokenToken},
|
||||
GrantTypes: []OIDCGrantType{OIDCGrantTypeAuthorizationCode},
|
||||
},
|
||||
},
|
||||
result: false,
|
||||
},
|
||||
{
|
||||
name: "valid oidc application: responsetype code & id_token",
|
||||
args: args{
|
||||
app: &OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"},
|
||||
AppID: "AppID",
|
||||
AppName: "Name",
|
||||
ResponseTypes: []OIDCResponseType{OIDCResponseTypeCode, OIDCResponseTypeIDToken},
|
||||
GrantTypes: []OIDCGrantType{OIDCGrantTypeAuthorizationCode, OIDCGrantTypeImplicit},
|
||||
},
|
||||
},
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
name: "valid oidc application: responsetype code & token_id_token",
|
||||
args: args{
|
||||
app: &OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"},
|
||||
AppID: "AppID",
|
||||
AppName: "Name",
|
||||
ResponseTypes: []OIDCResponseType{OIDCResponseTypeCode, OIDCResponseTypeIDTokenToken},
|
||||
GrantTypes: []OIDCGrantType{OIDCGrantTypeAuthorizationCode, OIDCGrantTypeImplicit},
|
||||
},
|
||||
},
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
name: "valid oidc application: responsetype code & id_token & token_id_token",
|
||||
args: args{
|
||||
app: &OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"},
|
||||
AppID: "AppID",
|
||||
AppName: "Name",
|
||||
ResponseTypes: []OIDCResponseType{OIDCResponseTypeCode, OIDCResponseTypeIDToken, OIDCResponseTypeIDTokenToken},
|
||||
GrantTypes: []OIDCGrantType{OIDCGrantTypeAuthorizationCode, OIDCGrantTypeImplicit},
|
||||
},
|
||||
},
|
||||
result: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := tt.args.app.IsValid()
|
||||
if result != tt.result {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -25,6 +25,10 @@ func (i *Member) IsValid() bool {
|
||||
return i.AggregateID != "" && i.UserID != "" && len(i.Roles) != 0
|
||||
}
|
||||
|
||||
func (i *Member) IsIAMValid() bool {
|
||||
return i.UserID != "" && len(i.Roles) != 0
|
||||
}
|
||||
|
||||
type MemberState int32
|
||||
|
||||
const (
|
||||
|
@ -11,3 +11,7 @@ type LabelPolicy struct {
|
||||
PrimaryColor string
|
||||
SecondaryColor string
|
||||
}
|
||||
|
||||
func (p *LabelPolicy) IsValid() bool {
|
||||
return p.PrimaryColor != "" && p.SecondaryColor != ""
|
||||
}
|
||||
|
@ -7,12 +7,8 @@ import (
|
||||
type Project struct {
|
||||
models.ObjectRoot
|
||||
|
||||
State ProjectState
|
||||
Name string
|
||||
Members []*Member
|
||||
Roles []*ProjectRole
|
||||
//Applications []*Application
|
||||
//Grants []*ProjectGrant
|
||||
State ProjectState
|
||||
Name string
|
||||
ProjectRoleAssertion bool
|
||||
ProjectRoleCheck bool
|
||||
}
|
||||
|
@ -29,21 +29,21 @@ func (p *ProjectGrant) IsValid() bool {
|
||||
return p.GrantedOrgID != ""
|
||||
}
|
||||
|
||||
func GetRemovedRoles(existingRoles, newRoles []string) []string {
|
||||
removed := make([]string, 0)
|
||||
for _, role := range existingRoles {
|
||||
if !containsKey(newRoles, role) {
|
||||
removed = append(removed, role)
|
||||
}
|
||||
}
|
||||
return removed
|
||||
}
|
||||
|
||||
func containsKey(roles []string, key string) bool {
|
||||
for _, role := range roles {
|
||||
if role == key {
|
||||
func (g *ProjectGrant) HasInvalidRoles(validRoles []string) bool {
|
||||
for _, roleKey := range g.RoleKeys {
|
||||
if !containsRoleKey(roleKey, validRoles) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetRemovedRoles(existingRoles, newRoles []string) []string {
|
||||
removed := make([]string, 0)
|
||||
for _, role := range existingRoles {
|
||||
if !containsRoleKey(role, newRoles) {
|
||||
removed = append(removed, role)
|
||||
}
|
||||
}
|
||||
return removed
|
||||
}
|
||||
|
@ -27,3 +27,12 @@ func NewProjectRole(projectID, key string) *ProjectRole {
|
||||
func (p *ProjectRole) IsValid() bool {
|
||||
return p.AggregateID != "" && p.Key != ""
|
||||
}
|
||||
|
||||
func containsRoleKey(roleKey string, validRoles []string) bool {
|
||||
for _, validRole := range validRoles {
|
||||
if roleKey == validRole {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -24,3 +24,12 @@ const (
|
||||
func (u *UserGrant) IsValid() bool {
|
||||
return u.ProjectID != "" && u.UserID != ""
|
||||
}
|
||||
|
||||
func (g *UserGrant) HasInvalidRoles(validRoles []string) bool {
|
||||
for _, roleKey := range g.RoleKeys {
|
||||
if !containsRoleKey(roleKey, validRoles) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ func (es *Eventstore) PushEvents(ctx context.Context, pushEvents ...EventPusher)
|
||||
func eventsToRepository(pushEvents []EventPusher) (events []*repository.Event, constraints []*repository.UniqueConstraint, err error) {
|
||||
events = make([]*repository.Event, len(pushEvents))
|
||||
for i, event := range pushEvents {
|
||||
data, err := eventData(event)
|
||||
data, err := EventData(event)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -195,7 +195,7 @@ func (es *Eventstore) RegisterFilterEventMapper(eventType EventType, mapper func
|
||||
return es
|
||||
}
|
||||
|
||||
func eventData(event EventPusher) ([]byte, error) {
|
||||
func EventData(event EventPusher) ([]byte, error) {
|
||||
switch data := event.Data().(type) {
|
||||
case nil:
|
||||
return nil, nil
|
||||
|
@ -350,13 +350,13 @@ func Test_eventData(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := eventData(tt.args.event)
|
||||
got, err := EventData(tt.args.event)
|
||||
if (err != nil) != tt.res.wantErr {
|
||||
t.Errorf("eventData() error = %v, wantErr %v", err, tt.res.wantErr)
|
||||
t.Errorf("EventData() error = %v, wantErr %v", err, tt.res.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.res.jsonText) {
|
||||
t.Errorf("eventData() = %v, want %v", string(got), string(tt.res.jsonText))
|
||||
t.Errorf("EventData() = %v, want %v", string(got), string(tt.res.jsonText))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
3
internal/eventstore/repository/mock/gen_mock.go
Normal file
3
internal/eventstore/repository/mock/gen_mock.go
Normal file
@ -0,0 +1,3 @@
|
||||
package mock
|
||||
|
||||
//go:generate mockgen -package mock -destination ./repository.mock.go github.com/caos/zitadel/internal/eventstore/repository Repository
|
98
internal/eventstore/repository/mock/repository.mock.go
Normal file
98
internal/eventstore/repository/mock/repository.mock.go
Normal file
@ -0,0 +1,98 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/caos/zitadel/internal/eventstore/repository (interfaces: Repository)
|
||||
|
||||
// Package mock is a generated GoMock package.
|
||||
package mock
|
||||
|
||||
import (
|
||||
context "context"
|
||||
repository "github.com/caos/zitadel/internal/eventstore/repository"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockRepository is a mock of Repository interface
|
||||
type MockRepository struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockRepositoryMockRecorder
|
||||
}
|
||||
|
||||
// MockRepositoryMockRecorder is the mock recorder for MockRepository
|
||||
type MockRepositoryMockRecorder struct {
|
||||
mock *MockRepository
|
||||
}
|
||||
|
||||
// NewMockRepository creates a new mock instance
|
||||
func NewMockRepository(ctrl *gomock.Controller) *MockRepository {
|
||||
mock := &MockRepository{ctrl: ctrl}
|
||||
mock.recorder = &MockRepositoryMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockRepository) EXPECT() *MockRepositoryMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Filter mocks base method
|
||||
func (m *MockRepository) Filter(arg0 context.Context, arg1 *repository.SearchQuery) ([]*repository.Event, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Filter", arg0, arg1)
|
||||
ret0, _ := ret[0].([]*repository.Event)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Filter indicates an expected call of Filter
|
||||
func (mr *MockRepositoryMockRecorder) Filter(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Filter", reflect.TypeOf((*MockRepository)(nil).Filter), arg0, arg1)
|
||||
}
|
||||
|
||||
// Health mocks base method
|
||||
func (m *MockRepository) Health(arg0 context.Context) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Health", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Health indicates an expected call of Health
|
||||
func (mr *MockRepositoryMockRecorder) Health(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Health", reflect.TypeOf((*MockRepository)(nil).Health), arg0)
|
||||
}
|
||||
|
||||
// LatestSequence mocks base method
|
||||
func (m *MockRepository) LatestSequence(arg0 context.Context, arg1 *repository.SearchQuery) (uint64, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "LatestSequence", arg0, arg1)
|
||||
ret0, _ := ret[0].(uint64)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// LatestSequence indicates an expected call of LatestSequence
|
||||
func (mr *MockRepositoryMockRecorder) LatestSequence(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestSequence", reflect.TypeOf((*MockRepository)(nil).LatestSequence), arg0, arg1)
|
||||
}
|
||||
|
||||
// Push mocks base method
|
||||
func (m *MockRepository) Push(arg0 context.Context, arg1 []*repository.Event, arg2 ...*repository.UniqueConstraint) error {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []interface{}{arg0, arg1}
|
||||
for _, a := range arg2 {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
ret := m.ctrl.Call(m, "Push", varargs...)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Push indicates an expected call of Push
|
||||
func (mr *MockRepositoryMockRecorder) Push(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
varargs := append([]interface{}{arg0, arg1}, arg2...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Push", reflect.TypeOf((*MockRepository)(nil).Push), varargs...)
|
||||
}
|
53
internal/eventstore/repository/mock/repository.mock.impl.go
Normal file
53
internal/eventstore/repository/mock/repository.mock.impl.go
Normal file
@ -0,0 +1,53 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||
)
|
||||
|
||||
func NewRepo(t *testing.T) *MockRepository {
|
||||
return NewMockRepository(gomock.NewController(t))
|
||||
}
|
||||
|
||||
func (m *MockRepository) ExpectFilterNoEventsNoError() *MockRepository {
|
||||
m.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(nil, nil)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MockRepository) ExpectFilterEvents(events ...*repository.Event) *MockRepository {
|
||||
m.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(events, nil)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MockRepository) ExpectPush(expectedEvents []*repository.Event, expectedUniqueConstraints ...*repository.UniqueConstraint) *MockRepository {
|
||||
m.EXPECT().Push(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
|
||||
func(ctx context.Context, events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) error {
|
||||
assert.Equal(m.ctrl.T, expectedEvents, events)
|
||||
if expectedUniqueConstraints == nil {
|
||||
expectedUniqueConstraints = []*repository.UniqueConstraint{}
|
||||
}
|
||||
assert.Equal(m.ctrl.T, expectedUniqueConstraints, uniqueConstraints)
|
||||
return nil
|
||||
},
|
||||
)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MockRepository) ExpectPushFailed(err error, expectedEvents []*repository.Event, expectedUniqueConstraints ...*repository.UniqueConstraint) *MockRepository {
|
||||
m.EXPECT().Push(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
|
||||
func(ctx context.Context, events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) error {
|
||||
assert.Equal(m.ctrl.T, expectedEvents, events)
|
||||
if expectedUniqueConstraints == nil {
|
||||
expectedUniqueConstraints = []*repository.UniqueConstraint{}
|
||||
}
|
||||
assert.Equal(m.ctrl.T, expectedUniqueConstraints, uniqueConstraints)
|
||||
return err
|
||||
},
|
||||
)
|
||||
return m
|
||||
}
|
27
internal/id/mock/generator.mock.impl.go
Normal file
27
internal/id/mock/generator.mock.impl.go
Normal file
@ -0,0 +1,27 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
func NewIDGenerator(t *testing.T) *MockGenerator {
|
||||
m := NewMockGenerator(gomock.NewController(t))
|
||||
m.EXPECT().Next().Return("1", nil)
|
||||
return m
|
||||
}
|
||||
|
||||
func NewIDGeneratorExpectIDs(t *testing.T, ids ...string) *MockGenerator {
|
||||
m := NewMockGenerator(gomock.NewController(t))
|
||||
for _, id := range ids {
|
||||
m.EXPECT().Next().Return(id, nil)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func ExpectID(t *testing.T, id string) *MockGenerator {
|
||||
m := NewMockGenerator(gomock.NewController(t))
|
||||
m.EXPECT().Next().Return(id, nil)
|
||||
return m
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package iam
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
@ -16,3 +17,14 @@ const (
|
||||
type Aggregate struct {
|
||||
eventstore.Aggregate
|
||||
}
|
||||
|
||||
func NewAggregate() *Aggregate {
|
||||
return &Aggregate{
|
||||
Aggregate: eventstore.Aggregate{
|
||||
Typ: AggregateType,
|
||||
Version: AggregateVersion,
|
||||
ID: domain.IAMID,
|
||||
ResourceOwner: domain.IAMID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
|
||||
RegisterFilterEventMapper(LoginPolicyAddedEventType, LoginPolicyAddedEventMapper).
|
||||
RegisterFilterEventMapper(LoginPolicyChangedEventType, LoginPolicyChangedEventMapper).
|
||||
RegisterFilterEventMapper(OrgIAMPolicyAddedEventType, OrgIAMPolicyAddedEventMapper).
|
||||
RegisterFilterEventMapper(OrgIAMPolicyChangedEventType, OrgIAMPolicyChangedEventMapper).
|
||||
RegisterFilterEventMapper(PasswordAgePolicyAddedEventType, PasswordAgePolicyAddedEventMapper).
|
||||
RegisterFilterEventMapper(PasswordAgePolicyChangedEventType, PasswordAgePolicyChangedEventMapper).
|
||||
RegisterFilterEventMapper(PasswordComplexityPolicyAddedEventType, PasswordComplexityPolicyAddedEventMapper).
|
||||
|
@ -16,3 +16,14 @@ const (
|
||||
type Aggregate struct {
|
||||
eventstore.Aggregate
|
||||
}
|
||||
|
||||
func NewAggregate(id, resourceOwner string) *Aggregate {
|
||||
return &Aggregate{
|
||||
Aggregate: eventstore.Aggregate{
|
||||
Typ: AggregateType,
|
||||
Version: AggregateVersion,
|
||||
ID: id,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -12,3 +12,14 @@ const (
|
||||
type Aggregate struct {
|
||||
eventstore.Aggregate
|
||||
}
|
||||
|
||||
func NewAggregate(id, resourceOwner string) *Aggregate {
|
||||
return &Aggregate{
|
||||
Aggregate: eventstore.Aggregate{
|
||||
Typ: AggregateType,
|
||||
Version: AggregateVersion,
|
||||
ID: id,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -36,9 +36,8 @@ func NewRemoveApplicationUniqueConstraint(name, projectID string) *eventstore.Ev
|
||||
type ApplicationAddedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
AppID string `json:"appId,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
projectID string
|
||||
AppID string `json:"appId,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
func (e *ApplicationAddedEvent) Data() interface{} {
|
||||
@ -46,15 +45,14 @@ func (e *ApplicationAddedEvent) Data() interface{} {
|
||||
}
|
||||
|
||||
func (e *ApplicationAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
return []*eventstore.EventUniqueConstraint{NewAddApplicationUniqueConstraint(e.Name, e.projectID)}
|
||||
return []*eventstore.EventUniqueConstraint{NewAddApplicationUniqueConstraint(e.Name, e.Aggregate().ID)}
|
||||
}
|
||||
|
||||
func NewApplicationAddedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
appID,
|
||||
name,
|
||||
projectID string,
|
||||
name string,
|
||||
) *ApplicationAddedEvent {
|
||||
return &ApplicationAddedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
@ -62,9 +60,8 @@ func NewApplicationAddedEvent(
|
||||
aggregate,
|
||||
ApplicationAddedType,
|
||||
),
|
||||
AppID: appID,
|
||||
Name: name,
|
||||
projectID: projectID,
|
||||
AppID: appID,
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,10 +81,9 @@ func ApplicationAddedEventMapper(event *repository.Event) (eventstore.EventReade
|
||||
type ApplicationChangedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
AppID string `json:"appId,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
oldName string
|
||||
projectID string
|
||||
AppID string `json:"appId,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
oldName string
|
||||
}
|
||||
|
||||
func (e *ApplicationChangedEvent) Data() interface{} {
|
||||
@ -96,8 +92,8 @@ func (e *ApplicationChangedEvent) Data() interface{} {
|
||||
|
||||
func (e *ApplicationChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
return []*eventstore.EventUniqueConstraint{
|
||||
NewRemoveApplicationUniqueConstraint(e.oldName, e.projectID),
|
||||
NewAddApplicationUniqueConstraint(e.Name, e.projectID),
|
||||
NewRemoveApplicationUniqueConstraint(e.oldName, e.Aggregate().ID),
|
||||
NewAddApplicationUniqueConstraint(e.Name, e.Aggregate().ID),
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,8 +102,7 @@ func NewApplicationChangedEvent(
|
||||
aggregate *eventstore.Aggregate,
|
||||
appID,
|
||||
oldName,
|
||||
newName,
|
||||
projectID string,
|
||||
newName string,
|
||||
) *ApplicationChangedEvent {
|
||||
return &ApplicationChangedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
@ -115,10 +110,9 @@ func NewApplicationChangedEvent(
|
||||
aggregate,
|
||||
ApplicationChangedType,
|
||||
),
|
||||
AppID: appID,
|
||||
Name: newName,
|
||||
oldName: oldName,
|
||||
projectID: projectID,
|
||||
AppID: appID,
|
||||
Name: newName,
|
||||
oldName: oldName,
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,9 +216,8 @@ func ApplicationReactivatedEventMapper(event *repository.Event) (eventstore.Even
|
||||
type ApplicationRemovedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
AppID string `json:"appId,omitempty"`
|
||||
name string
|
||||
projectID string
|
||||
AppID string `json:"appId,omitempty"`
|
||||
name string
|
||||
}
|
||||
|
||||
func (e *ApplicationRemovedEvent) Data() interface{} {
|
||||
@ -232,15 +225,14 @@ func (e *ApplicationRemovedEvent) Data() interface{} {
|
||||
}
|
||||
|
||||
func (e *ApplicationRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
return []*eventstore.EventUniqueConstraint{NewRemoveApplicationUniqueConstraint(e.name, e.projectID)}
|
||||
return []*eventstore.EventUniqueConstraint{NewRemoveApplicationUniqueConstraint(e.name, e.Aggregate().ID)}
|
||||
}
|
||||
|
||||
func NewApplicationRemovedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
appID,
|
||||
name,
|
||||
projectID string,
|
||||
name string,
|
||||
) *ApplicationRemovedEvent {
|
||||
return &ApplicationRemovedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
@ -248,9 +240,8 @@ func NewApplicationRemovedEvent(
|
||||
aggregate,
|
||||
ApplicationRemovedType,
|
||||
),
|
||||
AppID: appID,
|
||||
name: name,
|
||||
projectID: projectID,
|
||||
AppID: appID,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,6 @@ type GrantAddedEvent struct {
|
||||
GrantID string `json:"grantId,omitempty"`
|
||||
GrantedOrgID string `json:"grantedOrgId,omitempty"`
|
||||
RoleKeys []string `json:"roleKeys,omitempty"`
|
||||
projectID string
|
||||
}
|
||||
|
||||
func (e *GrantAddedEvent) Data() interface{} {
|
||||
@ -48,15 +47,14 @@ func (e *GrantAddedEvent) Data() interface{} {
|
||||
}
|
||||
|
||||
func (e *GrantAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
return []*eventstore.EventUniqueConstraint{NewAddProjectGrantUniqueConstraint(e.GrantedOrgID, e.projectID)}
|
||||
return []*eventstore.EventUniqueConstraint{NewAddProjectGrantUniqueConstraint(e.GrantedOrgID, e.Aggregate().ID)}
|
||||
}
|
||||
|
||||
func NewGrantAddedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
grantID,
|
||||
grantedOrgID,
|
||||
projectID string,
|
||||
grantedOrgID string,
|
||||
roleKeys []string,
|
||||
) *GrantAddedEvent {
|
||||
return &GrantAddedEvent{
|
||||
@ -68,7 +66,6 @@ func NewGrantAddedEvent(
|
||||
GrantID: grantID,
|
||||
GrantedOrgID: grantedOrgID,
|
||||
RoleKeys: roleKeys,
|
||||
projectID: projectID,
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,7 +261,6 @@ type GrantRemovedEvent struct {
|
||||
|
||||
GrantID string `json:"grantId,omitempty"`
|
||||
grantedOrgID string
|
||||
projectID string
|
||||
}
|
||||
|
||||
func (e *GrantRemovedEvent) Data() interface{} {
|
||||
@ -272,15 +268,14 @@ func (e *GrantRemovedEvent) Data() interface{} {
|
||||
}
|
||||
|
||||
func (e *GrantRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
return []*eventstore.EventUniqueConstraint{NewRemoveProjectGrantUniqueConstraint(e.grantedOrgID, e.projectID)}
|
||||
return []*eventstore.EventUniqueConstraint{NewRemoveProjectGrantUniqueConstraint(e.grantedOrgID, e.Aggregate().ID)}
|
||||
}
|
||||
|
||||
func NewGrantRemovedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
grantID,
|
||||
grantedOrgID,
|
||||
projectID string,
|
||||
grantedOrgID string,
|
||||
) *GrantRemovedEvent {
|
||||
return &GrantRemovedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
@ -289,7 +284,6 @@ func NewGrantRemovedEvent(
|
||||
GrantRemovedType,
|
||||
),
|
||||
GrantID: grantID,
|
||||
projectID: projectID,
|
||||
grantedOrgID: grantedOrgID,
|
||||
}
|
||||
}
|
||||
|
@ -35,10 +35,9 @@ func NewRemoveProjectGrantMemberUniqueConstraint(projectID, userID, grantID stri
|
||||
type GrantMemberAddedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
Roles []string `json:"roles"`
|
||||
UserID string `json:"userId"`
|
||||
GrantID string `json:"grantId"`
|
||||
projectID string
|
||||
Roles []string `json:"roles"`
|
||||
UserID string `json:"userId"`
|
||||
GrantID string `json:"grantId"`
|
||||
}
|
||||
|
||||
func (e *GrantMemberAddedEvent) Data() interface{} {
|
||||
@ -46,13 +45,12 @@ func (e *GrantMemberAddedEvent) Data() interface{} {
|
||||
}
|
||||
|
||||
func (e *GrantMemberAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
return []*eventstore.EventUniqueConstraint{NewAddProjectGrantMemberUniqueConstraint(e.projectID, e.UserID, e.GrantID)}
|
||||
return []*eventstore.EventUniqueConstraint{NewAddProjectGrantMemberUniqueConstraint(e.Aggregate().ID, e.UserID, e.GrantID)}
|
||||
}
|
||||
|
||||
func NewProjectGrantMemberAddedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
projectID,
|
||||
userID,
|
||||
grantID string,
|
||||
roles ...string,
|
||||
@ -63,10 +61,9 @@ func NewProjectGrantMemberAddedEvent(
|
||||
aggregate,
|
||||
GrantMemberAddedType,
|
||||
),
|
||||
projectID: projectID,
|
||||
UserID: userID,
|
||||
GrantID: grantID,
|
||||
Roles: roles,
|
||||
UserID: userID,
|
||||
GrantID: grantID,
|
||||
Roles: roles,
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,9 +131,8 @@ func GrantMemberChangedEventMapper(event *repository.Event) (eventstore.EventRea
|
||||
type GrantMemberRemovedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
UserID string `json:"userId"`
|
||||
GrantID string `json:"grantId"`
|
||||
projectID string
|
||||
UserID string `json:"userId"`
|
||||
GrantID string `json:"grantId"`
|
||||
}
|
||||
|
||||
func (e *GrantMemberRemovedEvent) Data() interface{} {
|
||||
@ -144,13 +140,12 @@ func (e *GrantMemberRemovedEvent) Data() interface{} {
|
||||
}
|
||||
|
||||
func (e *GrantMemberRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
return []*eventstore.EventUniqueConstraint{NewRemoveProjectGrantMemberUniqueConstraint(e.projectID, e.UserID, e.GrantID)}
|
||||
return []*eventstore.EventUniqueConstraint{NewRemoveProjectGrantMemberUniqueConstraint(e.Aggregate().ID, e.UserID, e.GrantID)}
|
||||
}
|
||||
|
||||
func NewProjectGrantMemberRemovedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
projectID,
|
||||
userID,
|
||||
grantID string,
|
||||
) *GrantMemberRemovedEvent {
|
||||
@ -160,9 +155,8 @@ func NewProjectGrantMemberRemovedEvent(
|
||||
aggregate,
|
||||
GrantMemberRemovedType,
|
||||
),
|
||||
UserID: userID,
|
||||
GrantID: grantID,
|
||||
projectID: projectID,
|
||||
UserID: userID,
|
||||
GrantID: grantID,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,6 @@ type RoleAddedEvent struct {
|
||||
Key string `json:"key,omitempty"`
|
||||
DisplayName string `json:"displayName,omitempty"`
|
||||
Group string `json:"group,omitempty"`
|
||||
projectID string
|
||||
}
|
||||
|
||||
func (e *RoleAddedEvent) Data() interface{} {
|
||||
@ -45,7 +44,7 @@ func (e *RoleAddedEvent) Data() interface{} {
|
||||
}
|
||||
|
||||
func (e *RoleAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
return []*eventstore.EventUniqueConstraint{NewAddProjectRoleUniqueConstraint(e.Key, e.projectID)}
|
||||
return []*eventstore.EventUniqueConstraint{NewAddProjectRoleUniqueConstraint(e.Key, e.Aggregate().ID)}
|
||||
}
|
||||
|
||||
func NewRoleAddedEvent(
|
||||
@ -53,8 +52,7 @@ func NewRoleAddedEvent(
|
||||
aggregate *eventstore.Aggregate,
|
||||
key,
|
||||
displayName,
|
||||
group,
|
||||
projectID string,
|
||||
group string,
|
||||
) *RoleAddedEvent {
|
||||
return &RoleAddedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
@ -65,7 +63,6 @@ func NewRoleAddedEvent(
|
||||
Key: key,
|
||||
DisplayName: displayName,
|
||||
Group: group,
|
||||
projectID: projectID,
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,6 +98,7 @@ func (e *RoleChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstrai
|
||||
func NewRoleChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
key string,
|
||||
changes []RoleChanges,
|
||||
) (*RoleChangedEvent, error) {
|
||||
if len(changes) == 0 {
|
||||
@ -112,6 +110,7 @@ func NewRoleChangedEvent(
|
||||
aggregate,
|
||||
RoleChangedType,
|
||||
),
|
||||
Key: key,
|
||||
}
|
||||
for _, change := range changes {
|
||||
change(changeEvent)
|
||||
@ -154,8 +153,7 @@ func RoleChangedEventMapper(event *repository.Event) (eventstore.EventReader, er
|
||||
type RoleRemovedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
Key string `json:"key,omitempty"`
|
||||
projectID string `json:"-"`
|
||||
Key string `json:"key,omitempty"`
|
||||
}
|
||||
|
||||
func (e *RoleRemovedEvent) Data() interface{} {
|
||||
@ -163,22 +161,20 @@ func (e *RoleRemovedEvent) Data() interface{} {
|
||||
}
|
||||
|
||||
func (e *RoleRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
return []*eventstore.EventUniqueConstraint{NewRemoveProjectRoleUniqueConstraint(e.Key, e.projectID)}
|
||||
return []*eventstore.EventUniqueConstraint{NewRemoveProjectRoleUniqueConstraint(e.Key, e.Aggregate().ID)}
|
||||
}
|
||||
|
||||
func NewRoleRemovedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
key,
|
||||
projectID string) *RoleRemovedEvent {
|
||||
key string) *RoleRemovedEvent {
|
||||
return &RoleRemovedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
RoleRemovedType,
|
||||
),
|
||||
Key: key,
|
||||
projectID: projectID,
|
||||
Key: key,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,3 +12,14 @@ const (
|
||||
type Aggregate struct {
|
||||
eventstore.Aggregate
|
||||
}
|
||||
|
||||
func NewAggregate(id, resourceOwner string) *Aggregate {
|
||||
return &Aggregate{
|
||||
Aggregate: eventstore.Aggregate{
|
||||
Typ: AggregateType,
|
||||
Version: AggregateVersion,
|
||||
ID: id,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -12,3 +12,14 @@ const (
|
||||
type Aggregate struct {
|
||||
eventstore.Aggregate
|
||||
}
|
||||
|
||||
func NewAggregate(id, resourceOwner string) *Aggregate {
|
||||
return &Aggregate{
|
||||
Aggregate: eventstore.Aggregate{
|
||||
Typ: AggregateType,
|
||||
Version: AggregateVersion,
|
||||
ID: id,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -2860,14 +2860,7 @@ message BulkRemoveUserGrantRequest {
|
||||
repeated string grant_id = 1;
|
||||
}
|
||||
|
||||
message BulkRemoveUserGrantResponse {
|
||||
message RemovedUserGrant {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
string grant_id = 2;
|
||||
}
|
||||
|
||||
repeated RemovedUserGrant result = 1;
|
||||
}
|
||||
message BulkRemoveUserGrantResponse {}
|
||||
|
||||
message GetOrgIAMPolicyRequest {}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user