feat: ldap provider login (#5448)

Add the logic to configure and use LDAP provider as an external IDP with a dedicated login GUI.
This commit is contained in:
Stefan Benz
2023-03-24 16:18:56 +01:00
committed by GitHub
parent a8bfcc166e
commit 41ff0bbc63
40 changed files with 2240 additions and 1142 deletions

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
@@ -18,7 +19,6 @@ import (
"github.com/zitadel/zitadel/internal/id"
id_mock "github.com/zitadel/zitadel/internal/id/mock"
"github.com/zitadel/zitadel/internal/repository/idp"
"github.com/zitadel/zitadel/internal/repository/idpconfig"
"github.com/zitadel/zitadel/internal/repository/instance"
)
@@ -3677,24 +3677,6 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) {
},
},
},
{
"invalid host",
fields{
eventstore: eventstoreExpect(t),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
},
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: LDAPProvider{
Name: "name",
},
},
res{
err: func(err error) bool {
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-SDVg2", ""))
},
},
},
{
"invalid baseDN",
fields{
@@ -3705,7 +3687,6 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) {
ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: LDAPProvider{
Name: "name",
Host: "host",
},
},
res{
@@ -3715,7 +3696,7 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) {
},
},
{
"invalid userObjectClass",
"invalid bindDN",
fields{
eventstore: eventstoreExpect(t),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
@@ -3724,7 +3705,6 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) {
ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: LDAPProvider{
Name: "name",
Host: "host",
BaseDN: "baseDN",
},
},
@@ -3735,7 +3715,7 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) {
},
},
{
"invalid userUniqueAttribute",
"invalid bindPassword",
fields{
eventstore: eventstoreExpect(t),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
@@ -3743,10 +3723,9 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) {
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: LDAPProvider{
Name: "name",
Host: "host",
BaseDN: "baseDN",
UserObjectClass: "userObjectClass",
Name: "name",
BindDN: "binddn",
BaseDN: "baseDN",
},
},
res{
@@ -3756,7 +3735,7 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) {
},
},
{
"invalid admin",
"invalid userBase",
fields{
eventstore: eventstoreExpect(t),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
@@ -3764,11 +3743,10 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) {
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: LDAPProvider{
Name: "name",
Host: "host",
BaseDN: "baseDN",
UserObjectClass: "userObjectClass",
UserUniqueAttribute: "userUniqueAttribute",
Name: "name",
BindDN: "binddn",
BaseDN: "baseDN",
BindPassword: "password",
},
},
res{
@@ -3778,7 +3756,7 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) {
},
},
{
"invalid password",
"invalid servers",
fields{
eventstore: eventstoreExpect(t),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
@@ -3786,17 +3764,63 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) {
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: LDAPProvider{
Name: "name",
Host: "host",
BaseDN: "baseDN",
UserObjectClass: "userObjectClass",
UserUniqueAttribute: "userUniqueAttribute",
Admin: "admin",
Name: "name",
BindDN: "binddn",
BaseDN: "baseDN",
BindPassword: "password",
UserBase: "user",
},
},
res{
err: func(err error) bool {
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-sdf5h", ""))
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-SAx905n", ""))
},
},
},
{
"invalid userObjectClasses",
fields{
eventstore: eventstoreExpect(t),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
},
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: LDAPProvider{
Name: "name",
Servers: []string{"server"},
BindDN: "binddn",
BaseDN: "baseDN",
BindPassword: "password",
UserBase: "user",
},
},
res{
err: func(err error) bool {
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-S1x905n", ""))
},
},
},
{
"invalid userFilters",
fields{
eventstore: eventstoreExpect(t),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
},
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: LDAPProvider{
Name: "name",
Servers: []string{"server"},
BindDN: "binddn",
BaseDN: "baseDN",
BindPassword: "password",
UserBase: "user",
UserObjectClasses: []string{"object"},
},
},
res{
err: func(err error) bool {
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-aAx905n", ""))
},
},
},
@@ -3812,24 +3836,24 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) {
instance.NewLDAPIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate,
"id1",
"name",
"host",
"",
[]string{"server"},
false,
"baseDN",
"userObjectClass",
"userUniqueAttribute",
"admin",
"dn",
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
KeyID: "id",
Crypted: []byte("password"),
},
"user",
[]string{"object"},
[]string{"filter"},
time.Second*30,
idp.LDAPAttributes{},
idp.Options{},
)),
},
uniqueConstraintsFromEventConstraintWithInstanceID("instance1", idpconfig.NewAddIDPConfigNameUniqueConstraint("name", "instance1")),
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
@@ -3838,13 +3862,16 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) {
args: args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: LDAPProvider{
Name: "name",
Host: "host",
BaseDN: "baseDN",
UserObjectClass: "userObjectClass",
UserUniqueAttribute: "userUniqueAttribute",
Admin: "admin",
Password: "password",
Name: "name",
Servers: []string{"server"},
StartTLS: false,
BaseDN: "baseDN",
BindDN: "dn",
BindPassword: "password",
UserBase: "user",
UserObjectClasses: []string{"object"},
UserFilters: []string{"filter"},
Timeout: time.Second * 30,
},
},
res: res{
@@ -3864,19 +3891,20 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) {
instance.NewLDAPIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate,
"id1",
"name",
"host",
"port",
true,
[]string{"server"},
false,
"baseDN",
"userObjectClass",
"userUniqueAttribute",
"admin",
"dn",
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
KeyID: "id",
Crypted: []byte("password"),
},
"user",
[]string{"object"},
[]string{"filter"},
time.Second*30,
idp.LDAPAttributes{
IDAttribute: "id",
FirstNameAttribute: "firstName",
@@ -3900,7 +3928,6 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) {
},
)),
},
uniqueConstraintsFromEventConstraintWithInstanceID("instance1", idpconfig.NewAddIDPConfigNameUniqueConstraint("name", "instance1")),
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
@@ -3909,15 +3936,16 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) {
args: args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: LDAPProvider{
Name: "name",
Host: "host",
Port: "port",
TLS: true,
BaseDN: "baseDN",
UserObjectClass: "userObjectClass",
UserUniqueAttribute: "userUniqueAttribute",
Admin: "admin",
Password: "password",
Name: "name",
Servers: []string{"server"},
StartTLS: false,
BaseDN: "baseDN",
BindDN: "dn",
BindPassword: "password",
UserBase: "user",
UserObjectClasses: []string{"object"},
UserFilters: []string{"filter"},
Timeout: time.Second * 30,
LDAPAttributes: idp.LDAPAttributes{
IDAttribute: "id",
FirstNameAttribute: "firstName",
@@ -4020,24 +4048,6 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) {
},
},
},
{
"invalid host",
fields{
eventstore: eventstoreExpect(t),
},
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1",
provider: LDAPProvider{
Name: "name",
},
},
res{
err: func(err error) bool {
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-Dz62d", ""))
},
},
},
{
"invalid baseDN",
fields{
@@ -4048,7 +4058,6 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) {
id: "id1",
provider: LDAPProvider{
Name: "name",
Host: "host",
},
},
res{
@@ -4058,7 +4067,7 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) {
},
},
{
"invalid userObjectClass",
"invalid bindDN",
fields{
eventstore: eventstoreExpect(t),
},
@@ -4067,7 +4076,6 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) {
id: "id1",
provider: LDAPProvider{
Name: "name",
Host: "host",
BaseDN: "baseDN",
},
},
@@ -4078,7 +4086,7 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) {
},
},
{
"invalid userUniqueAttribute",
"invalid userbase",
fields{
eventstore: eventstoreExpect(t),
},
@@ -4086,32 +4094,9 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) {
ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1",
provider: LDAPProvider{
Name: "name",
Host: "host",
BaseDN: "baseDN",
UserObjectClass: "userObjectClass",
},
},
res{
err: func(err error) bool {
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-ASFt6", ""))
},
},
},
{
"invalid admin",
fields{
eventstore: eventstoreExpect(t),
},
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1",
provider: LDAPProvider{
Name: "name",
Host: "host",
BaseDN: "baseDN",
UserObjectClass: "userObjectClass",
UserUniqueAttribute: "userUniqueAttribute",
Name: "name",
BaseDN: "baseDN",
BindDN: "bindDN",
},
},
res{
@@ -4120,6 +4105,72 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) {
},
},
},
{
"invalid servers",
fields{
eventstore: eventstoreExpect(t),
},
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1",
provider: LDAPProvider{
Name: "name",
BaseDN: "baseDN",
BindDN: "bindDN",
UserBase: "user",
},
},
res{
err: func(err error) bool {
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-SAx945n", ""))
},
},
},
{
"invalid userObjectClasses",
fields{
eventstore: eventstoreExpect(t),
},
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1",
provider: LDAPProvider{
Name: "name",
Servers: []string{"server"},
BaseDN: "baseDN",
BindDN: "bindDN",
UserBase: "user",
},
},
res{
err: func(err error) bool {
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-S1x605n", ""))
},
},
},
{
"invalid userFilters",
fields{
eventstore: eventstoreExpect(t),
},
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1",
provider: LDAPProvider{
Name: "name",
Servers: []string{"server"},
BaseDN: "baseDN",
BindDN: "bindDN",
UserBase: "user",
UserObjectClasses: []string{"object"},
},
},
res{
err: func(err error) bool {
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-aAx901n", ""))
},
},
},
{
name: "not found",
fields: fields{
@@ -4131,16 +4182,20 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) {
ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1",
provider: LDAPProvider{
Name: "name",
Host: "host",
BaseDN: "baseDN",
UserObjectClass: "userObjectClass",
UserUniqueAttribute: "userUniqueAttribute",
Admin: "admin",
Name: "name",
Servers: []string{"server"},
BaseDN: "baseDN",
BindDN: "binddn",
BindPassword: "password",
UserBase: "user",
UserObjectClasses: []string{"object"},
UserFilters: []string{"filter"},
},
},
res: res{
err: caos_errors.IsNotFound,
err: func(err error) bool {
return errors.Is(err, caos_errors.ThrowNotFound(nil, "INST-ASF3F", ""))
},
},
},
{
@@ -4152,19 +4207,20 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) {
instance.NewLDAPIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate,
"id1",
"name",
"host",
"",
[]string{"server"},
false,
"baseDN",
"userObjectClass",
"userUniqueAttribute",
"admin",
"basedn",
"binddn",
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
KeyID: "id",
Crypted: []byte("password"),
},
"user",
[]string{"object"},
[]string{"filter"},
time.Second*30,
idp.LDAPAttributes{},
idp.Options{},
)),
@@ -4175,12 +4231,15 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) {
ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1",
provider: LDAPProvider{
Name: "name",
Host: "host",
BaseDN: "baseDN",
UserObjectClass: "userObjectClass",
UserUniqueAttribute: "userUniqueAttribute",
Admin: "admin",
Name: "name",
Servers: []string{"server"},
StartTLS: false,
BaseDN: "basedn",
BindDN: "binddn",
UserBase: "user",
UserObjectClasses: []string{"object"},
UserFilters: []string{"filter"},
Timeout: time.Second * 30,
},
},
res: res{
@@ -4196,19 +4255,20 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) {
instance.NewLDAPIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate,
"id1",
"name",
"host",
"port",
[]string{"server"},
false,
"baseDN",
"userObjectClass",
"userUniqueAttribute",
"admin",
"basedn",
"binddn",
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
KeyID: "id",
Crypted: []byte("password"),
},
"user",
[]string{"object"},
[]string{"filter"},
time.Second*30,
idp.LDAPAttributes{},
idp.Options{},
)),
@@ -4221,22 +4281,22 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) {
t := true
event, _ := instance.NewLDAPIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate,
"id1",
"name",
[]idp.LDAPIDPChanges{
idp.ChangeLDAPName("new name"),
idp.ChangeLDAPHost("new host"),
idp.ChangeLDAPPort("new port"),
idp.ChangeLDAPTLS(true),
idp.ChangeLDAPBaseDN("new baseDN"),
idp.ChangeLDAPUserObjectClass("new userObjectClass"),
idp.ChangeLDAPUserUniqueAttribute("new userUniqueAttribute"),
idp.ChangeLDAPAdmin("new admin"),
idp.ChangeLDAPPassword(&crypto.CryptoValue{
idp.ChangeLDAPServers([]string{"new server"}),
idp.ChangeLDAPStartTLS(true),
idp.ChangeLDAPBaseDN("new basedn"),
idp.ChangeLDAPBindDN("new binddn"),
idp.ChangeLDAPBindPassword(&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
KeyID: "id",
Crypted: []byte("new password"),
}),
idp.ChangeLDAPUserBase("new user"),
idp.ChangeLDAPUserObjectClasses([]string{"new object"}),
idp.ChangeLDAPUserFilters([]string{"new filter"}),
idp.ChangeLDAPTimeout(time.Second * 20),
idp.ChangeLDAPAttributes(idp.LDAPAttributeChanges{
IDAttribute: stringPointer("new id"),
FirstNameAttribute: stringPointer("new firstName"),
@@ -4264,8 +4324,6 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) {
}(),
),
},
uniqueConstraintsFromEventConstraintWithInstanceID("instance1", idpconfig.NewRemoveIDPConfigNameUniqueConstraint("name", "instance1")),
uniqueConstraintsFromEventConstraintWithInstanceID("instance1", idpconfig.NewAddIDPConfigNameUniqueConstraint("new name", "instance1")),
),
),
secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
@@ -4274,15 +4332,16 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) {
ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1",
provider: LDAPProvider{
Name: "new name",
Host: "new host",
Port: "new port",
TLS: true,
BaseDN: "new baseDN",
UserObjectClass: "new userObjectClass",
UserUniqueAttribute: "new userUniqueAttribute",
Admin: "new admin",
Password: "new password",
Name: "new name",
Servers: []string{"new server"},
StartTLS: true,
BaseDN: "new basedn",
BindDN: "new binddn",
BindPassword: "new password",
UserBase: "new user",
UserObjectClasses: []string{"new object"},
UserFilters: []string{"new filter"},
Timeout: time.Second * 20,
LDAPAttributes: idp.LDAPAttributes{
IDAttribute: "new id",
FirstNameAttribute: "new firstName",