diff options
author | Quentin Dufour <quentin@deuxfleurs.fr> | 2022-07-19 10:35:14 +0200 |
---|---|---|
committer | Quentin Dufour <quentin@deuxfleurs.fr> | 2022-07-19 10:35:14 +0200 |
commit | 3c846b6a59c1e725b56b7784c30cfbd5a3dc080b (patch) | |
tree | c5c3353f3b5332e0477a3a376192d206d6e79faf | |
parent | 216e175eafd50b977e132ca0055df9fed9e83a6a (diff) | |
download | guichet-better_login.tar.gz guichet-better_login.zip |
Inclusive + Fix checks + Fix SSHAbetter_login
-rw-r--r-- | config.json.example | 6 | ||||
-rw-r--r-- | go.mod | 6 | ||||
-rw-r--r-- | go.sum | 19 | ||||
-rw-r--r-- | invite.go | 33 | ||||
-rw-r--r-- | profile.go | 15 | ||||
-rw-r--r-- | ssha.go | 33 | ||||
-rw-r--r-- | templates/home.html | 2 | ||||
-rw-r--r-- | templates/invite_new_account.html | 15 | ||||
-rw-r--r-- | templates/login.html | 8 | ||||
-rw-r--r-- | templates/profile.html | 2 |
10 files changed, 70 insertions, 69 deletions
diff --git a/config.json.example b/config.json.example index eb3f544..aa69b2e 100644 --- a/config.json.example +++ b/config.json.example @@ -4,9 +4,9 @@ "base_dn": "dc=example,dc=com", "user_base_dn": "ou=users,dc=example,dc=com", - "user_name_attr": "uid", + "user_name_attr": "cn", "group_base_dn": "ou=groups,dc=example,dc=com", - "group_name_attr": "gid", + "group_name_attr": "cn", "invitation_base_dn": "ou=invitations,dc=example,dc=com", "invitation_name_attr": "cn", @@ -21,7 +21,7 @@ "smtp_username": "guichet", "smtp_password": "", - "admin_account": "uid=admin,dc=example,dc=com", + "admin_account": "cn=admin,dc=example,dc=com", "group_can_admin": "gid=admin,ou=groups,dc=example,dc=com", "group_can_invite": "", @@ -5,14 +5,12 @@ go 1.13 require ( github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b github.com/emersion/go-smtp v0.12.1 - github.com/go-ldap/ldap v3.0.3+incompatible github.com/go-ldap/ldap/v3 v3.1.6 github.com/google/uuid v1.1.1 github.com/gorilla/mux v1.7.3 github.com/gorilla/sessions v1.2.0 + github.com/jsimonetti/pwscheme v0.0.0-20220125093853-4d9895f5db73 github.com/minio/minio-go/v7 v7.0.0 - github.com/sirupsen/logrus v1.6.0 - github.com/stretchr/objx v0.1.1 // indirect - golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 + golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 ) @@ -1,4 +1,5 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/emersion/go-sasl v0.0.0-20190817083125-240c8404624e/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k= @@ -8,13 +9,12 @@ github.com/emersion/go-smtp v0.12.1 h1:1R8BDqrR2HhlGwgFYcOi+BVTvK1bMjAB65QcVpJ5s github.com/emersion/go-smtp v0.12.1/go.mod h1:SD9V/xa4ndMw77lR3Mf7htkp8RBNYuPh9UeuBs9tpUQ= github.com/go-asn1-ber/asn1-ber v1.3.1 h1:gvPdv/Hr++TRFCl0UbPFHC54P9N9jgsRPnmnr419Uck= github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= -github.com/go-ldap/ldap v3.0.3+incompatible h1:HTeSZO8hWMS1Rgb2Ziku6b8a7qRIZZMHjsvuZyatzwk= -github.com/go-ldap/ldap v3.0.3+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/go-ldap/ldap/v3 v3.1.6 h1:VTihvB7egSAvU6KOagaiA/EvgJMR2jsjRAVIho2ydBo= github.com/go-ldap/ldap/v3 v3.1.6/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -22,14 +22,14 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ= github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/jsimonetti/pwscheme v0.0.0-20220125093853-4d9895f5db73 h1:ZhC4QngptYaGx53+ph1RjxcH8fkCozBaY+935TNX4i8= +github.com/jsimonetti/pwscheme v0.0.0-20220125093853-4d9895f5db73/go.mod h1:t0Q9JvoMTfTYdAWIk2MF69iz+Qpdk9D+PgVu6fVmaDI= github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -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/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4= github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= @@ -45,16 +45,16 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLD github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +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/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -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= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -66,7 +66,6 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgP golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 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/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -133,9 +133,12 @@ func handleNewAccount(w http.ResponseWriter, r *http.Request, l *ldap.Conn, invi } func tryCreateAccount(l *ldap.Conn, data *NewAccountData, pass1 string, pass2 string, invitedBy string) { + checkFailed := false + // Check if username is correct - if match, err := regexp.MatchString("^[a-zA-Z0-9._-]+$", data.Username); !(err == nil && match) { + if match, err := regexp.MatchString("^[a-z0-9._-]+$", data.Username); !(err == nil && match) { data.ErrorInvalidUsername = true + checkFailed = true } // Check if user exists @@ -150,30 +153,39 @@ func tryCreateAccount(l *ldap.Conn, data *NewAccountData, pass1 string, pass2 st sr, err := l.Search(searchRq) if err != nil { data.ErrorMessage = err.Error() - return + checkFailed = true } if len(sr.Entries) > 0 { data.ErrorUsernameTaken = true - return + checkFailed = true } // Check that password is long enough if len(pass1) < 8 { data.ErrorPasswordTooShort = true - return + checkFailed = true } if pass1 != pass2 { data.ErrorPasswordMismatch = true - return + checkFailed = true } + if checkFailed { + return + } + // Actually create user req := ldap.NewAddRequest(userDn, nil) req.Attribute("objectclass", []string{"inetOrgPerson", "organizationalPerson", "person", "top"}) req.Attribute("structuralobjectclass", []string{"inetOrgPerson"}) - req.Attribute("userpassword", []string{SSHAEncode([]byte(pass1))}) + pw, err := SSHAEncode(pass1) + if err != nil { + data.ErrorMessage = err.Error() + return + } + req.Attribute("userpassword", []string{pw}) req.Attribute("invitedby", []string{invitedBy}) if len(data.DisplayName) > 0 { req.Attribute("displayname", []string{data.DisplayName}) @@ -259,10 +271,15 @@ func trySendCode(login *LoginStatus, choice string, sendto string, data *SendCod // Create invitation object in database inviteDn := config.InvitationNameAttr + "=" + code_id + "," + config.InvitationBaseDN req := ldap.NewAddRequest(inviteDn, nil) - req.Attribute("userpassword", []string{SSHAEncode([]byte(code_pw))}) + pw, err := SSHAEncode(code_pw) + if err != nil { + data.ErrorMessage = err.Error() + return + } + req.Attribute("userpassword", []string{pw}) req.Attribute("objectclass", []string{"top", "invitationCode"}) - err := login.conn.Add(req) + err = login.conn.Add(req) if err != nil { data.ErrorMessage = err.Error() return @@ -122,12 +122,17 @@ func handlePasswd(w http.ResponseWriter, r *http.Request) { data.NoMatchError = true } else { modify_request := ldap.NewModifyRequest(login.Info.DN, nil) - modify_request.Replace("userpassword", []string{SSHAEncode([]byte(password))}) - err := login.conn.Modify(modify_request) - if err != nil { - data.ErrorMessage = err.Error() + pw, err := SSHAEncode(password); + if err == nil { + modify_request.Replace("userpassword", []string{pw}) + err := login.conn.Modify(modify_request) + if err != nil { + data.ErrorMessage = err.Error() + } else { + data.Success = true + } } else { - data.Success = true + data.ErrorMessage = err.Error() } } } @@ -1,37 +1,10 @@ package main import ( - "crypto/rand" - "crypto/sha1" - "encoding/base64" - "fmt" - - log "github.com/sirupsen/logrus" + "github.com/jsimonetti/pwscheme/ssha512" ) // Encode encodes the []byte of raw password -func SSHAEncode(rawPassPhrase []byte) string { - hash := makeSSHAHash(rawPassPhrase, makeSalt()) - b64 := base64.StdEncoding.EncodeToString(hash) - return fmt.Sprintf("{ssha}%s", b64) -} - -// makeSalt make a 32 byte array containing random bytes. -func makeSalt() []byte { - sbytes := make([]byte, 32) - _, err := rand.Read(sbytes) - if err != nil { - log.Panicf("Could not read random bytes: %s", err) - } - return sbytes -} - -// makeSSHAHash make hasing using SHA-1 with salt. This is not the final output though. You need to append {SSHA} string with base64 of this hash. -func makeSSHAHash(passphrase, salt []byte) []byte { - sha := sha1.New() - sha.Write(passphrase) - sha.Write(salt) - - h := sha.Sum(nil) - return append(h, salt...) +func SSHAEncode(rawPassPhrase string) (string, error) { + return ssha512.Generate(rawPassPhrase, 16) } diff --git a/templates/home.html b/templates/home.html index 7d8cc99..376aefe 100644 --- a/templates/home.html +++ b/templates/home.html @@ -38,7 +38,7 @@ Administration </div> <div class="list-group list-group-flush"> - <a class="list-group-item list-group-item-action" href="/admin/users">Utilisateurs</a> + <a class="list-group-item list-group-item-action" href="/admin/users">Utilisateur·ices</a> <a class="list-group-item list-group-item-action" href="/admin/groups">Groupes</a> <a class="list-group-item list-group-item-action" href="/admin/ldap/{{.BaseDN}}">Explorateur LDAP</a> </div> diff --git a/templates/invite_new_account.html b/templates/invite_new_account.html index ab80887..9ba6ae0 100644 --- a/templates/invite_new_account.html +++ b/templates/invite_new_account.html @@ -12,7 +12,7 @@ </div> {{end}} {{if .WarningMessage}} - <div class="alert alert-danger mt-4">Des erreurs se sont produtes, le compte pourrait ne pas être totalement fonctionnel. + <div class="alert alert-danger mt-4">Des erreurs se sont produites, le compte pourrait ne pas être totalement fonctionnel. <div style="font-size: 0.8em">{{ .WarningMessage }}</div> </div> {{end}} @@ -25,12 +25,15 @@ <form method="POST" class="mt-4"> <h5>Renseignements obligatoires</h5> <div class="form-group"> - <label for="username">Nom d'utilisateur souhaité :</label> + <label for="username">Identifiant souhaité :</label> <input type="text" id="username" name="username" class="form-control" value="{{ .Username }}" /> + <small class="form-text text-muted"> + Votre identifiant doit être en minuscule. + </small> </div> {{if .ErrorInvalidUsername}} <div class="alert alert-warning"> - Nom d'utilisateur invalide. Ne peut contenir que les caractères suivants : chiffres, lettres, point, tiret bas (_) et tiret du milieu (-). + Nom d'utilisateur invalide. Ne peut contenir que les caractères suivants : chiffres, lettres minuscules, point, tiret bas (_) et tiret du milieu (-). </div> {{end}} {{if .ErrorUsernameTaken}} @@ -41,6 +44,9 @@ <div class="form-group"> <label for="password">Mot de passe :</label> <input type="password" id="password" name="password" class="form-control" /> + <small class="form-text text-muted"> + La seule contrainte est que votre mot de passe doit faire au moins 8 caractères. Utilisez chiffres, majuscules, et caractères spéciaux sans modération ! + </small> </div> {{if .ErrorPasswordTooShort}} <div class="alert alert-warning"> @@ -58,6 +64,9 @@ {{end}} <h5>Renseignements optionnels</h5> + <small class="form-text text-muted"> + Ces informations sont utilisées exclusivement pour "pré-configurer" les services que vous utiliserez, elles n'ont pas besoin de correspondre à votre état civil. + </small> <div class="form-group"> <label for="displayname">Nom complet :</label> <input type="text" id="displayname" name="displayname" class="form-control" value="{{ .DisplayName }}" /> diff --git a/templates/login.html b/templates/login.html index f151456..84742e1 100644 --- a/templates/login.html +++ b/templates/login.html @@ -5,7 +5,7 @@ <form method="POST"> {{if .WrongUser}} - <div class="alert alert-danger">Nom d'utilisateur invalide.</div> + <div class="alert alert-danger">Identifiant invalide.</div> {{end}} {{if .WrongPass}} <div class="alert alert-danger">Mot de passe invalide.</div> @@ -16,11 +16,11 @@ </div> {{end}} <div class="form-group"> - <label for="username">Nom d'utilisateur:</label> + <label for="username">Identifiant :</label> <input type="text" name="username" id="username" class="form-control" value="{{ .Username }}" /> </div> <div class="form-group"> - <label for="password">Mot de passe:</label> + <label for="password">Mot de passe :</label> <input type="password" name="password" id="password" class="form-control" /> </div> <button type="submit" class="btn btn-primary">Se connecter</button> @@ -30,6 +30,6 @@ <p><strong>Mot de passe oublié ?</strong> Écrivez à <samp>coucou</samp><img src="static/image/at_sign.svg" style="height: 1em" alt="arobase" /><samp>deuxfleurs.fr</samp> - ou contactez directement votre administrateur favori.</p> + ou contactez directement votre opérateur·ice préféré·e.</p> {{end}} diff --git a/templates/profile.html b/templates/profile.html index edf9d76..56461eb 100644 --- a/templates/profile.html +++ b/templates/profile.html @@ -19,7 +19,7 @@ <form method="POST" class="mt-4" enctype="multipart/form-data"> <div class="form-row"> <div class="form-group col-md-6"> - <label>Nom d'utilisateur:</label> + <label>Identifiant:</label> <input type="text" disabled="true" class="form-control" value="{{ .Status.Info.Username }}" /> </div> <div class="form-group col-md-6"> |