Test End-to-end V1.0, testing Bottin's behavior

Tests wrote in golang without framework Testing on the Bottin's behavior

Tests made:
- crated random Users and Group - LDAP ADD
- check the match between Consul's data and Test's data- LDAP
  Search
- modify attributes and check them -
  LDAP Modify
This commit is contained in:
MrArmonius 2021-07-07 01:49:33 +02:00
parent aa912b5ceb
commit a98556d5c1
12 changed files with 558 additions and 12 deletions

View file

@ -1,13 +1,24 @@
--- ---
pipeline: kind: pipeline
build: name: bottin
image: golang:stretch
commands: steps:
- go get -d -v - name: build
- go build -v image: golang:stretch
commands:
- go get -d -v
- go build -v
- cd test_automatic
- go get -d -v
- go build -v
- name: test_bottin
image: consul:latest
commands:
- ./test_automatic/start_test.sh
--- ---
kind: signature kind: signature
hmac: 8f49fdf0e4abb0790827eed7cac8eedd5e11705d1fa01954a84929933eb7b254 hmac: 939fca00ff84d40e9364cd936c18c40c5becafa05e0f887bc04cf6336a4913a2
... ...

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
bottin bottin
bottin.static bottin.static
config.json config.json
test_automatic/integration

1
go.mod
View file

@ -3,6 +3,7 @@ module bottin
go 1.13 go 1.13
require ( require (
github.com/go-ldap/ldap/v3 v3.3.0
github.com/google/uuid v1.1.1 github.com/google/uuid v1.1.1
github.com/hashicorp/consul/api v1.3.0 github.com/hashicorp/consul/api v1.3.0
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3

14
go.sum
View file

@ -1,3 +1,5 @@
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/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@ -7,6 +9,11 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
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 v2.5.1+incompatible h1:Opaoft5zMW8IU/VRULB0eGMBQ9P5buRvCW6sFTRmMn8=
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/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
@ -79,12 +86,19 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3 h1:KYQXGkl6vs02hK7pK4eIbw0NpNPedieTSTEiJ//bwGs= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3 h1:KYQXGkl6vs02hK7pK4eIbw0NpNPedieTSTEiJ//bwGs=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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=

17
main.go
View file

@ -320,12 +320,19 @@ func (server *Server) init() error {
return err return err
} }
admin_pass := make([]byte, 8)
_, err = rand.Read(admin_pass) admin_pass_str, environnement_variable_exist := os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW")
if err != nil { if !environnement_variable_exist {
return err admin_pass := make([]byte, 8)
_, err = rand.Read(admin_pass)
if err != nil {
return err
}
admin_pass_str = base64.RawURLEncoding.EncodeToString(admin_pass)
} else {
server.logger.Printf("It seems that exists a password in environnement variable")
} }
admin_pass_str := base64.RawURLEncoding.EncodeToString(admin_pass)
admin_pass_hash := SSHAEncode([]byte(admin_pass_str)) admin_pass_hash := SSHAEncode([]byte(admin_pass_str))
admin_dn := "cn=admin," + server.config.Suffix admin_dn := "cn=admin," + server.config.Suffix

Binary file not shown.

Binary file not shown.

8
test_automatic/go.mod Normal file
View file

@ -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
)

23
test_automatic/go.sum Normal file
View file

@ -0,0 +1,23 @@
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/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/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/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=

View file

