mailing list: ability to add guest users

This commit is contained in:
Alex Auvolat 2023-02-08 16:46:13 +01:00
parent 670123df38
commit f77697f501
6 changed files with 139 additions and 30 deletions

View File

@ -164,6 +164,7 @@ type AdminMailingListTplData struct {
MailingList *ldap.Entry MailingList *ldap.Entry
Members EntryList Members EntryList
PossibleNewMembers EntryList PossibleNewMembers EntryList
AllowGuest bool
Error string Error string
Success bool Success bool
@ -198,6 +199,60 @@ func handleAdminMailingList(w http.ResponseWriter, r *http.Request) {
} else { } else {
dSuccess = true dSuccess = true
} }
} else if action == "add-external" {
mail := strings.Join(r.Form["mail"], "")
displayname := strings.Join(r.Form["displayname"], "")
searchRequest := ldap.NewSearchRequest(
config.UserBaseDN,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=organizationalPerson)(mail=%s))", mail),
[]string{"dn", "displayname", "mail"},
nil)
sr, err := login.conn.Search(searchRequest)
if err != nil {
dError = err.Error()
} else {
if len(sr.Entries) == 0 {
if config.MailingGuestsBaseDN != "" {
guestDn := fmt.Sprintf("%s=%s,%s", config.UserNameAttr, mail, config.MailingGuestsBaseDN)
req := ldap.NewAddRequest(guestDn, nil)
req.Attribute("objectclass", []string{"inetOrgPerson", "organizationalPerson", "person", "top"})
req.Attribute("mail", []string{mail})
if displayname != "" {
req.Attribute("displayname", []string{displayname})
}
err := login.conn.Add(req)
if err != nil {
dError = err.Error()
} else {
modify_request := ldap.NewModifyRequest(dn, nil)
modify_request.Add("member", []string{guestDn})
err := login.conn.Modify(modify_request)
if err != nil {
dError = err.Error()
} else {
dSuccess = true
}
}
} else {
dError = "Adding guest users not supported, the user must already have an LDAP account."
}
} else if len(sr.Entries) == 1 {
modify_request := ldap.NewModifyRequest(dn, nil)
modify_request.Add("member", []string{sr.Entries[0].DN})
err := login.conn.Modify(modify_request)
if err != nil {
dError = err.Error()
} else {
dSuccess = true
}
} else {
dError = fmt.Sprintf("Multiple users exist with email address %s", mail)
}
}
} else if action == "delete-member" { } else if action == "delete-member" {
member := strings.Join(r.Form["member"], "") member := strings.Join(r.Form["member"], "")
modify_request := ldap.NewModifyRequest(dn, nil) modify_request := ldap.NewModifyRequest(dn, nil)
@ -217,7 +272,7 @@ func handleAdminMailingList(w http.ResponseWriter, r *http.Request) {
dn, dn,
ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(objectclass=groupOfNames)"), fmt.Sprintf("(objectclass=groupOfNames)"),
[]string{"dn", config.MailingNameAttr, "member"}, []string{"dn", config.MailingNameAttr, "member", "description"},
nil) nil)
sr, err := login.conn.Search(searchRequest) sr, err := login.conn.Search(searchRequest)
@ -274,6 +329,7 @@ func handleAdminMailingList(w http.ResponseWriter, r *http.Request) {
MailingList: ml, MailingList: ml,
Members: members, Members: members,
PossibleNewMembers: possibleNewMembers, PossibleNewMembers: possibleNewMembers,
AllowGuest: config.MailingGuestsBaseDN != "",
Error: dError, Error: dError,
Success: dSuccess, Success: dSuccess,
@ -785,7 +841,7 @@ func handleAdminCreate(w http.ResponseWriter, r *http.Request) {
data.IdType = config.UserNameAttr data.IdType = config.UserNameAttr
data.StructuralObjectClass = "inetOrgPerson" data.StructuralObjectClass = "inetOrgPerson"
data.ObjectClass = "inetOrgPerson\norganizationalPerson\nperson\ntop" data.ObjectClass = "inetOrgPerson\norganizationalPerson\nperson\ntop"
} else if template == "group" { } else if template == "group" || template == "ml" {
data.IdType = config.UserNameAttr data.IdType = config.UserNameAttr
data.StructuralObjectClass = "groupOfNames" data.StructuralObjectClass = "groupOfNames"
data.ObjectClass = "groupOfNames\ntop" data.ObjectClass = "groupOfNames\ntop"
@ -842,7 +898,7 @@ func handleAdminCreate(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
data.Error = err.Error() data.Error = err.Error()
} else { } else {
if super_dn == config.MailingBaseDN && data.IdType == config.MailingNameAttr { if template == "ml" {
http.Redirect(w, r, "/admin/mailing/"+data.IdValue, http.StatusFound) http.Redirect(w, r, "/admin/mailing/"+data.IdValue, http.StatusFound)
} else { } else {
http.Redirect(w, r, "/admin/ldap/"+dn, http.StatusFound) http.Redirect(w, r, "/admin/ldap/"+dn, http.StatusFound)

16
main.go
View File

@ -23,13 +23,15 @@ type ConfigFile struct {
LdapServerAddr string `json:"ldap_server_addr"` LdapServerAddr string `json:"ldap_server_addr"`
LdapTLS bool `json:"ldap_tls"` LdapTLS bool `json:"ldap_tls"`
BaseDN string `json:"base_dn"` BaseDN string `json:"base_dn"`
UserBaseDN string `json:"user_base_dn"` UserBaseDN string `json:"user_base_dn"`
UserNameAttr string `json:"user_name_attr"` UserNameAttr string `json:"user_name_attr"`
GroupBaseDN string `json:"group_base_dn"` GroupBaseDN string `json:"group_base_dn"`
GroupNameAttr string `json:"group_name_attr"` GroupNameAttr string `json:"group_name_attr"`
MailingBaseDN string `json:"mailing_list_base_dn"`
MailingNameAttr string `json:"mailing_list_name_attr"` MailingBaseDN string `json:"mailing_list_base_dn"`
MailingNameAttr string `json:"mailing_list_name_attr"`
MailingGuestsBaseDN string `json:"mailing_list_guest_user_base_dn"`
InvitationBaseDN string `json:"invitation_base_dn"` InvitationBaseDN string `json:"invitation_base_dn"`
InvitationNameAttr string `json:"invitation_name_attr"` InvitationNameAttr string `json:"invitation_name_attr"`

View File

@ -30,33 +30,40 @@
<input type="text" disabled="true" class="form-control" value="{{ .SuperDN }}" /> <input type="text" disabled="true" class="form-control" value="{{ .SuperDN }}" />
</div> </div>
--> -->
{{if eq .Template "ml"}}
<div class="form-group">
<label for="idvalue">Adresse complète de la mailing list :</label>
<input type="text" id="idvalue" name="idvalue" class="form-control" value="{{ .IdValue }}" placeholder="example@deuxfleurs.fr" />
</div>
{{else}}
<div class="form-group">
<label for="idvalue">Identifiant :</label>
<input type="text" id="idvalue" name="idvalue" class="form-control" value="{{ .IdValue }}" />
</div>
{{end}}
<div class="form-group"> <div class="form-group">
<label for="idvalue">Identifiant:</label> <label for="idtype">Type d'identifiant :</label>
<input type="text" id="idvalue" name="idvalue" class="form-control" value="{{ .IdValue }}" />
</div>
<div class="form-group">
<label for="idtype">Type d'identifiant:</label>
<input type="text" {{if .Template}}disabled="disabled"{{end}} id="idtype" name="idtype" class="form-control" value="{{ .IdType }}" /> <input type="text" {{if .Template}}disabled="disabled"{{end}} id="idtype" name="idtype" class="form-control" value="{{ .IdType }}" />
</div> </div>
{{ if eq .Template "user" }} {{ if eq .Template "user" }}
<div class="form-group"> <div class="form-group">
<label for="displayname">Nom:</label> <label for="displayname">Nom :</label>
<input type="text" id="displayname" name="displayname" class="form-control" value="{{ .DisplayName }}" /> <input type="text" id="displayname" name="displayname" class="form-control" value="{{ .DisplayName }}" />
</div> </div>
<input type="hidden" name="description" value="" /> <input type="hidden" name="description" value="" />
{{ else }} {{ else }}
<div class="form-group"> <div class="form-group">
<label for="description">Description:</label> <label for="description">Description :</label>
<input type="text" id="description" name="description" class="form-control" value="{{ .Description }}" /> <input type="text" id="description" name="description" class="form-control" value="{{ .Description }}" />
</div> </div>
<input type="hidden" name="displayname" value="" /> <input type="hidden" name="displayname" value="" />
{{ end }} {{ end }}
<div class="form-group"> <div class="form-group">
<label for="soc">StructuralObjectClass:</label> <label for="soc">StructuralObjectClass :</label>
<input type="text" {{if .Template}}disabled="disabled"{{end}} id="soc" name="soc" class="form-control" value="{{ .StructuralObjectClass }}" /> <input type="text" {{if .Template}}disabled="disabled"{{end}} id="soc" name="soc" class="form-control" value="{{ .StructuralObjectClass }}" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="oc">ObjectClass:</label> <label for="oc">ObjectClass :</label>
<textarea rows="5" {{if .Template}}disabled="disabled"{{end}} id="oc" name="oc" class="form-control">{{ .ObjectClass }}</textarea> <textarea rows="5" {{if .Template}}disabled="disabled"{{end}} id="oc" name="oc" class="form-control">{{ .ObjectClass }}</textarea>
</div> </div>
<button type="submit" class="btn btn-primary">Créer l'objet</button> <button type="submit" class="btn btn-primary">Créer l'objet</button>

View File

@ -4,7 +4,7 @@
<div class="d-flex"> <div class="d-flex">
<h4>Mailing lists</h4> <h4>Mailing lists</h4>
<a class="ml-auto btn btn-success" href="/admin/create/group/{{.MailingBaseDN}}">Nouvelle mailing list</a> <a class="ml-auto btn btn-success" href="/admin/create/ml/{{.MailingBaseDN}}">Nouvelle mailing list</a>
<a class="ml-4 btn btn-info" href="/">Menu principal</a> <a class="ml-4 btn btn-info" href="/">Menu principal</a>
</div> </div>

View File

@ -20,6 +20,10 @@
</div> </div>
{{end}} {{end}}
{{with $desc := .MailingList.GetAttributeValue "description"}}{{if $desc}}
<p class="mt-4">{{$desc}}</p>
{{end}}{{end}}
<table class="table mt-4"> <table class="table mt-4">
<thead> <thead>
<th scope="col">Adresse</th> <th scope="col">Adresse</th>
@ -46,16 +50,20 @@
</tr> </tr>
{{end}} {{end}}
{{end}} {{end}}
{{if not .Members}}
<tr><td>(aucun abonné)</td></tr>
{{end}}
</tbody> </tbody>
</table> </table>
<hr class="mt-4" /> <hr class="mt-4" />
<h5 class="mt-4">Ajouter un destinataire</h5> <h5 class="mt-4">Ajouter un destinataire</h5>
<form method="POST">
<input type="hidden" name="action" value="add-member" /> <div class="container">
<div class="row mt-4"> <form method="POST">
<div class="col-md-3"><strong>Utilisateur existant :</strong> <input type="hidden" name="action" value="add-member" />
</div> <div class="row mt-4">
<div class="col-md-3"><strong>Utilisateur existant :</strong> </div>
<div class="col-md-5"> <div class="col-md-5">
<input class="form-control" type="text" list="users" name="member" placeholder="Utilisateur..." /> <input class="form-control" type="text" list="users" name="member" placeholder="Utilisateur..." />
<datalist id="users"> <datalist id="users">
@ -67,7 +75,43 @@
</datalist> </datalist>
</div> </div>
<div class="col-md-2"> <div class="col-md-2">
<input type="submit" value="Ajouter" class="form-control btn btn-success btn-sm" /> <input type="submit" value="Ajouter" class="form-control btn btn-success btn-sm" />
</div> </div>
</form> </div>
</form>
{{if .AllowGuest}}
<div class="row mt-4">
<div class="col-md-10">OU</div>
</div>
<form method="POST">
<input type="hidden" name="action" value="add-external" />
<div class="row mt-4">
<div class="col-md-3"><strong>E-mail :</strong></div>
<div class="col-md-5">
<input class="form-control" type="text" name="mail" placeholder="machin@truc.net..." />
</div>
<div class="col-md-2">
</div>
</div>
<div class="row mt-4">
<div class="col-md-3"><strong>Nom (optionnel) :</strong></div>
<div class="col-md-5">
<input class="form-control" type="text" name="displayname" placeholder="Machin Truc..." />
</div>
<div class="col-md-2">
<input type="submit" value="Ajouter" class="form-control btn btn-success btn-sm" />
</div>
</div>
<div class="row">
<small class="form-text text-muted col-md-10">
Si un utilisateur existe déjà avec l'email spécifiée, celui-ci sera ajouté à la liste.
Sinon, un utilisateur invité sera créé.
</small>
</div>
</form>
{{end}}
</div>
{{end}} {{end}}

View File

@ -6,7 +6,7 @@
<link rel="stylesheet" href="/static/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/css/bootstrap.min.css">
<title>{{template "title"}} Guichet</title> <title>{{template "title" .}} Guichet</title>
</head> </head>
<body> <body>
<div class="container mb-4"> <div class="container mb-4">