implement better ldap tls handling, fix naming for groupnames and usernames in path params

This commit is contained in:
2026-05-31 02:43:28 +00:00
parent d3462723df
commit b6fd060daf
3 changed files with 86 additions and 62 deletions
+3 -1
View File
@@ -19,8 +19,10 @@ type PVEConfig struct {
type LDAPConfig struct { type LDAPConfig struct {
BaseDN string `json:""` BaseDN string `json:""`
LdapURL string Hostname string
StartTLS bool StartTLS bool
TLS bool
Verify bool
} }
type Config struct { type Config struct {
+33 -13
View File
@@ -19,30 +19,50 @@ type LDAPClient struct {
// returns a new LDAPClient from the config // returns a new LDAPClient from the config
func NewClientFromCredentials(config common.LDAPConfig, username common.Username, password string) (*LDAPClient, int, error) { func NewClientFromCredentials(config common.LDAPConfig, username common.Username, password string) (*LDAPClient, int, error) {
LDAPConn, err := ldap.DialURL(config.LdapURL) ldapclient := LDAPClient{}
if config.TLS {
tlsConfig := &tls.Config{
InsecureSkipVerify: !config.Verify,
}
url := fmt.Sprintf("ldaps://%s", config.Hostname)
LDAPConn, err := ldap.DialURL(url, ldap.DialWithTLSConfig(tlsConfig))
if err != nil { if err != nil {
return nil, http.StatusInternalServerError, err return nil, http.StatusInternalServerError, err
} }
ldapclient.config = &config
if config.StartTLS { ldapclient.client = LDAPConn
err = LDAPConn.StartTLS(&tls.Config{}) } else if config.StartTLS {
tlsConfig := &tls.Config{
InsecureSkipVerify: !config.Verify,
}
url := fmt.Sprintf("ldap://%s", config.Hostname)
LDAPConn, err := ldap.DialURL(url)
if err != nil { if err != nil {
return nil, http.StatusInternalServerError, err return nil, http.StatusInternalServerError, err
} }
err = LDAPConn.StartTLS(tlsConfig)
if err != nil {
return nil, http.StatusInternalServerError, err
}
ldapclient.config = &config
ldapclient.client = LDAPConn
} else {
url := fmt.Sprintf("ldap://%s", config.Hostname)
LDAPConn, err := ldap.DialURL(url)
if err != nil {
return nil, http.StatusInternalServerError, err
}
ldapclient.config = &config
ldapclient.client = LDAPConn
} }
ldap := LDAPClient{ userdn := fmt.Sprintf("uid=%s,ou=people,%s", username.UserID, ldapclient.config.BaseDN)
config: &config, err := ldapclient.client.Bind(userdn, password)
client: LDAPConn,
}
userdn := fmt.Sprintf("uid=%s,ou=people,%s", username.UserID, ldap.config.BaseDN)
err = ldap.client.Bind(userdn, password)
if err != nil { if err != nil {
return nil, http.StatusUnauthorized, err return nil, http.StatusUnauthorized, err
} else { } else {
return &ldap, http.StatusOK, nil return &ldapclient, http.StatusOK, nil
} }
} }
+48 -46
View File
@@ -199,14 +199,14 @@ func Run(configPath *string, localDBPath *string) {
} }
}) })
router.GET("/groups/:groupid", func(c *gin.Context) { router.GET("/groups/:groupname", func(c *gin.Context) {
groupid, ok := c.Params.Get("groupid") groupname_str, ok := c.Params.Get("groupname")
if !ok { if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter poolid")}) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter poolid")})
return return
} }
groupname, err := common.ParseGroupname(groupid) groupname, err := common.ParseGroupname(groupname_str)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
} }
@@ -225,14 +225,14 @@ func Run(configPath *string, localDBPath *string) {
} }
}) })
router.POST("/groups/:groupid", func(c *gin.Context) { router.POST("/groups/:groupname", func(c *gin.Context) {
groupid, ok := c.Params.Get("groupid") groupname_str, ok := c.Params.Get("groupname")
if !ok { if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter groupid")}) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter groupname")})
return return
} }
groupname, err := paas.ParseGroupname(groupid) groupname, err := paas.ParseGroupname(groupname_str)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err}) c.JSON(http.StatusBadRequest, gin.H{"error": err})
return return
@@ -252,14 +252,14 @@ func Run(configPath *string, localDBPath *string) {
} }
}) })
router.DELETE("/groups/:groupid", func(c *gin.Context) { router.DELETE("/groups/:groupname", func(c *gin.Context) {
groupid, ok := c.Params.Get("groupid") groupname_str, ok := c.Params.Get("groupname")
if !ok { if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter groupid")}) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter groupname")})
return return
} }
groupname, err := paas.ParseGroupname(groupid) groupname, err := paas.ParseGroupname(groupname_str)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err}) c.JSON(http.StatusBadRequest, gin.H{"error": err})
return return
@@ -279,20 +279,20 @@ func Run(configPath *string, localDBPath *string) {
} }
}) })
router.POST("/pools/:poolid/groups/:groupid", func(c *gin.Context) { router.POST("/pools/:poolid/groups/:groupname", func(c *gin.Context) {
poolid, ok := c.Params.Get("poolid") poolid, ok := c.Params.Get("poolid")
if !ok { if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter poolid")}) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter poolid")})
return return
} }
groupid, ok := c.Params.Get("groupid") groupname_str, ok := c.Params.Get("groupname")
if !ok { if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter groupid")}) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter groupname")})
return return
} }
groupname, err := paas.ParseGroupname(groupid) groupname, err := paas.ParseGroupname(groupname_str)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err}) c.JSON(http.StatusBadRequest, gin.H{"error": err})
return return
@@ -312,20 +312,20 @@ func Run(configPath *string, localDBPath *string) {
} }
}) })
router.DELETE("/pools/:poolid/groups/:groupid", func(c *gin.Context) { router.DELETE("/pools/:poolid/groups/:groupname", func(c *gin.Context) {
poolid, ok := c.Params.Get("poolid") poolid, ok := c.Params.Get("poolid")
if !ok { if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter poolid")}) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter poolid")})
return return
} }
groupid, ok := c.Params.Get("groupid") groupname_str, ok := c.Params.Get("groupname")
if !ok { if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter groupid")}) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter groupname")})
return return
} }
groupname, err := paas.ParseGroupname(groupid) groupname, err := paas.ParseGroupname(groupname_str)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err}) c.JSON(http.StatusBadRequest, gin.H{"error": err})
return return
@@ -345,14 +345,14 @@ func Run(configPath *string, localDBPath *string) {
} }
}) })
router.GET("/users/:userid", func(c *gin.Context) { router.GET("/users/:username", func(c *gin.Context) {
userid, ok := c.Params.Get("userid") username_str, ok := c.Params.Get("username")
if !ok { if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter poolid")}) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter poolid")})
return return
} }
username, err := common.ParseUsername(userid) username, err := common.ParseUsername(username_str)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
} }
@@ -371,14 +371,14 @@ func Run(configPath *string, localDBPath *string) {
} }
}) })
router.POST("/users/:userid", func(c *gin.Context) { router.POST("/users/:username", func(c *gin.Context) {
userid, ok := c.Params.Get("userid") username_str, ok := c.Params.Get("username")
if !ok { if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter groupid")}) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter groupname")})
return return
} }
username, err := paas.ParseUsername(userid) username, err := paas.ParseUsername(username_str)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err}) c.JSON(http.StatusBadRequest, gin.H{"error": err})
return return
@@ -411,14 +411,14 @@ func Run(configPath *string, localDBPath *string) {
} }
}) })
router.DELETE("/users/:userid", func(c *gin.Context) { router.DELETE("/users/:username", func(c *gin.Context) {
userid, ok := c.Params.Get("userid") username_str, ok := c.Params.Get("username")
if !ok { if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter groupid")}) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter groupname")})
return return
} }
username, err := paas.ParseUsername(userid) username, err := paas.ParseUsername(username_str)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err}) c.JSON(http.StatusBadRequest, gin.H{"error": err})
return return
@@ -438,26 +438,26 @@ func Run(configPath *string, localDBPath *string) {
} }
}) })
router.POST("/groups/:groupid/users/:userid", func(c *gin.Context) { router.POST("/groups/:groupname/users/:username", func(c *gin.Context) {
groupid, ok := c.Params.Get("groupid") groupname_str, ok := c.Params.Get("groupname")
if !ok { if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter groupid")}) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter groupname")})
return return
} }
userid, ok := c.Params.Get("userid") username_str, ok := c.Params.Get("username")
if !ok { if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter userid")}) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter username")})
return return
} }
groupname, err := paas.ParseGroupname(groupid) groupname, err := paas.ParseGroupname(groupname_str)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err}) c.JSON(http.StatusBadRequest, gin.H{"error": err})
return return
} }
username, err := paas.ParseUsername(userid) username, err := paas.ParseUsername(username_str)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err}) c.JSON(http.StatusBadRequest, gin.H{"error": err})
return return
@@ -477,26 +477,26 @@ func Run(configPath *string, localDBPath *string) {
} }
}) })
router.DELETE("/groups/:groupid/users/:userid", func(c *gin.Context) { router.DELETE("/groups/:groupname/users/:username", func(c *gin.Context) {
groupid, ok := c.Params.Get("groupid") groupname_str, ok := c.Params.Get("groupname")
if !ok { if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter groupid")}) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter groupname")})
return return
} }
userid, ok := c.Params.Get("userid") username_str, ok := c.Params.Get("username")
if !ok { if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter userid")}) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("Missing required path parameter username")})
return return
} }
groupname, err := paas.ParseGroupname(groupid) groupname, err := paas.ParseGroupname(groupname_str)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err}) c.JSON(http.StatusBadRequest, gin.H{"error": err})
return return
} }
username, err := paas.ParseUsername(userid) username, err := paas.ParseUsername(username_str)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err}) c.JSON(http.StatusBadRequest, gin.H{"error": err})
return return
@@ -585,8 +585,10 @@ func GetRealmsFromPVE(config *common.Config) map[string]Realm {
if realm.Type == "ldap" { if realm.Type == "ldap" {
ldapconfig := common.LDAPConfig{ ldapconfig := common.LDAPConfig{
BaseDN: realm.BaseDN, BaseDN: realm.BaseDN,
LdapURL: fmt.Sprintf("ldap://%s", realm.Server1), Hostname: realm.Server1,
StartTLS: false, // todo fix startlts TLS: realm.Mode == "ldaps",
StartTLS: realm.Mode == "ldap+starttls",
Verify: bool(realm.Verify),
} }
realms[realm.Realm] = Realm{ realms[realm.Realm] = Realm{
Type: realm.Type, Type: realm.Type,