@ -0,0 +1,430 @@
package main
import (
"github.com/go-ldap/ldap/v3"
"fmt"
log "github.com/sirupsen/logrus"
"math/rand"
"strings"
"errors"
"os"
)
const bindusername = "cn=admin,dc=deuxfleurs,dc=fr"
const adresse = "127.0.0.1"
const port = 1389
var bindpassword string
var all_names = make(map[string]struct{})
func printError(LDAPError error) {
if LDAPError != nil {
log.Fatal(LDAPError)
}
}
func createOU(l *ldap.Conn) error {
req := ldap.NewAddRequest("ou=groups,dc=deuxfleurs,dc=fr",nil)
req.Attribute("description",[]string{"OrganizationalUnit qui regroupe tous les groupes"})
req.Attribute("objectclass",[]string{"organizationalUnit", "top"})
req.Attribute("ou",[]string{"groups"})
req.Attribute("structuralobjectclass", []string{"organizationalUnit"})
err := l.Add(req)
if err != nil {
return err
}
req = ldap.NewAddRequest("ou=users,dc=deuxfleurs,dc=fr",nil)
req.Attribute("description",[]string{"OrganizationalUnit qui regroupe tous les utilisateurs"})
req.Attribute("objectclass",[]string{"organizationalUnit", "top"})
req.Attribute("ou",[]string{"users"})
req.Attribute("structuralobjectclass", []string{"organizationalUnit"})
err = l.Add(req)
return err
}
func generateName(r *rand.Rand) (name string) {
for only_one := true; only_one; _, only_one = all_names[name]{
name = fmt.Sprintf("%d",r.Int())
}
all_names[name] = struct{}{}
log.Debug(fmt.Sprintf("Name generated: %s.\n", name))
return
}
func createGroup(r *rand.Rand, l *ldap.Conn) (tab_AddRequest []ldap.AddRequest, err error) {
StructuralObjectClass := []string{"groupOfNames"}
ObjectClass := []string{"groupOfNames","top"}
for i := 0; i<20; i++ {
//Generate name and check if he is unique
name := generateName(r)
req := ldap.NewAddRequest(fmt.Sprintf("cn=%s,ou=groups,dc=deuxfleurs,dc=fr",name),nil)
req.Attribute("description",[]string{generateName(r)})
req.Attribute("objectclass",ObjectClass)
req.Attribute("structuralobjectclass",StructuralObjectClass)
err = l.Add(req)
if err != nil {
log.Warn(fmt.Sprintf("Erreur survenue sur la création du [%d] groupe.\n",i))
return nil, err
}
tab_AddRequest = append(tab_AddRequest, *req)
}
return
}
func createUser(r *rand.Rand, l *ldap.Conn) (tab_AddRequest []ldap.AddRequest, err error) {
StructuralObjectClass := []string{"inetOrgPerson"}
ObjectClass := []string{"inetOrgPerson","organizationalPerson","person","top"}
for i := 0; i<20; i++ {
name := generateName(r)
req := ldap.NewAddRequest(fmt.Sprintf("cn=%s,ou=users,dc=deuxfleurs,dc=fr",name),nil)
req.Attribute("displayname",[]string{generateName(r)})
req.Attribute("objectclass",ObjectClass)
req.Attribute("structuralobjectclass",StructuralObjectClass)
err = l.Add(req)
if err != nil {
log.Warn(fmt.Sprintf("Erreur survenue sur la création du [%d] user.\n",i))
return nil, err
}
tab_AddRequest = append(tab_AddRequest, *req)
}
return
}
func search_attributes(tab_Attributes []ldap.Attribute, tipe string) (*ldap.Attribute) {
for _,att := range tab_Attributes {
if att.Type == tipe {
return &att
}
}
return nil
// return ldap.Attribute{}
}
func test_attributes(l *ldap.Conn, tab_AddRequest []ldap.AddRequest, filter_objectclass, user_or_group string) (err error) {
for _, addRequest := range tab_AddRequest {
//On prend le cn en supposant qu'il est unique
cn := strings.Split(addRequest.DN,",")[0]
//On crée la requête pour la recherche
search_req := ldap.NewSearchRequest(
fmt.Sprintf("ou=%s,dc=deuxfleurs,dc=fr",user_or_group),
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectclass=%s)(%s))", filter_objectclass,cn),
[]string{"displayname","objectclass","structuralobjectclass"},
nil,
)
//On lance la recherche
result, err := l.Search(search_req)
if err != nil {
return err
}
if len(result.Entries) != 1 {
return errors.New("Test a trouvé plusieurs displaynames en commun ou en a trouvé aucun")
}
//On compare les attributs qu'on a reçu avec les attributs qu'on a envoyé
result_attributes := result.Entries[0].Attributes
log.Debug(fmt.Sprintf("La longueur est de %d, contient : \n %s.\n",len(result_attributes), result_attributes))
//Notre recherche crée un attribut par valeur, même si les valeurs viennent du même nom d'attribut
//Par exemple: objectclass possède 4 valeurs. Alors on aura 4 EntryAttribute qui contient chacune une des 4 valeurs de l'attribut objectclass
//j est l'indice qui représente la j-ème valeur de notre attribut
var j int
var att *ldap.Attribute
for i,attributes := range result_attributes {
//On cherche l'attribut de l'user i qui a le même nom que celui qu'on a reçu et qu'on traite dans cette boucle
if j == 0 {
att = search_attributes(addRequest.Attributes, attributes.Name)
if att == nil {
return errors.New(fmt.Sprintf("Error: test_attributes - Don't find match name attributes. We search %s.\n", attributes.Name))
}
}
log.Debug(fmt.Sprintf("Le nom de l'attribut est %s, sa valeur est: \n %s.", att.Type, att))
if j >= len(att.Vals) || att.Vals[j] != attributes.Values[0] {
return errors.New(fmt.Sprintf("Error: test_attributes - Theses values aren't the same: %d, %d",att.Vals, attributes.Values))
}
if i+1 < len(result_attributes) && result_attributes[i+1].Name == attributes.Name {
j += 1
} else { j = 0}
}
}
return nil
}
func clean(l *ldap.Conn, AddReq_users, AddReq_groups []ldap.AddRequest,user, group bool) (err error){
log.Debug("Debut clean")
if(user) {
for _,req := range AddReq_users {
delReq := ldap.NewDelRequest(req.DN,nil)
err = l.Del(delReq)
if err != nil {
return
}
}
}
if group {
for _,req := range AddReq_groups {
delReq := ldap.NewDelRequest(req.DN, nil)
err = l.Del(delReq)
if err != nil {
return
}
}
}
defer log.Debug("Fin clean")
return
}
func test_modify_attributes(l *ldap.Conn, r *rand.Rand, tab_AddReq []ldap.AddRequest, tab_type_name []string) (err error) {
for _, AddReq := range tab_AddReq {
modReq := ldap.NewModifyRequest(AddReq.DN,nil)
for _, type_name := range tab_type_name {
newName := generateName(r)
modReq.Replace(type_name, []string{newName})
att := search_attributes(AddReq.Attributes, type_name)
att.Vals[0] = newName
}
err = l.Modify(modReq)
if err != nil {
return
}
}
return
}
func add_user_in_groups(l *ldap.Conn, r *rand.Rand, users, groups []ldap.AddRequest) (err error) {
for _,group := range groups {
numberUsers := r.Intn(19) + 1 //Always a minimum of 1 user
list_users := []string{}
for i:=0; i < numberUsers; i++ {
list_users = append(list_users, users[i].DN)
}
modifyReq := ldap.NewModifyRequest( group.DN, nil)
modifyReq.Add("member", list_users)
err = l.Modify(modifyReq)
if err != nil {
log.Warn(fmt.Sprintf("Error: ModifyReq failed, func:add_users_in_groups from group:\n %d",group))
return
}
}
return
}
func delete_groups(l *ldap.Conn, groups []ldap.AddRequest) (list map[string][]string ,err error) {
list = make(map[string][]string)
for _, group := range groups {
//Get lists_users
cn := strings.Split(group.DN,",")[0]
search_req := ldap.NewSearchRequest(
"ou=groups,dc=deuxfleurs,dc=fr",
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectclass=groupOfNames)(%s))",cn),
[]string{"member"},
nil,
)
res , err := l.Search(search_req)
if err != nil {
log.Warn(fmt.Sprintf("Error Search: func: delete_groups_and_check_memberOf, from group: \n %d", group))
return list, err
}
if len(res.Entries) != 1 {
err = errors.New(fmt.Sprintf("SearchResult get: %s, SearchResult wanted: 1", len(res.Entries)))
return list, err
}
EntryAtt := res.Entries[0].Attributes
list_users := []string{}
for _, att := range EntryAtt {
list_users = append(list_users ,att.Values[0])
}
//Del group
del := ldap.NewDelRequest( group.DN, nil)
err = l.Del(del)
if err != nil {
return list, err
}
list[group.DN] = list_users
}
return
}
func check_memberOf(l *ldap.Conn, list map[string][]string) (err error) {
//Check the memberOf of all users
for groupeDN,_ := range list{
search_req := ldap.NewSearchRequest(
"ou=users,dc=deuxfleurs,dc=fr",
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases,0 ,0, false,
fmt.Sprintf("(&(objectclass=inetOrgPerson)(memberOf=%s))",groupeDN),
[]string{"cn"},
nil,
)
res, err := l.Search(search_req)
if err != nil {
return err
}
if len(res.Entries) != 0 {
err = errors.New(fmt.Sprintf("L'user '%s' a encore le DN d'un groupe supprimé: %s",res.Entries[0].Attributes[0].Values[0],groupeDN))
return err
}
}
return err
}
func reconnect(l *ldap.Conn) (l_nouv *ldap.Conn, err error){
l.Close()
l_nouv, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d",adresse,port))
if err != nil {
return
}
err = l_nouv.Bind(bindusername, bindpassword)
return
}
func main() {
var ok bool
bindpassword, ok = os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW")
if !ok {
if len(os.Args) == 2 {
bindpassword = os.Args[1]
} else {
bindpassword = ""
}
}
log.Info(fmt.Sprintf("Password selected: %s",bindpassword))
//log.SetLevel(log.TraceLevel)
//Create a connection with Bottin server
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", adresse, port))
//l.Debug = true
printError(err)
//Bind with the admin account generated
err = l.Bind(bindusername, bindpassword)
printError(err)
//Create our object Rand, it's important to always have the same values
source := rand.NewSource(666475745)
r := rand.New(source)
log.Info(fmt.Sprintf("The seed of the rand object is %d.\n",r.Seed))
//Create user and groups OrgaUnit
err = createOU(l)
if ldap.IsErrorWithCode(err, uint16(68)) {
log.Warn("Les OrganizationalUnit users et groups sont déjà présents.")
}else {
printError(err)
log.Info("Création des OU de groups et users")
}
//Create random groups
tab_AddRequest_groups, err := createGroup(r, l)
printError(err)
log.Info(fmt.Sprintf("Création des groupes aléatoirement réussi: %d\n", len(tab_AddRequest_groups)))
//Create random users
tab_AddRequest_users, err := createUser(r, l)
printError(err)
log.Info(fmt.Sprintf("Création des users aléatoirement réussi: %d\n", len(tab_AddRequest_users)))
//Search and compare attribute Users. (We keep Attribute object from 'Create random users' and compare with the result of our search)
err = test_attributes(l,tab_AddRequest_users, "inetOrgPerson","users")
printError(err)
log.Info("Tous les attributs users insérés dans Consul ont été vérifiés..\n")
//Search and compare attributes Groups
err = test_attributes(l,tab_AddRequest_groups, "groupOfNames","groups")
printError(err)
log.Info("Tous les attributs groups insérés dans Consul ont été vérifiés.\n")
//Close the connection and open an other. If we don't do this, bottin server send a wrong answer. Comment this part if you want to try this
l,err = reconnect(l)
printError(err)
//Modify attributes users and groups.
//Modify users' attributes and check them
log.Debug(fmt.Sprintf("Les valeurs sont:\n %s", tab_AddRequest_users))
err = test_modify_attributes(l, r, tab_AddRequest_users, []string{"displayname"})
printError(err)
log.Debug("Modifications users faites")
//Check if the attributes are correct:
err = test_attributes(l,tab_AddRequest_users, "inetOrgPerson", "users")
printError(err)
log.Info("Les modifications ont bien été prises en compte")
log.Debug(fmt.Sprintf("Les nouvelles valeurs sont:\n %s", tab_AddRequest_users))
//Modify users' attributes and check them
err = test_modify_attributes(l, r, tab_AddRequest_groups, []string{"description"})
printError(err)
log.Info("Modifications groups faites")
//Check if the attributes are correct:
err = test_attributes(l,tab_AddRequest_groups, "groupOfNames", "groups")
printError(err)
log.Info("Les modifications ont bien été prises en compte")
//Close the connection
l, err = reconnect(l)
printError(err)
//Add users in group, search them, delete several samples and search again to be sur it's good
err = add_user_in_groups(l, r, tab_AddRequest_users, tab_AddRequest_groups)
printError(err)
log.Info("Ajout d'users dans les groupes fait")
//Close the connection
l, err = reconnect(l)
printError(err)
list, err := delete_groups(l, tab_AddRequest_groups)
printError(err)
log.Info("groupe supprimé")
l,err = reconnect(l)
printError(err)
err = check_memberOf(l, list)
printError(err)
log.Info("Le memberOf a été correctement vidé")
//Clean: Delete all users and groups (not OU users and groups)
err = clean(l, tab_AddRequest_users, tab_AddRequest_groups, true, false)
printError(err)
log.Info("Clean succes")
return
}

