aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuentin Dufour <quentin@deuxfleurs.fr>2022-07-19 10:35:14 +0200
committerQuentin Dufour <quentin@deuxfleurs.fr>2022-07-19 10:35:14 +0200
commit3c846b6a59c1e725b56b7784c30cfbd5a3dc080b (patch)
treec5c3353f3b5332e0477a3a376192d206d6e79faf
parent216e175eafd50b977e132ca0055df9fed9e83a6a (diff)
downloadguichet-better_login.tar.gz
guichet-better_login.zip
Inclusive + Fix checks + Fix SSHAbetter_login
-rw-r--r--config.json.example6
-rw-r--r--go.mod6
-rw-r--r--go.sum19
-rw-r--r--invite.go33
-rw-r--r--profile.go15
-rw-r--r--ssha.go33
-rw-r--r--templates/home.html2
-rw-r--r--templates/invite_new_account.html15
-rw-r--r--templates/login.html8
-rw-r--r--templates/profile.html2
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": "",
diff --git a/go.mod b/go.mod
index 781b3ce..0ea29a6 100644
--- a/go.mod
+++ b/go.mod
@@ -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
)
diff --git a/go.sum b/go.sum
index 9594bab..77eaf79 100644
--- a/go.sum
+++ b/go.sum
@@ -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=
diff --git a/invite.go b/invite.go
index 2ec6243..689c7e4 100644
--- a/invite.go
+++ b/invite.go
@@ -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
diff --git a/profile.go b/profile.go
index 4f5cf28..603b10a 100644
--- a/profile.go
+++ b/profile.go
@@ -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()
}
}
}
diff --git a/ssha.go b/ssha.go
index fb7f3d8..a198d27 100644
--- a/ssha.go
+++ b/ssha.go
@@ -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">