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:
parent
aa912b5ceb
commit
a98556d5c1
12 changed files with 558 additions and 12 deletions
17
.drone.yml
17
.drone.yml
|
@ -1,13 +1,24 @@
|
|||
---
|
||||
pipeline:
|
||||
build:
|
||||
kind: pipeline
|
||||
name: bottin
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
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
|
||||
hmac: 8f49fdf0e4abb0790827eed7cac8eedd5e11705d1fa01954a84929933eb7b254
|
||||
hmac: 939fca00ff84d40e9364cd936c18c40c5becafa05e0f887bc04cf6336a4913a2
|
||||
|
||||
...
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
bottin
|
||||
bottin.static
|
||||
config.json
|
||||
test_automatic/integration
|
||||
|
|
1
go.mod
1
go.mod
|
@ -3,6 +3,7 @@ module bottin
|
|||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/go-ldap/ldap/v3 v3.3.0
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/hashicorp/consul/api v1.3.0
|
||||
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
|
||||
|
|
14
go.sum
14
go.sum
|
@ -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/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
|
||||
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
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=
|
||||
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-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-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-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/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-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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
|
9
main.go
9
main.go
|
@ -320,12 +320,19 @@ func (server *Server) init() error {
|
|||
return err
|
||||
}
|
||||
|
||||
|
||||
admin_pass_str, environnement_variable_exist := os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW")
|
||||
if !environnement_variable_exist {
|
||||
admin_pass := make([]byte, 8)
|
||||
_, err = rand.Read(admin_pass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
admin_pass_str := base64.RawURLEncoding.EncodeToString(admin_pass)
|
||||
admin_pass_str = base64.RawURLEncoding.EncodeToString(admin_pass)
|
||||
} else {
|
||||
server.logger.Printf("It seems that exists a password in environnement variable")
|
||||
}
|
||||
|
||||
admin_pass_hash := SSHAEncode([]byte(admin_pass_str))
|
||||
|
||||
admin_dn := "cn=admin," + server.config.Suffix
|
||||
|
|
BIN
test_automatic/Scan_Bad_Packets.pcapng
Normal file
BIN
test_automatic/Scan_Bad_Packets.pcapng
Normal file
Binary file not shown.
BIN
test_automatic/Scan_Good_Packets.pcapng
Normal file
BIN
test_automatic/Scan_Good_Packets.pcapng
Normal file
Binary file not shown.
8
test_automatic/go.mod
Normal file
8
test_automatic/go.mod
Normal 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
23
test_automatic/go.sum
Normal 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=
|
430
test_automatic/integration.go
Normal file
430
test_automatic/integration.go
Normal 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
|
||||
|
||||
}
|
40
test_automatic/rapport_beug_bottin.txt
Normal file
40
test_automatic/rapport_beug_bottin.txt
Normal 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
11
test_automatic/start_test.sh
Executable 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
|
Loading…
Reference in a new issue