diff --git a/admin.go b/admin.go index 7806cfd..daf0267 100644 --- a/admin.go +++ b/admin.go @@ -18,7 +18,7 @@ func checkAdminLogin(w http.ResponseWriter, r *http.Request) *LoginStatus { return nil } - can_admin := false + can_admin := (login.Info.DN == config.AdminAccount) for _, group := range login.UserEntry.GetAttributeValues("memberof") { if config.GroupCanAdmin != "" && group == config.GroupCanAdmin { can_admin = true @@ -320,7 +320,7 @@ func handleAdminLDAP(w http.ResponseWriter, r *http.Request) { } if len(sr.Entries) != 1 { - http.Error(w, fmt.Sprintf("%d objects found", len(sr.Entries)), http.StatusInternalServerError) + http.Error(w, fmt.Sprintf("Object not found: %s", dn), http.StatusNotFound) return } @@ -509,6 +509,25 @@ func handleAdminCreate(w http.ResponseWriter, r *http.Request) { template := mux.Vars(r)["template"] super_dn := mux.Vars(r)["super_dn"] + // Check that base DN exists + searchRequest := ldap.NewSearchRequest( + super_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("Parent object %s does not exist", super_dn), http.StatusNotFound) + return + } + // Build path path := []PathItem{ PathItem{ @@ -541,6 +560,11 @@ func handleAdminCreate(w http.ResponseWriter, r *http.Request) { data.StructuralObjectClass = "groupOfNames" data.ObjectClass = "groupOfNames\ntop" data.IsTemplated = true + } else if template == "ou" { + data.IdType = "ou" + data.StructuralObjectClass = "organizationalUnit" + data.ObjectClass = "organizationalUnit\ntop" + data.IsTemplated = true } else { data.IdType = "cn" data.ObjectClass = "top" @@ -549,12 +573,12 @@ func handleAdminCreate(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { r.ParseForm() if !data.IsTemplated { - data.IdType = strings.Join(r.Form["idtype"], "") - data.StructuralObjectClass = strings.Join(r.Form["soc"], "") + data.IdType = strings.TrimSpace(strings.Join(r.Form["idtype"], "")) + data.StructuralObjectClass = strings.TrimSpace(strings.Join(r.Form["soc"], "")) data.ObjectClass = strings.Join(r.Form["oc"], "") } - data.IdValue = strings.Join(r.Form["idvalue"], "") - data.DisplayName = strings.Join(r.Form["displayname"], "") + data.IdValue = strings.TrimSpace(strings.Join(r.Form["idvalue"], "")) + data.DisplayName = strings.TrimSpace(strings.Join(r.Form["displayname"], "")) object_class := []string{} for _, oc := range strings.Split(data.ObjectClass, "\n") { @@ -578,7 +602,10 @@ func handleAdminCreate(w http.ResponseWriter, r *http.Request) { req.Attribute("objectClass", object_class) req.Attribute("structuralObjectClass", []string{data.StructuralObjectClass}) - req.Attribute("displayname", []string{data.DisplayName}) + if data.DisplayName != "" { + req.Attribute("displayname", []string{data.DisplayName}) + } + err := login.conn.Add(req) if err != nil { data.Error = err.Error() diff --git a/main.go b/main.go index a1070c8..677e7b2 100644 --- a/main.go +++ b/main.go @@ -31,6 +31,7 @@ type ConfigFile struct { GroupBaseDN string `json:"group_base_dn"` GroupNameAttr string `json:"group_name_attr"` + AdminAccount string `json:"admin_account"` GroupCanInvite string `json:"group_can_invite"` GroupCanAdmin string `json:"group_can_admin"` } @@ -60,6 +61,7 @@ func readConfig() ConfigFile { UserNameAttr: "uid", GroupBaseDN: "ou=groups,dc=example,dc=com", GroupNameAttr: "gid", + AdminAccount: "uid=admin,dc=example,dc=com", GroupCanInvite: "", GroupCanAdmin: "gid=admin,ou=groups,dc=example,dc=com", } @@ -120,6 +122,7 @@ func main() { staticfiles := http.FileServer(http.Dir("static")) r.Handle("/static/{file:.*}", http.StripPrefix("/static/", staticfiles)) + log.Printf("Starting HTTP server on %s", config.HttpBindAddr) err := http.ListenAndServe(config.HttpBindAddr, logRequest(r)) if err != nil { log.Fatal("Cannot start http server: ", err) @@ -189,10 +192,19 @@ func checkLogin(w http.ResponseWriter, r *http.Request) *LoginStatus { return checkLogin(w, r) } + loginStatus := &LoginStatus{ + Info: login_info, + conn: l, + } + + requestKind := "(objectClass=organizationalPerson)" + if login_info.DN == config.AdminAccount { + requestKind = "(objectclass=*)" + } searchRequest := ldap.NewSearchRequest( login_info.DN, ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(objectClass=organizationalPerson))"), + requestKind, []string{"dn", "displayname", "givenname", "sn", "mail", "memberof"}, nil) @@ -203,15 +215,13 @@ func checkLogin(w http.ResponseWriter, r *http.Request) *LoginStatus { } if len(sr.Entries) != 1 { - http.Error(w, fmt.Sprintf("Multiple entries: %#v", sr.Entries), http.StatusInternalServerError) + http.Error(w, fmt.Sprintf("Unable to find entry for %s", login_info.DN), http.StatusInternalServerError) return nil } - return &LoginStatus{ - Info: login_info, - conn: l, - UserEntry: sr.Entries[0], - } + loginStatus.UserEntry = sr.Entries[0] + + return loginStatus } func ldapOpen(w http.ResponseWriter) *ldap.Conn { @@ -236,6 +246,7 @@ func ldapOpen(w http.ResponseWriter) *ldap.Conn { type HomePageData struct { Login *LoginStatus + WelcomeName string CanAdmin bool CanInvite bool BaseDN string @@ -249,7 +260,7 @@ func handleHome(w http.ResponseWriter, r *http.Request) { return } - can_admin := false + can_admin := (login.Info.DN == config.AdminAccount) can_invite := false for _, group := range login.UserEntry.GetAttributeValues("memberof") { if config.GroupCanInvite != "" && group == config.GroupCanInvite { @@ -260,12 +271,21 @@ func handleHome(w http.ResponseWriter, r *http.Request) { } } - templateHome.Execute(w, &HomePageData{ + data := &HomePageData{ Login: login, CanAdmin: can_admin, CanInvite: can_invite, BaseDN: config.BaseDN, - }) + WelcomeName: login.UserEntry.GetAttributeValue("givenname"), + } + if data.WelcomeName == "" { + data.WelcomeName = login.UserEntry.GetAttributeValue("displayname") + } + if data.WelcomeName == "" { + data.WelcomeName = login.Info.Username + } + + templateHome.Execute(w, data) } func handleLogout(w http.ResponseWriter, r *http.Request) { @@ -305,6 +325,9 @@ func handleLogin(w http.ResponseWriter, r *http.Request) *LoginInfo { username := strings.Join(r.Form["username"], "") password := strings.Join(r.Form["password"], "") user_dn := fmt.Sprintf("%s=%s,%s", config.UserNameAttr, username, config.UserBaseDN) + if username == config.AdminAccount { + user_dn = username + } l := ldapOpen(w) if l == nil { diff --git a/profile.go b/profile.go index fa372bc..e5da091 100644 --- a/profile.go +++ b/profile.go @@ -35,10 +35,10 @@ func handleProfile(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { r.ParseForm() - data.Mail = strings.Join(r.Form["mail"], "") - data.DisplayName = strings.Join(r.Form["display_name"], "") - data.GivenName = strings.Join(r.Form["given_name"], "") - data.Surname = strings.Join(r.Form["surname"], "") + data.Mail = strings.TrimSpace(strings.Join(r.Form["mail"], "")) + data.DisplayName = strings.TrimSpace(strings.Join(r.Form["display_name"], "")) + data.GivenName = strings.TrimSpace(strings.Join(r.Form["given_name"], "")) + data.Surname = strings.TrimSpace(strings.Join(r.Form["surname"], "")) modify_request := ldap.NewModifyRequest(login.Info.DN, nil) modify_request.Replace("mail", []string{data.Mail}) diff --git a/templates/admin_ldap.html b/templates/admin_ldap.html index c7a0872..06d568c 100644 --- a/templates/admin_ldap.html +++ b/templates/admin_ldap.html @@ -40,6 +40,7 @@
+utilisateur +groupe + +ou +objet

diff --git a/templates/home.html b/templates/home.html index 011dcc1..9a9773a 100644 --- a/templates/home.html +++ b/templates/home.html @@ -2,7 +2,7 @@ {{define "body"}}
- Bienvenue, {{ .Login.UserEntry.GetAttributeValue "givenname" }} ! + Bienvenue, {{ .WelcomeName }} !
Se déconnecter