From eacc349cac1515398f5147d8b870f2959c22841b Mon Sep 17 00:00:00 2001 From: Arthur Lu Date: Sat, 5 Oct 2024 00:08:58 +0000 Subject: [PATCH] fix critical userPassword bug, improve ldap user/group data handling --- app/app.go | 24 ++++++----- app/ldap.go | 110 +++++++++++++++++++++------------------------------ app/utils.go | 80 ++++++++++++++++++++++++++++++++++++- 3 files changed, 137 insertions(+), 77 deletions(-) diff --git a/app/app.go b/app/app.go index 01752ac..892fe2d 100644 --- a/app/app.go +++ b/app/app.go @@ -15,7 +15,7 @@ import ( ) var LDAPSessions map[string]*LDAPClient -var APIVersion = "1.0.1" +var APIVersion = "1.0.2" func Run() { gob.Register(LDAPClient{}) @@ -122,18 +122,22 @@ func Run() { return } - var body User - if err := c.ShouldBind(&body); err != nil { // bad request from binding - c.JSON(http.StatusBadRequest, gin.H{"auth": false, "error": err.Error()}) - return - } - // check if user already exists status, res := LDAPSession.GetUser(c.Param("userid")) if status != 200 && ldap.IsErrorWithCode(res["error"].(error), ldap.LDAPResultNoSuchObject) { // user does not already exist, create new user + var body UserRequired // all user attributes required for new users + if err := c.ShouldBind(&body); err != nil { // attempt to bind user data + c.JSON(http.StatusBadRequest, gin.H{"auth": false, "error": err.Error()}) + return + } status, res = LDAPSession.AddUser(c.Param("userid"), body) c.JSON(status, res) } else { // user already exists, attempt to modify user + var body UserOptional // all user attributes optional for new users + if err := c.ShouldBind(&body); err != nil { // attempt to bind user data + c.JSON(http.StatusBadRequest, gin.H{"auth": false, "error": err.Error()}) + return + } status, res = LDAPSession.ModUser(c.Param("userid"), body) c.JSON(status, res) } @@ -231,12 +235,12 @@ func Run() { return } - // check if user already exists + // check if group already exists status, res := LDAPSession.GetGroup(c.Param("groupid")) - if status != 200 && ldap.IsErrorWithCode(res["error"].(error), ldap.LDAPResultNoSuchObject) { // user does not already exist, create new user + if status != 200 && ldap.IsErrorWithCode(res["error"].(error), ldap.LDAPResultNoSuchObject) { // group does not already exist, create new group status, res = LDAPSession.AddGroup(c.Param("groupid"), body) c.JSON(status, res) - } else { // user already exists, attempt to modify user + } else { // group already exists, attempt to modify group status, res = LDAPSession.ModGroup(c.Param("groupid"), body) c.JSON(status, res) } diff --git a/app/ldap.go b/app/ldap.go index 5173c41..adafb3c 100644 --- a/app/ldap.go +++ b/app/ldap.go @@ -8,6 +8,7 @@ import ( "github.com/go-ldap/ldap/v3" ) +// LDAPClient wrapper struct containing the connection, baseDN, peopleDN, and groupsDN type LDAPClient struct { client *ldap.Conn basedn string @@ -15,6 +16,7 @@ type LDAPClient struct { groupsdn string } +// returns a new LDAPClient from the config func NewLDAPClient(config Config) (*LDAPClient, error) { LDAPConn, err := ldap.DialURL(config.LdapURL) return &LDAPClient{ @@ -25,6 +27,7 @@ func NewLDAPClient(config Config) (*LDAPClient, error) { }, err } +// bind a user using username and password to the LDAPClient func (l LDAPClient) BindUser(username string, password string) error { userdn := fmt.Sprintf("uid=%s,%s", username, l.peopledn) return l.client.Bind(userdn, password) @@ -50,16 +53,8 @@ func (l LDAPClient) GetAllUsers() (int, gin.H) { var results = []gin.H{} // create list of results for _, entry := range searchResponse.Entries { // for each result, - results = append(results, gin.H{ - "dn": entry.DN, - "attributes": gin.H{ - "cn": entry.GetAttributeValue("cn"), - "sn": entry.GetAttributeValue("sn"), - "mail": entry.GetAttributeValue("mail"), - "uid": entry.GetAttributeValue("uid"), - "memberOf": entry.GetAttributeValues("memberOf"), - }, - }) + user := LDAPEntryToLDAPUser(entry) + results = append(results, LDAPUserToGin(user)) } return http.StatusOK, gin.H{ @@ -69,37 +64,6 @@ func (l LDAPClient) GetAllUsers() (int, gin.H) { } } -func (l LDAPClient) AddUser(uid string, user User) (int, gin.H) { - if user.CN == "" || user.SN == "" || user.UserPassword == "" { - return http.StatusBadRequest, gin.H{ - "ok": false, - "error": "Missing one of required fields: cn, sn, userpassword", - } - } - - addRequest := ldap.NewAddRequest( - fmt.Sprintf("uid=%s,%s", uid, l.peopledn), // DN - nil, // controls - ) - addRequest.Attribute("sn", []string{user.SN}) - addRequest.Attribute("cn", []string{user.CN}) - addRequest.Attribute("userPassword", []string{user.CN}) - addRequest.Attribute("objectClass", []string{"inetOrgPerson"}) - - err := l.client.Add(addRequest) - if err != nil { - return http.StatusBadRequest, gin.H{ - "ok": false, - "error": err, - } - } - - return http.StatusOK, gin.H{ - "ok": true, - "error": nil, - } -} - func (l LDAPClient) GetUser(uid string) (int, gin.H) { searchRequest := ldap.NewSearchRequest( // setup search for user by uid fmt.Sprintf("uid=%s,%s", uid, l.peopledn), // The base dn to search @@ -118,16 +82,9 @@ func (l LDAPClient) GetUser(uid string) (int, gin.H) { } entry := searchResponse.Entries[0] - result := gin.H{ - "dn": entry.DN, - "attributes": gin.H{ - "cn": entry.GetAttributeValue("cn"), - "sn": entry.GetAttributeValue("sn"), - "mail": entry.GetAttributeValue("mail"), - "uid": entry.GetAttributeValue("uid"), - "memberOf": entry.GetAttributeValues("memberOf"), - }, - } + + user := LDAPEntryToLDAPUser(entry) + result := LDAPUserToGin(user) return http.StatusOK, gin.H{ "ok": true, @@ -136,7 +93,38 @@ func (l LDAPClient) GetUser(uid string) (int, gin.H) { } } -func (l LDAPClient) ModUser(uid string, user User) (int, gin.H) { +func (l LDAPClient) AddUser(uid string, user UserRequired) (int, gin.H) { + if user.CN == "" || user.SN == "" || user.UserPassword == "" { + return http.StatusBadRequest, gin.H{ + "ok": false, + "error": "Missing one of required fields: cn, sn, userpassword", + } + } + + addRequest := ldap.NewAddRequest( + fmt.Sprintf("uid=%s,%s", uid, l.peopledn), // DN + nil, // controls + ) + addRequest.Attribute("sn", []string{user.SN}) + addRequest.Attribute("cn", []string{user.CN}) + addRequest.Attribute("userPassword", []string{user.UserPassword}) + addRequest.Attribute("objectClass", []string{"inetOrgPerson"}) + + err := l.client.Add(addRequest) + if err != nil { + return http.StatusBadRequest, gin.H{ + "ok": false, + "error": err, + } + } + + return http.StatusOK, gin.H{ + "ok": true, + "error": nil, + } +} + +func (l LDAPClient) ModUser(uid string, user UserOptional) (int, gin.H) { if user.CN == "" && user.SN == "" && user.UserPassword == "" { return http.StatusBadRequest, gin.H{ "ok": false, @@ -216,13 +204,8 @@ func (l LDAPClient) GetAllGroups() (int, gin.H) { var results = []gin.H{} // create list of results for _, entry := range searchResponse.Entries { // for each result, - results = append(results, gin.H{ - "dn": entry.DN, - "attributes": gin.H{ - "cn": entry.GetAttributeValue("cn"), - "member": entry.GetAttributeValues("member"), - }, - }) + group := LDAPEntryToLDAPGroup(entry) + results = append(results, LDAPGroupToGin(group)) } return http.StatusOK, gin.H{ @@ -250,13 +233,8 @@ func (l LDAPClient) GetGroup(gid string) (int, gin.H) { } entry := searchResponse.Entries[0] - result := gin.H{ - "dn": entry.DN, - "attributes": gin.H{ - "cn": entry.GetAttributeValue("cn"), - "member": entry.GetAttributeValues("member"), - }, - } + group := LDAPEntryToLDAPGroup(entry) + result := LDAPGroupToGin(group) return http.StatusOK, gin.H{ "ok": true, diff --git a/app/utils.go b/app/utils.go index 1898a9e..1bca7fb 100644 --- a/app/utils.go +++ b/app/utils.go @@ -4,6 +4,9 @@ import ( "encoding/json" "log" "os" + + "github.com/gin-gonic/gin" + "github.com/go-ldap/ldap/v3" ) type Config struct { @@ -38,11 +41,86 @@ type Login struct { // login body struct Password string `form:"password" binding:"required"` } -type User struct { // add or modify user body struct +type LDAPUserAttributes struct { + CN string + SN string + Mail string + UID string + MemberOf []string +} + +type LDAPUser struct { + DN string + Attributes LDAPUserAttributes +} + +func LDAPEntryToLDAPUser(entry *ldap.Entry) LDAPUser { + return LDAPUser{ + DN: entry.DN, + Attributes: LDAPUserAttributes{ + CN: entry.GetAttributeValue("cn"), + SN: entry.GetAttributeValue("sn"), + Mail: entry.GetAttributeValue("mail"), + UID: entry.GetAttributeValue("uid"), + MemberOf: entry.GetAttributeValues("memberOf"), + }, + } +} + +func LDAPUserToGin(user LDAPUser) gin.H { + return gin.H{ + "dn": user.DN, + "attributes": gin.H{ + "cn": user.Attributes.CN, + "sn": user.Attributes.SN, + "mail": user.Attributes.Mail, + "uid": user.Attributes.UID, + "memberOf": user.Attributes.MemberOf, + }, + } +} + +type LDAPGroupAttributes struct { + CN string + Member []string +} + +type LDAPGroup struct { + DN string + Attributes LDAPGroupAttributes +} + +func LDAPEntryToLDAPGroup(entry *ldap.Entry) LDAPGroup { + return LDAPGroup{ + DN: entry.DN, + Attributes: LDAPGroupAttributes{ + CN: entry.GetAttributeValue("cn"), + Member: entry.GetAttributeValues("member"), + }, + } +} + +func LDAPGroupToGin(group LDAPGroup) gin.H { + return gin.H{ + "dn": group.DN, + "attributes": gin.H{ + "cn": group.Attributes.CN, + "member": group.Attributes.Member, + }, + } +} + +type UserOptional struct { // add or modify user body struct CN string `form:"cn"` SN string `form:"sn"` UserPassword string `form:"userpassword"` } +type UserRequired struct { // add or modify user body struct + CN string `form:"cn" binding:"required"` + SN string `form:"sn" binding:"required"` + UserPassword string `form:"userpassword" binding:"required"` +} + type Group struct { // add or modify group body struct }