View file

@ -0,0 +1,40 @@
Introduction et Observation premières:
Lors de la réalisation de mon code Go, j'ai trouvé un beug qui provoquait l'arrêt de mon programme car Bottin envoyé une réponse erronée à mon programme.
Dans logs de Bottin je ne voyais aucune erreur, Bottin n'avait pas cessé de fonctionner. Il s'arrêtait à chaque fois sur mes requêtes Del (mes dernières).
Reproduction du beug:
Pour reproduire le beug, il suffit de lancer le programme interrogation.go et de commenter les lignes suivantes :
260 //Close the connection and open an other. If we don't do this, bottin server send a wrong answer. Comment this part if you want to try this
261 l.Close()
262 l, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d",adresse, port))
263 printError(err)
264 err = l.Bind(bindusername, bindpassword)
265 printError(err)
Ainsi on obtient l'erreur suivante:
2021/07/07 01:25:51 Received unexpected message -128, false
Test réalisé pour comprendre la source du problème:
Ma première hypothèses fut que j'envoyais trop de requêtes dans un court laps de temps et ainsi Bottin ou Consul ne pouvait pas suivre.
J'ai placé un sleep de 50 puis de 100 Millisecondes entre chaque requête, j'obtenais toujours la même erreur.
J'ai essayé de mettre un sleep de 10 secondes avant mes requêtes de suppression mais j'obtenais toujours la même chose.
Hack pour résoudre:
La première solution qui a fonctionné était de réduire le nombre de requêtes en n'exécutant pas certains tests random.
La dernière solutions qui est utilisée:
Fermée la connexion puis la réouvrir permet de palier à ce problème
Hypothèses du problème ?:
Existerait-il un Buffer par Bind qui se remplirait ? Et lorsque celui-ci est plein, ne renvoie pas d'erreur juste n'arrive plus à répondre.
Erwan DUFOUR
Deuxfleurs

11
test_automatic/start_test.sh Executable file
View file

@ -0,0 +1,11 @@
#!/bin/bash
trap "kill 0" EXIT
export BOTTIN_DEFAULT_ADMIN_PW=$(openssl rand -base64 24)
echo $BOTTIN_DEFAULT_ADMIN_PW
consul agent -dev > /dev/null 2>&1 &
sleep 2
./bottin > /dev/null 2>&1 &
sleep 1
./test_automatic/integration