aboutsummaryrefslogtreecommitdiff
path: root/admin.go
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2020-02-09 22:06:33 +0100
committerAlex Auvolat <alex@adnab.me>2020-02-09 22:06:33 +0100
commit43825b1bbc02e9b1697b965a1621a936c5ae0334 (patch)
tree19945695086a20c8f8aedda7c90e171b61638b41 /admin.go
parentf929ca7297905e4233aa32fe2d80dd4cc3fcda30 (diff)
downloadguichet-43825b1bbc02e9b1697b965a1621a936c5ae0334.tar.gz
guichet-43825b1bbc02e9b1697b965a1621a936c5ae0334.zip
LDAP modification form
Diffstat (limited to 'admin.go')
-rw-r--r--admin.go287
1 files changed, 276 insertions, 11 deletions
diff --git a/admin.go b/admin.go
index 5a86fe2..e6a55f5 100644
--- a/admin.go
+++ b/admin.go
@@ -1,12 +1,14 @@
package main
import (
+ "strings"
"fmt"
"html/template"
"net/http"
"sort"
"github.com/go-ldap/ldap/v3"
+ "github.com/gorilla/mux"
)
func checkAdminLogin(w http.ResponseWriter, r *http.Request) *LoginStatus {
@@ -30,16 +32,31 @@ func checkAdminLogin(w http.ResponseWriter, r *http.Request) *LoginStatus {
return login
}
+type EntryList []*ldap.Entry
+
+func (d EntryList) Len() int {
+ return len(d)
+}
+
+func (d EntryList) Swap(i, j int) {
+ d[i], d[j] = d[j], d[i]
+}
+
+func (d EntryList) Less(i, j int) bool {
+ return d[i].DN < d[j].DN
+}
+
+
type AdminUsersTplData struct {
Login *LoginStatus
UserNameAttr string
- Users []*ldap.Entry
+ Users EntryList
}
func handleAdminUsers(w http.ResponseWriter, r *http.Request) {
templateAdminUsers := template.Must(template.ParseFiles("templates/layout.html", "templates/admin_users.html"))
- login := checkLogin(w, r)
+ login := checkAdminLogin(w, r)
if login == nil {
return
}
@@ -60,22 +77,270 @@ func handleAdminUsers(w http.ResponseWriter, r *http.Request) {
data := &AdminUsersTplData{
Login: login,
UserNameAttr: config.UserNameAttr,
- Users: sr.Entries,
+ Users: EntryList(sr.Entries),
}
- sort.Sort(data)
+ sort.Sort(data.Users)
templateAdminUsers.Execute(w, data)
}
-func (d *AdminUsersTplData) Len() int {
- return len(d.Users)
+type AdminGroupsTplData struct {
+ Login *LoginStatus
+ GroupNameAttr string
+ Groups EntryList
}
-func (d *AdminUsersTplData) Swap(i, j int) {
- d.Users[i], d.Users[j] = d.Users[j], d.Users[i]
+func handleAdminGroups(w http.ResponseWriter, r *http.Request) {
+ templateAdminGroups := template.Must(template.ParseFiles("templates/layout.html", "templates/admin_groups.html"))
+
+ login := checkAdminLogin(w, r)
+ if login == nil {
+ return
+ }
+
+ searchRequest := ldap.NewSearchRequest(
+ config.GroupBaseDN,
+ ldap.ScopeSingleLevel, ldap.NeverDerefAliases, 0, 0, false,
+ fmt.Sprintf("(&(objectClass=groupOfNames))"),
+ []string{config.GroupNameAttr, "dn", "displayname"},
+ nil)
+
+ sr, err := login.conn.Search(searchRequest)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ data := &AdminGroupsTplData{
+ Login: login,
+ GroupNameAttr: config.GroupNameAttr,
+ Groups: EntryList(sr.Entries),
+ }
+ sort.Sort(data.Groups)
+
+ templateAdminGroups.Execute(w, data)
}
-func (d *AdminUsersTplData) Less(i, j int) bool {
- return d.Users[i].GetAttributeValue(config.UserNameAttr) <
- d.Users[j].GetAttributeValue(config.UserNameAttr)
+type AdminLDAPTplData struct {
+ DN string
+ Members []string
+ Groups []string
+ Props map[string]*PropValues
+ Children []Child
+ Path []PathItem
+ AddError string
+}
+
+type Child struct {
+ DN string
+ Identifier string
+ DisplayName string
+}
+
+type PathItem struct {
+ DN string
+ Identifier string
+ Active bool
+}
+
+type PropValues struct {
+ Values []string
+ Editable bool
+ ModifySuccess bool
+ ModifyError string
+}
+
+func handleAdminLDAP(w http.ResponseWriter, r *http.Request) {
+ templateAdminLDAP := template.Must(template.ParseFiles("templates/layout.html", "templates/admin_ldap.html"))
+
+ login := checkAdminLogin(w, r)
+ if login == nil {
+ return
+ }
+
+ dn := mux.Vars(r)["dn"]
+
+ modifyAttr := ""
+ modifyError := ""
+ modifySuccess := false
+ addError := ""
+
+ if r.Method == "POST" {
+ r.ParseForm()
+ action := strings.Join(r.Form["action"], "")
+ if action == "modify" {
+ attr := strings.Join(r.Form["attr"], "")
+ values := strings.Split(strings.Join(r.Form["values"], ""), "\n")
+ values_filtered := []string{}
+ for _, v := range values {
+ v2 := strings.TrimSpace(v)
+ if v2 != "" {
+ values_filtered = append(values_filtered, v2)
+ }
+ }
+
+ modifyAttr = attr
+ if len(values_filtered) == 0 {
+ modifyError = "Refusing to delete attribute."
+ } else {
+ modify_request := ldap.NewModifyRequest(dn, nil)
+ modify_request.Replace(attr, values_filtered)
+
+ err := login.conn.Modify(modify_request)
+ if err != nil {
+ modifyError = err.Error()
+ } else {
+ modifySuccess = true
+ }
+ }
+ } else if action == "add" {
+ attr := strings.Join(r.Form["attr"], "")
+ values := strings.Split(strings.Join(r.Form["values"], ""), "\n")
+ values_filtered := []string{}
+ for _, v := range values {
+ v2 := strings.TrimSpace(v)
+ if v2 != "" {
+ values_filtered = append(values_filtered, v2)
+ }
+ }
+
+ modify_request := ldap.NewModifyRequest(dn, nil)
+ modify_request.Add(attr, values_filtered)
+
+ err := login.conn.Modify(modify_request)
+ modifyAttr = attr
+ if err != nil {
+ addError = err.Error()
+ }
+ } else if action == "delete" {
+ attr := strings.Join(r.Form["attr"], "")
+
+ modify_request := ldap.NewModifyRequest(dn, nil)
+ modify_request.Replace(attr, []string{})
+
+ err := login.conn.Modify(modify_request)
+ if err != nil {
+ modifyError = err.Error()
+ }
+ }
+ }
+
+ // Build path
+ path := []PathItem{
+ PathItem{
+ DN: config.BaseDN,
+ Identifier: config.BaseDN,
+ Active: dn == config.BaseDN,
+ },
+ }
+
+ len_base_dn := len(strings.Split(config.BaseDN, ","))
+ dn_split := strings.Split(dn, ",")
+ dn_last_attr := strings.Split(dn_split[0], "=")[0]
+ for i := len_base_dn + 1; i <= len(dn_split); i++ {
+ path = append(path, PathItem{
+ DN: strings.Join(dn_split[len(dn_split)-i:len(dn_split)], ","),
+ Identifier: dn_split[len(dn_split)-i],
+ Active: i == len(dn_split),
+ })
+ }
+
+ // Get object and parse it
+ searchRequest := ldap.NewSearchRequest(
+ dn,
+ ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false,
+ fmt.Sprintf("(objectclass=*)"),
+ []string{},
+ nil)
+
+ sr, err := login.conn.Search(searchRequest)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ if len(sr.Entries) != 1 {
+ http.Error(w, fmt.Sprintf("%d objects found", len(sr.Entries)), http.StatusInternalServerError)
+ return
+ }
+
+ object := sr.Entries[0]
+
+ props := make(map[string]*PropValues)
+ for _, attr := range object.Attributes {
+ if attr.Name != dn_last_attr {
+ if existing, ok := props[attr.Name]; ok {
+ existing.Values = append(existing.Values, attr.Values...)
+ } else {
+ editable := true
+ for _, restricted := range []string{
+ "creatorsname", "modifiersname", "createtimestamp",
+ "modifytimestamp", "entryuuid",
+ } {
+ if strings.EqualFold(attr.Name, restricted) {
+ editable = false
+ break
+ }
+ }
+ pv := &PropValues{
+ Values: attr.Values,
+ Editable: editable,
+ }
+ if attr.Name == modifyAttr {
+ if modifySuccess {
+ pv.ModifySuccess = true
+ } else if modifyError != "" {
+ pv.ModifyError = modifyError
+ }
+ }
+ props[attr.Name] = pv
+ }
+ }
+ }
+
+ members := []string{}
+ if mp, ok := props["member"]; ok {
+ members = mp.Values
+ delete(props, "member")
+ }
+ groups := []string{}
+ if gp, ok := props["memberof"]; ok {
+ groups = gp.Values
+ delete(props, "memberof")
+ }
+
+ // Get children
+ searchRequest = ldap.NewSearchRequest(
+ dn,
+ ldap.ScopeSingleLevel, ldap.NeverDerefAliases, 0, 0, false,
+ fmt.Sprintf("(objectclass=*)"),
+ []string{"dn", "displayname"},
+ nil)
+
+ sr, err = login.conn.Search(searchRequest)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ sort.Sort(EntryList(sr.Entries))
+
+ children := []Child{}
+ for _, item := range sr.Entries {
+ children = append(children, Child{
+ DN: item.DN,
+ Identifier: strings.Split(item.DN, ",")[0],
+ DisplayName: item.GetAttributeValue("displayname"),
+ })
+ }
+
+ templateAdminLDAP.Execute(w, &AdminLDAPTplData{
+ DN: dn,
+ Members: members,
+ Groups: groups,
+ Props: props,
+ Children: children,
+ Path: path,
+ AddError: addError,
+ })
}