aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/bottin_test.go160
-rw-r--r--test/config.json.test13
-rw-r--r--test/create.go281
-rw-r--r--test/functionTest.go173
-rw-r--r--test/go.mod8
-rw-r--r--test/go.sum26
-rw-r--r--test/handler.go141
-rw-r--r--test/request.go59
-rwxr-xr-xtest/runner.sh16
9 files changed, 877 insertions, 0 deletions
diff --git a/test/bottin_test.go b/test/bottin_test.go
new file mode 100644
index 0000000..f4ed7fe
--- /dev/null
+++ b/test/bottin_test.go
@@ -0,0 +1,160 @@
+package main
+
+import (
+ "testing"
+)
+
+func TestAddThenDelete(t *testing.T) {
+ t.Parallel()
+ //SetUp - Create Users and Groups
+ inst, err := Init()
+ if err != nil {
+ t.Error(err)
+ }
+
+ //TearDown - Delete all the users and groups created
+ err = inst.Clean()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestConfirmAddAttributes(t *testing.T) {
+ t.Parallel()
+ //SetUp - Create Users and Groups
+ inst, err := Init()
+ if err != nil {
+ t.Error(err)
+ }
+
+ //Test search_attribute to confirm the Add
+ if ok, err := inst.CompareOurDataWithConsul(); !ok {
+ t.Error(err)
+ }
+ //TearDown - Delete all the users and groups created
+ err = inst.Clean()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+//Modifyrequest Test
+func TestModifyRequest(t *testing.T) {
+ t.Parallel()
+ //SetUp - Create Users and Groups
+ inst, err := Init()
+ if err != nil {
+ t.Error(err)
+ }
+
+ //Test modify all data (groups and users)
+ err = inst.ModifyRandomAllData()
+ if err != nil {
+ t.Error(err)
+ }
+
+ //TearDown - Delete all the users and groups created
+ err = inst.Clean()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestModifyRequestAndCheck(t *testing.T) {
+ t.Parallel()
+ //SetUp - Create Users and Groups
+ inst, err := Init()
+ if err != nil {
+ t.Error(err)
+ }
+
+ //Test modify all data (groups and users)
+ err = inst.ModifyRandomAllData()
+ if err != nil {
+ t.Error(err)
+ }
+
+ //Check if the data was modify on Consul
+ if ok, err := inst.CompareOurDataWithConsul(); !ok {
+ t.Error(err)
+ }
+
+ //TearDown - Delete all the users and groups created
+ err = inst.Clean()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestAddUserInGroup(t *testing.T) {
+ t.Parallel()
+ //SetUp - Create Users and Groups
+ inst, err := Init()
+ if err != nil {
+ t.Error(err)
+ }
+
+ //Add users in group
+ err = inst.AddAllUsersInGroup()
+ if err != nil {
+ t.Error(err)
+ }
+
+ //TearDown - Delete all the users and groups created
+ err = inst.Clean()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestDeleteGroupsAfterAddedUsers(t *testing.T) {
+ t.Parallel()
+ //SetUp - Create Users and Groups
+ inst, err := Init()
+ if err != nil {
+ t.Error(err)
+ }
+
+ //Add users in group
+ err = inst.AddAllUsersInGroup()
+ if err != nil {
+ t.Error(err)
+ }
+
+ //Delete the half groups
+ number := len(inst.dataGroups) / 2
+ err = inst.clean(inst.dataGroups[0:number])
+ if err != nil {
+ t.Error(err)
+ }
+ inst.dataGroups = inst.dataGroups[number:len(inst.dataGroups)]
+
+ //Check all the groups in memberOf exist
+ ok, err := inst.CheckMemberOf()
+ if err != nil {
+ t.Error(err)
+ }
+ if !ok {
+ t.Errorf("Found group in memberOf that isn't in Consul.")
+ }
+
+ //TearDown - Delete all the users and groups created
+ err = inst.Clean()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+//Example of paralellism Test
+func TestPrincipal(t *testing.T) {
+
+ t.Run("A=Add and delete", TestAddThenDelete)
+ t.Run("A=Modify", TestModifyRequest)
+ if !testing.Short() {
+ t.Run("B=Add attributes", TestConfirmAddAttributes)
+ t.Run("B=Modify and check", TestModifyRequestAndCheck)
+ t.Run("C=Add user in group", TestAddUserInGroup)
+ t.Run("C=Delete group", TestDeleteGroupsAfterAddedUsers)
+ }
+
+}
diff --git a/test/config.json.test b/test/config.json.test
new file mode 100644
index 0000000..bc1eeec
--- /dev/null
+++ b/test/config.json.test
@@ -0,0 +1,13 @@
+{
+ "suffix": "dc=deuxfleurs,dc=fr",
+ "bind": "127.0.0.1:1389",
+ "acl": [
+ "ANONYMOUS::bind:*,ou=users,dc=deuxfleurs,dc=fr:",
+ "ANONYMOUS::bind:cn=admin,dc=deuxfleurs,dc=fr:",
+ "*,dc=deuxfleurs,dc=fr::read:*:* !userpassword",
+ "*::read modify:SELF:*",
+ "cn=admin,dc=deuxfleurs,dc=fr::read add modify delete:*:*",
+ "*:cn=admin,ou=groups,dc=deuxfleurs,dc=fr:read add modify delete:*:*"
+ ]
+}
+
diff --git a/test/create.go b/test/create.go
new file mode 100644
index 0000000..4ad8106
--- /dev/null
+++ b/test/create.go
@@ -0,0 +1,281 @@
+package main
+
+import (
+ "fmt"
+ "strings"
+ "sync"
+
+ "github.com/go-ldap/ldap/v3"
+ "github.com/sirupsen/logrus"
+)
+
+//Mux value, this value permits do not have two identicals values in the parallel instances
+type StoreAllCN struct {
+ mu sync.Mutex
+ cn map[string]struct{}
+}
+
+var allNames = StoreAllCN{cn: make(map[string]struct{})}
+
+//Type used for the tests
+type attributes struct {
+ Name string
+ Data []string
+}
+
+type data_DN struct {
+ DN string
+ Attributes []attributes
+}
+
+type instance struct {
+ numberUsers, numberGroups int
+ dataGroups, dataUsers []data_DN
+ logging *ldap.Conn
+}
+
+//Create a new object instance
+//With this instance, we can obtain an isolated container where
+//we have our users and groups. It allows to run tests in parallel.
+func NewInstance(numberUsers, numberGroups int) (*instance, error) {
+ l, err := Connect()
+ if err != nil {
+ return nil, err
+ }
+
+ logging.Level = logrus.InfoLevel
+
+ inst := instance{
+ numberUsers: numberUsers,
+ numberGroups: numberGroups,
+ dataGroups: []data_DN{},
+ dataUsers: []data_DN{},
+ logging: l,
+ }
+
+ err = inst.createOrganizationnalUnit()
+ if ldap.IsErrorWithCode(err, uint16(68)) {
+ logging.Warn("OrganizationnalUnit already created")
+ err = nil
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ err = inst.CreateGroups()
+ if err != nil {
+ return nil, err
+ }
+ err = inst.CreateUsers()
+ if err != nil {
+ return nil, err
+ }
+
+ return &inst, nil
+}
+
+//Part: Created users or groups or OU
+
+func (inst *instance) createOrganizationnalUnit() error {
+ dn := []string{"ou=groups,dc=deuxfleurs,dc=fr", "ou=users,dc=deuxfleurs,dc=fr"}
+ attributes := []map[string][]string{{
+ "description": []string{"OrganizationalUnit qui regroupe tous les groupes"},
+ "objectclass": []string{"organizationalUnit", "top"},
+ "ou": []string{"groups"},
+ "structuralobjectclass": []string{"organizationalUnit"},
+ },
+ {
+ "description": []string{"OrganizationalUnit qui regroupe tous les users"},
+ "objectclass": []string{"organizationalUnit", "top"},
+ "ou": []string{"users"},
+ "structuralobjectclass": []string{"organizationalUnit"},
+ },
+ }
+
+ for index := range dn {
+ err := inst.Add_Request(dn[index], attributes[index])
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+
+}
+
+//Part: Create User or group
+
+func (inst *instance) CreateUsers() (err error) {
+
+ dn := "cn=%s,ou=users,dc=deuxfleurs,dc=fr"
+ attributes := map[string][]string{
+ "displayname": {},
+ "objectclass": {"inetOrgPerson", "organizationalPerson", "person", "top"},
+ "structuralobjectclass": {"inetOrgPerson"},
+ }
+
+ du, err := inst.create(dn, []string{"displayname"}, inst.numberUsers, attributes, inst.dataUsers)
+ if err == nil {
+ inst.dataUsers = du
+ }
+ return err
+}
+
+func (inst *instance) CreateGroups() error {
+ dn := "cn=%s,ou=groups,dc=deuxfleurs,dc=fr"
+ attributes := map[string][]string{
+ "description": {},
+ "objectclass": {"groupOfNames", "top"},
+ "structuralobjectclass": {"groupOfNames"},
+ }
+
+ dg, err := inst.create(dn, []string{"description"}, inst.numberGroups, attributes, inst.dataGroups)
+ if err == nil {
+ inst.dataGroups = dg
+ }
+
+ return err
+}
+
+//Hard Function: She does:
+//- generate an unique name
+//- store the Data of each AddRequest in instance struct
+//- send AddRequest to Bottin
+func (inst *instance) create(dn string, unique_attr []string, number int, attributes map[string][]string, data []data_DN) ([]data_DN, error) {
+ for i := 0; i < number; i++ {
+ name := inst.GenerateName()
+
+ datDn := data_DN{DN: fmt.Sprintf(dn, name)}
+
+ for _, value := range unique_attr {
+ attributes[value] = []string{name}
+ }
+
+ datDn.Attributes = MapAttToStruct(attributes)
+ data = append(data, datDn)
+
+ err := inst.Add_Request(fmt.Sprintf(dn, name), attributes)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return data, nil
+}
+
+//Part: clean
+
+func (inst *instance) Clean() error {
+ err := inst.CleanGroups()
+ if err != nil {
+ return err
+ }
+ err = inst.CleanUsers()
+ return err
+}
+
+func (inst *instance) CleanUsers() error {
+ err := inst.clean(inst.dataUsers)
+ if err != nil {
+ return err
+ }
+ inst.dataUsers = []data_DN{}
+ return err
+}
+
+func (inst *instance) CleanGroups() error {
+ err := inst.clean(inst.dataGroups)
+ if err != nil {
+ return err
+ }
+ inst.dataGroups = []data_DN{}
+ return err
+}
+
+func (inst *instance) clean(stock []data_DN) error {
+ logging.Debugf("Delete %d elements.", len(stock))
+ for _, value := range stock {
+ err := inst.Delete_Request(value.DN)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+//Part: Verify if a data_Dn is a group or an user
+func (inst *instance) VerifyUser(user data_DN) (bool, error) {
+ dn := "ou=users,dc=deuxfleurs,dc=fr"
+ cn := strings.Split(user.DN, ",")[0]
+ filter := fmt.Sprintf("(%s)", cn)
+
+ res, err := inst.Search_Request(dn, filter, []string{"cn"})
+
+ return len(res.Entries) == 1, err
+}
+
+func (inst *instance) VerifyGroup(group data_DN) (bool, error) {
+ dn := "ou=groups,dc=deuxfleurs,dc=fr"
+ cn := strings.Split(group.DN, ",")[0]
+ filter := fmt.Sprintf("(%s)", cn)
+
+ res, err := inst.Search_Request(dn, filter, []string{"cn"})
+
+ return len(res.Entries) == 1, err
+}
+
+//Part: Add user in a group
+func (inst *instance) AddUserInGroup(user, group data_DN) error {
+
+ err := inst.Modify_Request(group.DN, nil, nil, map[string][]string{
+ "member": {user.DN},
+ })
+ return err
+}
+
+func (inst *instance) AddUserSliceInGroup(users_cn []string, group_dn string) error {
+
+ err := inst.Modify_Request(group_dn, nil, nil, map[string][]string{
+ "member": users_cn,
+ })
+ return err
+}
+
+//Part: modify, add, delete data_DN struct
+
+func AddAtt(name string, data []string, dat data_DN) data_DN {
+ dat.Attributes = append(dat.Attributes, attributes{
+ Name: name,
+ Data: data,
+ })
+
+ logging.Debug(fmt.Sprintf("Attributes %s add from %s.", name, dat.DN))
+ return dat
+}
+
+func DelAtt(name string, dat data_DN) data_DN {
+ for index, value := range dat.Attributes {
+ if value.Name == name {
+ dat.Attributes[index] = dat.Attributes[len(dat.Attributes)-1]
+ //tmp := dat.Attributes[:len(dat.Attributes)-1]
+ dat.Attributes = []attributes{}
+ logging.Debugf("Attributes %s delete from %s.", name, dat.DN)
+ return dat
+ }
+ }
+ logging.Debugf("Can't delete attribute %s from %s.", name, dat.DN)
+ return dat
+}
+
+func ReplaceAtt(name string, data []string, dat data_DN) data_DN {
+ for index, value := range dat.Attributes {
+ if value.Name == name {
+ dat.Attributes[index] = attributes{
+ Name: name,
+ Data: data,
+ }
+ logging.Debugf("Replace attributes %s from %s succesful..", name, dat.DN)
+ return dat
+ }
+ }
+ logging.Debugf("Can't replace attributes %s from %s.", name, dat.DN)
+ return dat
+}
diff --git a/test/functionTest.go b/test/functionTest.go
new file mode 100644
index 0000000..e8d95ab
--- /dev/null
+++ b/test/functionTest.go
@@ -0,0 +1,173 @@
+package main
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/go-ldap/ldap/v3"
+)
+
+const default_users, default_groups = 1000, 1000
+
+func Init() (*instance, error) {
+ inst, err := NewInstance(default_users, default_groups)
+ return inst, err
+}
+
+//Part to compare our datas
+func (inst *instance) CompareOurDataWithConsul() (bool, error) {
+ if ok, err := inst.VerifyOurData(inst.dataUsers); !ok {
+ return false, err
+ }
+ if ok, err := inst.VerifyOurData(inst.dataGroups); !ok {
+ return false, err
+ }
+ return true, nil
+}
+
+func (inst *instance) VerifyOurData(tabData []data_DN) (bool, error) {
+ for _, value := range tabData {
+ names := getNamesAtt(value)
+ cn := strings.Split(value.DN, ",")[0]
+ res, err := inst.Search_Request(value.DN, fmt.Sprintf("(&(%s))", cn), names)
+ if err != nil {
+ return false, err
+ }
+ if len(res.Entries) != 1 {
+ return false, fmt.Errorf("expected 1 entry, but found %d entry/ies", len(res.Entries))
+ }
+ if !Compare(value, res.Entries[0]) {
+ return false, fmt.Errorf("no match with the DN: %s", value.DN)
+ }
+ }
+ return true, nil
+}
+
+func Compare(dat data_DN, ent *ldap.Entry) bool {
+ for _, value := range dat.Attributes {
+ logging.Debugf("Attributes from %s is now: %s.", dat.DN, dat.Attributes)
+ entVal := GetAttributeValuesBottin(ent, value.Name)
+ logging.Debugf("Values of the Entry: attributName: %s, Values: %s.", value.Name, entVal)
+ if !CompareSliceString(entVal, value.Data) {
+ logging.Debugf("Values expected: %s, values found: %s.", value.Data, entVal)
+ return false
+ }
+ }
+ return true
+}
+
+//Part modify datas
+func (inst *instance) ModifyRandomAllData() error {
+ dg, err := inst.ModifyRandom(inst.dataGroups, []string{"description"})
+ if err != nil {
+ return err
+ } else {
+ inst.dataGroups = dg
+ }
+
+ dg, err = inst.ModifyRandom(inst.dataUsers, []string{"displayname"})
+ if err != nil {
+ return err
+ } else {
+ inst.dataUsers = dg
+ }
+ return nil
+}
+
+//Function which modify random way the attributes in attName of a data_DN's slice, it can delete, replace and delete
+//The function modify also in the dat object
+func (inst *instance) ModifyRandom(dat []data_DN, attName []string) ([]data_DN, error) {
+ for index, value := range dat {
+ del := make(map[string][]string)
+ add := make(map[string][]string)
+ replace := make(map[string][]string)
+
+ for _, att := range attName {
+
+ switch selNumber := R.Intn(3); selNumber {
+ case 0:
+ del[att] = []string{}
+ value = DelAtt(att, value)
+ logging.Debug(fmt.Sprintf("Delete the attribute %s of the DN %s.", att, value.DN))
+ case 1:
+ name := inst.GenerateName()
+ value = AddAtt(name, []string{name}, value)
+ add[name] = []string{name}
+ logging.Debug(fmt.Sprintf("Add the attribute %s with value %s of the DN %s.", name, name, value.DN))
+ case 2:
+ name := inst.GenerateName()
+ value = ReplaceAtt(att, []string{name}, value)
+ replace[att] = []string{name}
+ logging.Debug(fmt.Sprintf("Replace the attribute %s with value %s of the DN %s.", att, name, value.DN))
+ }
+
+ }
+
+ err := inst.Modify_Request(value.DN, add, del, replace)
+ if err != nil {
+ return dat, err
+ }
+ dat[index] = value
+ }
+ return dat, nil
+}
+
+//Add all users in a random group
+func (inst *instance) AddAllUsersInGroup() error {
+ for _, value := range inst.dataGroups {
+ valueRand := (len(inst.dataUsers) + 1) / 30
+ if valueRand == 0 {
+ valueRand = 1
+ }
+ numberOfMembers := R.Intn(valueRand) + 1
+ logging.Debugf("%s will be have %d members.", value.DN, numberOfMembers)
+
+ groupMemory := make(map[int]struct{})
+ users_cn := []string{}
+
+ for i := 0; i < numberOfMembers; i++ {
+ selectGroup := R.Intn(len(inst.dataUsers))
+ for _, ok := groupMemory[selectGroup]; ok; _, ok = groupMemory[selectGroup] {
+ selectGroup = R.Intn(len(inst.dataUsers))
+
+ logging.Debugf("Search an other member. The value is %d , and we have %d members available.", selectGroup, len(inst.dataUsers))
+ }
+ groupMemory[selectGroup] = struct{}{}
+
+ users_cn = append(users_cn, inst.dataGroups[selectGroup].DN)
+
+ }
+ err := inst.AddUserSliceInGroup(users_cn, value.DN)
+ if err != nil {
+ return err
+ }
+
+ }
+ return nil
+}
+
+//Check if the groups in memberOf exist in Consul
+func (inst *instance) CheckMemberOf() (bool, error) {
+ for _, value := range inst.dataUsers {
+ cn := strings.Split(value.DN, ",")[0]
+ res, err := inst.Search_Request(value.DN, fmt.Sprintf("(&(%s))", cn), []string{"memberOf"})
+ if err != nil {
+ return false, err
+ }
+ if len(res.Entries) != 1 {
+ return false, fmt.Errorf("expected 1 entry, but found %d entry/ies", len(res.Entries))
+ }
+ attValues := GetAttributeValuesBottin(res.Entries[0], "memberOf")
+ for _, dnGroup := range attValues {
+ logging.Debugf("Verify if the group %s exist...", dnGroup)
+ ok, err := inst.VerifyGroup(data_DN{DN: dnGroup})
+ if err != nil {
+ return false, err
+ }
+ if !ok {
+ return false, fmt.Errorf("don't found the group: %s", dnGroup)
+ }
+ }
+ }
+ return true, nil
+}
diff --git a/test/go.mod b/test/go.mod
new file mode 100644
index 0000000..74ed1ce
--- /dev/null
+++ b/test/go.mod
@@ -0,0 +1,8 @@
+module bottin/integration
+
+go 1.14
+
+require (
+ github.com/go-ldap/ldap/v3 v3.3.0
+ github.com/sirupsen/logrus v1.4.2
+)
diff --git a/test/go.sum b/test/go.sum
new file mode 100644
index 0000000..9c882d3
--- /dev/null
+++ b/test/go.sum
@@ -0,0 +1,26 @@
+github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
+github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
+github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
+github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ=
+github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
+golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/test/handler.go b/test/handler.go
new file mode 100644
index 0000000..fdee34f
--- /dev/null
+++ b/test/handler.go
@@ -0,0 +1,141 @@
+package main
+
+import (
+ "fmt"
+ "math/rand"
+ "os"
+
+ ldap "github.com/go-ldap/ldap/v3"
+ "github.com/sirupsen/logrus"
+)
+
+const maxlength_generateName, minlength_generateName = 25, 3
+
+const bindusername = "cn=admin,dc=deuxfleurs,dc=fr"
+const adresse = "127.0.0.1"
+const port = 1389
+
+var logging = logrus.New()
+
+const seed = 654258
+
+var R = rand.New(rand.NewSource(seed))
+
+var bindpassword = "sf7yO52NCuE"
+
+//Handler just to facilite the print error
+func PrintError(LDAPError error) {
+ if LDAPError != nil {
+ logging.Fatal(LDAPError)
+ }
+}
+
+//Generate an unique name, which store in all_names
+func (inst *instance) GenerateName() (name string) {
+ alphabet := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
+ "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y",
+ "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "é", "è", "ê", "ë", "à", "@", "â", "ä", "û", "ü", "ù", "$", "£", "%", "ø", "€"}
+ length := R.Intn(maxlength_generateName) + minlength_generateName
+
+ //Check if this name not exist already
+ //Lock thhis variable because she is hared with other goroutine
+ allNames.mu.Lock()
+ for only_one := true; only_one; _, only_one = allNames.cn[name] {
+ //Create the name
+ for i := 0; i < length; i++ {
+ name += alphabet[R.Intn(len(alphabet))]
+ }
+
+ }
+ //Add the new name in the map to store this one
+ allNames.cn[name] = struct{}{}
+ allNames.mu.Unlock()
+ logging.Debug(fmt.Sprintf("Name generated: %s.", name))
+ return
+}
+
+//Handler to around the bug with MessageId
+func (inst *instance) Reconnect() (err error) {
+ inst.logging.Close()
+ inst.logging, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", adresse, port))
+ if err != nil {
+ return
+ }
+ err = inst.logging.Bind(bindusername, bindpassword)
+ //logging.Debug("Reconnect succesful")
+ return
+}
+
+//Transform attributes in map format to the struct attributes
+func MapAttToStruct(att map[string][]string) []attributes {
+ resultat := []attributes{}
+ for key, value := range att {
+ logging.Debug(fmt.Sprintf("Transform: key: %s, values: %s to attributes struct.\n", key, value))
+ resultat = append(resultat, attributes{
+ Name: key,
+ Data: value,
+ })
+ }
+ return resultat
+}
+
+func Connect() (*ldap.Conn, error) {
+
+ l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", adresse, port))
+ if err != nil {
+ return nil, err
+ }
+
+ if key, ok := os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW"); ok {
+ bindpassword = key
+ }
+ //l.Debug.Enable(true)
+ err = l.Bind(bindusername, bindpassword)
+ logging.Debug("Connection succesful")
+ return l, err
+}
+
+//Handler to get only attributes names
+func getNamesAtt(dat data_DN) []string {
+ resultat := []string{}
+ for _, values := range dat.Attributes {
+ resultat = append(resultat, values.Name)
+ }
+ return resultat
+}
+
+//Handler to compare slice string
+func CompareSliceString(string1, string2 []string) bool {
+ if len(string1) != len(string2) {
+ return false
+ } else {
+ for index := range string1 {
+ if string1[index] != string2[index] {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+//Handler to remove an element in slice string
+func DeleteElementSliceString(s string, sSlice []string) []string {
+
+ for index, value := range sSlice {
+ if value == s {
+ sSlice[index] = sSlice[len(sSlice)-1]
+ return sSlice[:len(sSlice)-1]
+ }
+ }
+ return sSlice
+}
+
+//Get attributes entry values bottin bug
+func GetAttributeValuesBottin(ent *ldap.Entry, name string) (res []string) {
+ for _, val := range ent.Attributes {
+ if val.Name == name {
+ res = append(res, val.Values...)
+ }
+ }
+ return
+}
diff --git a/test/request.go b/test/request.go
new file mode 100644
index 0000000..adde22e
--- /dev/null
+++ b/test/request.go
@@ -0,0 +1,59 @@
+package main
+
+import (
+ ldap "github.com/go-ldap/ldap/v3"
+)
+
+func (inst *instance) Add_Request(dn string, attributes map[string][]string) error {
+ //Create the AddRequest
+ req := ldap.NewAddRequest(dn, nil)
+ for key, value := range attributes {
+ req.Attribute(key, value)
+ }
+
+ //Send the request
+ err := inst.logging.Add(req)
+ return err
+
+}
+
+//Use enum to select Replace,Delete,Modify
+func (inst *instance) Modify_Request(dn string, add_attributes, delete_attributes, replace_attributes map[string][]string) error {
+ modifyReq := ldap.NewModifyRequest(dn, nil)
+
+ for key, value := range add_attributes {
+ modifyReq.Add(key, value)
+ }
+
+ for key, value := range delete_attributes {
+ modifyReq.Delete(key, value)
+ }
+
+ for key, value := range replace_attributes {
+ modifyReq.Replace(key, value)
+ }
+
+ err := inst.logging.Modify(modifyReq)
+ return err
+}
+
+func (inst *instance) Delete_Request(dn string) error {
+ del := ldap.NewDelRequest(dn, nil)
+
+ err := inst.logging.Del(del)
+ return err
+}
+
+func (inst *instance) Search_Request(dn, filter string, name_attributes []string) (*ldap.SearchResult, error) {
+ searchReq := ldap.NewSearchRequest(
+ dn,
+ ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
+ filter,
+ name_attributes,
+ nil,
+ )
+
+ res, err := inst.logging.Search(searchReq)
+ logging.Debugf("Search Request made with: dn: %s, filter: %s, attributes: %s. \n", dn, filter, name_attributes)
+ return res, err
+}
diff --git a/test/runner.sh b/test/runner.sh
new file mode 100755
index 0000000..04e42be
--- /dev/null
+++ b/test/runner.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+set -ex
+
+echo $BOTTIN_DEFAULT_ADMIN_PW
+consul agent -dev > /dev/null 2>&1 &
+sleep 2
+cp test/config.json.test config.json
+./bottin > /tmp/bottin.log 2>&1 &
+sleep 1
+./test/test -test.v -test.failfast -test.short -test.run TestPrincipal
+./test/test -test.v -test.failfast -test.run TestPrincipal/B=
+
+jobs
+kill %2
+kill %1