Merge pull request 'Rewrite API in GO' (#1) from go-rewrite into main
Reviewed-on: #1
This commit is contained in:
commit
eea5b8599e
@ -1,42 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"es2021": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": "standard",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"no-tabs": [
|
||||
"error",
|
||||
{
|
||||
"allowIndentationTabs": true
|
||||
}
|
||||
],
|
||||
"indent": [
|
||||
"error",
|
||||
"tab"
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"double"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"brace-style": [
|
||||
"error",
|
||||
"stroustrup",
|
||||
{
|
||||
"allowSingleLine": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,3 +1,3 @@
|
||||
**/package-lock.json
|
||||
**/node_modules
|
||||
**/config.json
|
||||
**/go.sum
|
||||
**/config.json
|
||||
dist/*
|
9
Makefile
Normal file
9
Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
build: clean
|
||||
go build -ldflags="-s -w" -o dist/ .
|
||||
|
||||
test: clean
|
||||
go run .
|
||||
|
||||
clean:
|
||||
go clean
|
||||
rm -f dist/*
|
295
app/app.go
Normal file
295
app/app.go
Normal file
@ -0,0 +1,295 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"flag"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-contrib/sessions/cookie"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
uuid "github.com/nu7hatch/gouuid"
|
||||
)
|
||||
|
||||
var LDAPSessions map[string]*LDAPClient
|
||||
|
||||
func Run() {
|
||||
gob.Register(LDAPClient{})
|
||||
|
||||
configPath := flag.String("config", "config.json", "path to config.json file")
|
||||
flag.Parse()
|
||||
|
||||
config := GetConfig(*configPath)
|
||||
log.Println("Initialized config from " + *configPath)
|
||||
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
router := gin.Default()
|
||||
store := cookie.NewStore([]byte(config.SessionSecretKey))
|
||||
store.Options(sessions.Options{
|
||||
Path: config.SessionCookie.Path,
|
||||
HttpOnly: config.SessionCookie.HttpOnly,
|
||||
Secure: config.SessionCookie.Secure,
|
||||
MaxAge: config.SessionCookie.MaxAge,
|
||||
})
|
||||
router.Use(sessions.Sessions(config.SessionCookieName, store))
|
||||
|
||||
LDAPSessions = make(map[string]*LDAPClient)
|
||||
|
||||
router.POST("/ticket", func(c *gin.Context) {
|
||||
var body Login
|
||||
if err := c.ShouldBind(&body); err != nil { // bad request from binding
|
||||
c.JSON(http.StatusBadRequest, gin.H{"auth": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
newLDAPClient, err := NewLDAPClient(config)
|
||||
if err != nil { // failed to dial ldap server, considered a server error
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"auth": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
err = newLDAPClient.BindUser(body.Username, body.Password)
|
||||
if err != nil { // failed to authenticate, return error
|
||||
c.JSON(http.StatusBadRequest, gin.H{"auth": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// successful binding at this point
|
||||
// create new session
|
||||
session := sessions.Default(c)
|
||||
// create (hopefully) safe uuid to map to ldap session
|
||||
uuid, _ := uuid.NewV4()
|
||||
// set uuid mapping in session
|
||||
session.Set("SessionUUID", uuid.String())
|
||||
// set uuid mapping in LDAPSessions
|
||||
LDAPSessions[uuid.String()] = newLDAPClient
|
||||
// save the session
|
||||
session.Save()
|
||||
// return successful auth
|
||||
c.JSON(http.StatusOK, gin.H{"auth": true})
|
||||
})
|
||||
|
||||
router.DELETE("/ticket", func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
SessionUUID := session.Get("SessionUUID")
|
||||
if SessionUUID == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
uuid := SessionUUID.(string)
|
||||
delete(LDAPSessions, uuid)
|
||||
session.Options(sessions.Options{MaxAge: -1}) // set max age to -1 so it is deleted
|
||||
_ = session.Save()
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
})
|
||||
|
||||
router.GET("/users", func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
SessionUUID := session.Get("SessionUUID")
|
||||
if SessionUUID == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
uuid := SessionUUID.(string)
|
||||
LDAPSession := LDAPSessions[uuid]
|
||||
if LDAPSession == nil { // does not have registered ldap session associated with cookie session
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
|
||||
status, res := LDAPSession.GetAllUsers()
|
||||
c.JSON(status, res)
|
||||
})
|
||||
|
||||
router.POST("/users/:userid", func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
SessionUUID := session.Get("SessionUUID")
|
||||
if SessionUUID == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
uuid := SessionUUID.(string)
|
||||
LDAPSession := LDAPSessions[uuid]
|
||||
if LDAPSession == nil { // does not have registered ldap session associated with cookie session
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
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
|
||||
status, res = LDAPSession.AddUser(c.Param("userid"), body)
|
||||
c.JSON(status, res)
|
||||
} else { // user already exists, attempt to modify user
|
||||
status, res = LDAPSession.ModUser(c.Param("userid"), body)
|
||||
c.JSON(status, res)
|
||||
}
|
||||
})
|
||||
|
||||
router.GET("/users/:userid", func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
SessionUUID := session.Get("SessionUUID")
|
||||
if SessionUUID == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
uuid := SessionUUID.(string)
|
||||
LDAPSession := LDAPSessions[uuid]
|
||||
if LDAPSession == nil { // does not have registered ldap session associated with cookie session
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
|
||||
status, res := LDAPSession.GetUser(c.Param("userid"))
|
||||
c.JSON(status, res)
|
||||
})
|
||||
|
||||
router.DELETE("/users/:userid", func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
SessionUUID := session.Get("SessionUUID")
|
||||
if SessionUUID == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
uuid := SessionUUID.(string)
|
||||
LDAPSession := LDAPSessions[uuid]
|
||||
if LDAPSession == nil { // does not have registered ldap session associated with cookie session
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
|
||||
status, res := LDAPSession.DelUser(c.Param("userid"))
|
||||
c.JSON(status, res)
|
||||
})
|
||||
|
||||
router.GET("/groups", func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
SessionUUID := session.Get("SessionUUID")
|
||||
if SessionUUID == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
uuid := SessionUUID.(string)
|
||||
LDAPSession := LDAPSessions[uuid]
|
||||
if LDAPSession == nil { // does not have registered ldap session associated with cookie session
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
|
||||
status, res := LDAPSession.GetAllGroups()
|
||||
c.JSON(status, res)
|
||||
})
|
||||
|
||||
router.GET("/groups/:groupid", func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
SessionUUID := session.Get("SessionUUID")
|
||||
if SessionUUID == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
uuid := SessionUUID.(string)
|
||||
LDAPSession := LDAPSessions[uuid]
|
||||
if LDAPSession == nil { // does not have registered ldap session associated with cookie session
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
|
||||
status, res := LDAPSession.GetGroup(c.Param("groupid"))
|
||||
c.JSON(status, res)
|
||||
})
|
||||
|
||||
router.POST("/groups/:groupid", func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
SessionUUID := session.Get("SessionUUID")
|
||||
if SessionUUID == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
uuid := SessionUUID.(string)
|
||||
LDAPSession := LDAPSessions[uuid]
|
||||
if LDAPSession == nil { // does not have registered ldap session associated with cookie session
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
|
||||
var body Group
|
||||
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.GetGroup(c.Param("groupid"))
|
||||
if status != 200 && ldap.IsErrorWithCode(res["error"].(error), ldap.LDAPResultNoSuchObject) { // user does not already exist, create new user
|
||||
status, res = LDAPSession.AddGroup(c.Param("groupid"), body)
|
||||
c.JSON(status, res)
|
||||
} else { // user already exists, attempt to modify user
|
||||
status, res = LDAPSession.ModGroup(c.Param("groupid"), body)
|
||||
c.JSON(status, res)
|
||||
}
|
||||
})
|
||||
|
||||
router.DELETE("/groups/:groupid", func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
SessionUUID := session.Get("SessionUUID")
|
||||
if SessionUUID == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
uuid := SessionUUID.(string)
|
||||
LDAPSession := LDAPSessions[uuid]
|
||||
if LDAPSession == nil { // does not have registered ldap session associated with cookie session
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
|
||||
status, res := LDAPSession.DelGroup(c.Param("groupid"))
|
||||
c.JSON(status, res)
|
||||
})
|
||||
|
||||
router.POST("/groups/:groupid/members/:userid", func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
SessionUUID := session.Get("SessionUUID")
|
||||
if SessionUUID == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
uuid := SessionUUID.(string)
|
||||
LDAPSession := LDAPSessions[uuid]
|
||||
if LDAPSession == nil { // does not have registered ldap session associated with cookie session
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
|
||||
status, res := LDAPSession.AddUserToGroup(c.Param("userid"), c.Param("groupid"))
|
||||
c.JSON(status, res)
|
||||
})
|
||||
|
||||
router.DELETE("/groups/:groupid/members/:userid", func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
SessionUUID := session.Get("SessionUUID")
|
||||
if SessionUUID == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
uuid := SessionUUID.(string)
|
||||
LDAPSession := LDAPSessions[uuid]
|
||||
if LDAPSession == nil { // does not have registered ldap session associated with cookie session
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||
return
|
||||
}
|
||||
|
||||
status, res := LDAPSession.DelUserFromGroup(c.Param("userid"), c.Param("groupid"))
|
||||
c.JSON(status, res)
|
||||
})
|
||||
|
||||
router.Run("0.0.0.0:" + strconv.Itoa(config.ListenPort))
|
||||
}
|
368
app/ldap.go
Normal file
368
app/ldap.go
Normal file
@ -0,0 +1,368 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
)
|
||||
|
||||
type LDAPClient struct {
|
||||
client *ldap.Conn
|
||||
basedn string
|
||||
peopledn string
|
||||
groupsdn string
|
||||
}
|
||||
|
||||
func NewLDAPClient(config Config) (*LDAPClient, error) {
|
||||
LDAPConn, err := ldap.DialURL(config.LdapURL)
|
||||
return &LDAPClient{
|
||||
client: LDAPConn,
|
||||
basedn: config.BaseDN,
|
||||
peopledn: "ou=people," + config.BaseDN,
|
||||
groupsdn: "ou=groups," + config.BaseDN,
|
||||
}, err
|
||||
}
|
||||
|
||||
func (l LDAPClient) BindUser(username string, password string) error {
|
||||
userdn := fmt.Sprintf("uid=%s,%s", username, l.peopledn)
|
||||
return l.client.Bind(userdn, password)
|
||||
}
|
||||
|
||||
func (l LDAPClient) GetAllUsers() (int, gin.H) {
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
l.peopledn, // The base dn to search
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
"(&(objectClass=inetOrgPerson))", // The filter to apply
|
||||
[]string{"dn", "cn", "sn", "mail", "uid"}, // A list attributes to retrieve
|
||||
nil,
|
||||
)
|
||||
|
||||
searchResponse, err := l.client.Search(searchRequest) // perform search
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, gin.H{
|
||||
"ok": false,
|
||||
"error": err,
|
||||
}
|
||||
}
|
||||
|
||||
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"),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return http.StatusOK, gin.H{
|
||||
"ok": true,
|
||||
"error": nil,
|
||||
"users": results,
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
"(&(objectClass=inetOrgPerson))", // The filter to apply
|
||||
[]string{"dn", "cn", "sn", "mail", "uid"}, // A list attributes to retrieve
|
||||
nil,
|
||||
)
|
||||
|
||||
searchResponse, err := l.client.Search(searchRequest) // perform search
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, gin.H{
|
||||
"ok": false,
|
||||
"error": err,
|
||||
}
|
||||
}
|
||||
|
||||
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"),
|
||||
},
|
||||
}
|
||||
|
||||
return http.StatusOK, gin.H{
|
||||
"ok": true,
|
||||
"error": nil,
|
||||
"user": result,
|
||||
}
|
||||
}
|
||||
|
||||
func (l LDAPClient) ModUser(uid string, user User) (int, gin.H) {
|
||||
if user.CN == "" && user.SN == "" && user.UserPassword == "" {
|
||||
return http.StatusBadRequest, gin.H{
|
||||
"ok": false,
|
||||
"error": "Requires one of fields: cn, sn, userpassword",
|
||||
}
|
||||
}
|
||||
|
||||
modifyRequest := ldap.NewModifyRequest(
|
||||
fmt.Sprintf("uid=%s,%s", uid, l.peopledn),
|
||||
nil,
|
||||
)
|
||||
if user.CN != "" {
|
||||
modifyRequest.Replace("cn", []string{user.CN})
|
||||
}
|
||||
if user.SN != "" {
|
||||
modifyRequest.Replace("sn", []string{user.SN})
|
||||
}
|
||||
if user.UserPassword != "" {
|
||||
modifyRequest.Replace("userPassword", []string{user.UserPassword})
|
||||
}
|
||||
|
||||
err := l.client.Modify(modifyRequest)
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, gin.H{
|
||||
"ok": false,
|
||||
"error": err,
|
||||
}
|
||||
}
|
||||
|
||||
return http.StatusOK, gin.H{
|
||||
"ok": true,
|
||||
"error": nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (l LDAPClient) DelUser(uid string) (int, gin.H) {
|
||||
userDN := fmt.Sprintf("uid=%s,%s", uid, l.peopledn)
|
||||
|
||||
// assumes that olcMemberOfRefint=true updates member attributes of referenced groups
|
||||
|
||||
deleteUserRequest := ldap.NewDelRequest( // setup delete request
|
||||
userDN,
|
||||
nil,
|
||||
)
|
||||
|
||||
err := l.client.Del(deleteUserRequest) // delete user
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, gin.H{
|
||||
"ok": false,
|
||||
"error": err,
|
||||
}
|
||||
}
|
||||
|
||||
return http.StatusOK, gin.H{
|
||||
"ok": true,
|
||||
"error": nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (l LDAPClient) GetAllGroups() (int, gin.H) {
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
l.groupsdn, // The base dn to search
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
"(&(objectClass=groupOfNames))", // The filter to apply
|
||||
[]string{"cn", "member"}, // A list attributes to retrieve
|
||||
nil,
|
||||
)
|
||||
|
||||
searchResponse, err := l.client.Search(searchRequest) // perform search
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, gin.H{
|
||||
"ok": false,
|
||||
"error": err,
|
||||
}
|
||||
}
|
||||
|
||||
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"),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return http.StatusOK, gin.H{
|
||||
"ok": true,
|
||||
"error": nil,
|
||||
"groups": results,
|
||||
}
|
||||
}
|
||||
|
||||
func (l LDAPClient) GetGroup(gid string) (int, gin.H) {
|
||||
searchRequest := ldap.NewSearchRequest( // setup search for user by uid
|
||||
fmt.Sprintf("cn=%s,%s", gid, l.groupsdn), // The base dn to search
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
"(&(objectClass=groupOfNames))", // The filter to apply
|
||||
[]string{"cn", "member"}, // A list attributes to retrieve
|
||||
nil,
|
||||
)
|
||||
|
||||
searchResponse, err := l.client.Search(searchRequest) // perform search
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, gin.H{
|
||||
"ok": false,
|
||||
"error": err,
|
||||
}
|
||||
}
|
||||
|
||||
entry := searchResponse.Entries[0]
|
||||
result := gin.H{
|
||||
"dn": entry.DN,
|
||||
"attributes": gin.H{
|
||||
"cn": entry.GetAttributeValue("cn"),
|
||||
"member": entry.GetAttributeValues("member"),
|
||||
},
|
||||
}
|
||||
|
||||
return http.StatusOK, gin.H{
|
||||
"ok": true,
|
||||
"error": nil,
|
||||
"group": result,
|
||||
}
|
||||
}
|
||||
|
||||
func (l LDAPClient) AddGroup(gid string, group Group) (int, gin.H) {
|
||||
addRequest := ldap.NewAddRequest(
|
||||
fmt.Sprintf("cn=%s,%s", gid, l.groupsdn), // DN
|
||||
nil, // controls
|
||||
)
|
||||
addRequest.Attribute("cn", []string{gid})
|
||||
addRequest.Attribute("member", []string{""})
|
||||
addRequest.Attribute("objectClass", []string{"groupOfNames"})
|
||||
|
||||
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) ModGroup(gid string, group Group) (int, gin.H) {
|
||||
return 200, gin.H{
|
||||
"ok": true,
|
||||
"error": nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (l LDAPClient) DelGroup(gid string) (int, gin.H) {
|
||||
groupDN := fmt.Sprintf("cn=%s,%s", gid, l.groupsdn)
|
||||
|
||||
// assumes that memberOf overlay will automatically update referenced memberOf attributes
|
||||
|
||||
deleteGroupRequest := ldap.NewDelRequest( // setup delete request
|
||||
groupDN,
|
||||
nil,
|
||||
)
|
||||
|
||||
err := l.client.Del(deleteGroupRequest) // delete group
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, gin.H{
|
||||
"ok": false,
|
||||
"error": err,
|
||||
}
|
||||
}
|
||||
|
||||
return http.StatusOK, gin.H{
|
||||
"ok": true,
|
||||
"error": nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (l LDAPClient) AddUserToGroup(uid string, gid string) (int, gin.H) {
|
||||
userDN := fmt.Sprintf("uid=%s,%s", uid, l.peopledn)
|
||||
groupDN := fmt.Sprintf("cn=%s,%s", gid, l.groupsdn)
|
||||
|
||||
modifyRequest := ldap.NewModifyRequest( // modify group member value
|
||||
groupDN,
|
||||
nil,
|
||||
)
|
||||
|
||||
modifyRequest.Add("member", []string{userDN}) // add user to group member attribute
|
||||
|
||||
err := l.client.Modify(modifyRequest) // modify group
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, gin.H{
|
||||
"ok": false,
|
||||
"error": err,
|
||||
}
|
||||
}
|
||||
|
||||
return http.StatusOK, gin.H{
|
||||
"ok": true,
|
||||
"error": nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (l LDAPClient) DelUserFromGroup(uid string, gid string) (int, gin.H) {
|
||||
userDN := fmt.Sprintf("uid=%s,%s", uid, l.peopledn)
|
||||
groupDN := fmt.Sprintf("cn=%s,%s", gid, l.groupsdn)
|
||||
|
||||
modifyRequest := ldap.NewModifyRequest( // modify group member value
|
||||
groupDN,
|
||||
nil,
|
||||
)
|
||||
|
||||
modifyRequest.Delete("member", []string{userDN}) // remove user from group member attribute
|
||||
|
||||
err := l.client.Modify(modifyRequest) // modify group
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, gin.H{
|
||||
"ok": false,
|
||||
"error": err,
|
||||
}
|
||||
}
|
||||
|
||||
return http.StatusOK, gin.H{
|
||||
"ok": true,
|
||||
"error": nil,
|
||||
}
|
||||
}
|
48
app/utils.go
Normal file
48
app/utils.go
Normal file
@ -0,0 +1,48 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ListenPort int `json:"listenPort"`
|
||||
LdapURL string `json:"ldapURL"`
|
||||
BaseDN string `json:"baseDN"`
|
||||
SessionSecretKey string `json:"sessionSecretKey"`
|
||||
SessionCookieName string `json:"sessionCookieName"`
|
||||
SessionCookie struct {
|
||||
Path string `json:"path"`
|
||||
HttpOnly bool `json:"httpOnly"`
|
||||
Secure bool `json:"secure"`
|
||||
MaxAge int `json:"maxAge"`
|
||||
}
|
||||
}
|
||||
|
||||
func GetConfig(configPath string) Config {
|
||||
content, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
log.Fatal("Error when opening config file: ", err)
|
||||
}
|
||||
var config Config
|
||||
err = json.Unmarshal(content, &config)
|
||||
if err != nil {
|
||||
log.Fatal("Error during parsing config file: ", err)
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
type Login struct { // login body struct
|
||||
Username string `form:"username" binding:"required"`
|
||||
Password string `form:"password" binding:"required"`
|
||||
}
|
||||
|
||||
type User struct { // add or modify user body struct
|
||||
CN string `form:"cn"`
|
||||
SN string `form:"sn"`
|
||||
UserPassword string `form:"userpassword"`
|
||||
}
|
||||
|
||||
type Group struct { // add or modify group body struct
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"listenPort": 8082,
|
||||
"listenPort": 80,
|
||||
"ldapURL": "ldap://localhost",
|
||||
"basedn": "dc=example,dc=com",
|
||||
"sessionSecretKey": "super secret key",
|
45
go.mod
Normal file
45
go.mod
Normal file
@ -0,0 +1,45 @@
|
||||
module proxmoxaas-ldap
|
||||
|
||||
go 1.22.4
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/sessions v1.0.1
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/go-ldap/ldap/v3 v3.4.8
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/bytedance/sonic v1.11.8 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.4 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.22.0 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/context v1.1.2 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/gorilla/sessions v1.3.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.24.0 // indirect
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
11
init/proxmoxaas-ldap.service
Normal file
11
init/proxmoxaas-ldap.service
Normal file
@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=proxmoxaas-ldap
|
||||
After=network.target
|
||||
[Service]
|
||||
WorkingDirectory=/<path to dir>
|
||||
ExecStart=/<path to dir>/proxmoxaas-ldap
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Type=simple
|
||||
[Install]
|
||||
WantedBy=default.target
|
29
package.json
29
package.json
@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "proxmoxaas-ldap",
|
||||
"version": "0.0.1",
|
||||
"description": "LDAP intermediate API for ProxmoxAAS",
|
||||
"main": "src/main.js",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"axios": "^1.5.1",
|
||||
"body-parser": "^1.20.1",
|
||||
"cookie": "^0.5.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.18.2",
|
||||
"express-session": "^1.17.3",
|
||||
"ldapjs": "^3.0.5",
|
||||
"minimist": "^1.2.8",
|
||||
"morgan": "^1.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.43.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-n": "^16.0.1",
|
||||
"eslint-plugin-promise": "^6.1.1"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "DEBUG=eslint:cli-engine eslint --fix ."
|
||||
}
|
||||
}
|
9
proxmoxaas-ldap.go
Normal file
9
proxmoxaas-ldap.go
Normal file
@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
app "proxmoxaas-ldap/app"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app.Run()
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
[Unit]
|
||||
Description=proxmoxaas-ldap
|
||||
After=network.target
|
||||
[Service]
|
||||
WorkingDirectory=/<path to dir>/ProxmoxAAS-LDAP/
|
||||
ExecStart=/<path to dir>/ProxmoxAAS-LDAP/start.sh
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Type=simple
|
||||
[Install]
|
||||
WantedBy=default.target
|
275
src/ldap.js
275
src/ldap.js
@ -1,275 +0,0 @@
|
||||
import ldap from "ldapjs";
|
||||
|
||||
export default class LDAP {
|
||||
#client = null;
|
||||
#basedn = null;
|
||||
#peopledn = null;
|
||||
#groupsdn = null;
|
||||
|
||||
constructor (url, basedn) {
|
||||
const opts = {
|
||||
url
|
||||
};
|
||||
this.#client = new LDAPJS_CLIENT_ASYNC_WRAPPER(opts);
|
||||
this.#basedn = basedn;
|
||||
this.#peopledn = `ou=people,${basedn}`;
|
||||
this.#groupsdn = `ou=groups,${basedn}`;
|
||||
}
|
||||
|
||||
async bindUser (uid, password) {
|
||||
return await this.#client.bind(`uid=${uid},${this.#peopledn}`, password);
|
||||
}
|
||||
|
||||
async getAllUsers () {
|
||||
const result = await this.#client.search(this.#peopledn, {
|
||||
scope: "one"
|
||||
});
|
||||
result.users = result.entries;
|
||||
return result;
|
||||
}
|
||||
|
||||
async addUser (uid, attrs) {
|
||||
const userDN = `uid=${uid},${this.#peopledn}`;
|
||||
if (!attrs.cn || !attrs.sn || !attrs.userPassword) {
|
||||
return {
|
||||
ok: false,
|
||||
error: {
|
||||
code: 100,
|
||||
name: "UndefinedAttributeValueError",
|
||||
message: "Undefined Attribute Value"
|
||||
}
|
||||
};
|
||||
}
|
||||
const entry = {
|
||||
objectClass: "inetOrgPerson",
|
||||
cn: attrs.cn,
|
||||
sn: attrs.sn,
|
||||
uid,
|
||||
userPassword: attrs.userPassword
|
||||
};
|
||||
return await this.#client.add(userDN, entry);
|
||||
}
|
||||
|
||||
async getUser (uid) {
|
||||
const result = await this.#client.search(`uid=${uid},${this.#peopledn}`, {});
|
||||
result.user = result.entries[0]; // assume there should only be 1 entry
|
||||
return result;
|
||||
}
|
||||
|
||||
async modUser (uid, newAttrs) {
|
||||
const logger = new LDAP_MULTIOP_LOGGER(`modify ${uid}`);
|
||||
for (const attr of ["cn", "sn", "userPassword"]) {
|
||||
if (attr in newAttrs && newAttrs[attr]) { // attr should exist and not be undefined or null
|
||||
const change = new ldap.Change({
|
||||
operation: "replace",
|
||||
modification: {
|
||||
type: attr,
|
||||
values: [newAttrs[attr]]
|
||||
}
|
||||
});
|
||||
await this.#client.modify(`uid=${uid},${this.#peopledn}`, change, logger);
|
||||
}
|
||||
}
|
||||
return logger;
|
||||
}
|
||||
|
||||
async delUser (uid) {
|
||||
const logger = new LDAP_MULTIOP_LOGGER(`del ${uid}`);
|
||||
const userDN = `uid=${uid},${this.#peopledn}`;
|
||||
await this.#client.del(userDN, logger);
|
||||
const groups = await this.#client.search(this.#groupsdn, {
|
||||
scope: "one",
|
||||
filter: `(member=uid=${uid},${this.#peopledn})`
|
||||
}, logger);
|
||||
if (!logger.ok) {
|
||||
return logger;
|
||||
}
|
||||
for (const element of groups.entries) {
|
||||
const change = {
|
||||
operation: "delete",
|
||||
modification: {
|
||||
type: "member",
|
||||
values: [`uid=${uid},${this.#peopledn}`]
|
||||
}
|
||||
};
|
||||
await this.#client.modify(element.dn, change, logger);
|
||||
}
|
||||
return logger;
|
||||
}
|
||||
|
||||
async getAllGroups () {
|
||||
const result = await this.#client.search(this.#groupsdn, {
|
||||
scope: "one"
|
||||
});
|
||||
result.groups = result.entries;
|
||||
return result;
|
||||
}
|
||||
|
||||
async addGroup (gid) {
|
||||
const groupDN = `cn=${gid},${this.#groupsdn}`;
|
||||
const entry = {
|
||||
objectClass: "groupOfNames",
|
||||
member: "",
|
||||
cn: gid
|
||||
};
|
||||
return await this.#client.add(groupDN, entry);
|
||||
}
|
||||
|
||||
async getGroup (gid) {
|
||||
const result = await this.#client.search(`cn=${gid},${this.#groupsdn}`, {});
|
||||
result.group = result.entries[0]; // assume there should only be 1 entry
|
||||
return result;
|
||||
}
|
||||
|
||||
async delGroup (gid) {
|
||||
const groupDN = `cn=${gid},${this.#groupsdn}`;
|
||||
return await this.#client.del(groupDN);
|
||||
}
|
||||
|
||||
async addUserToGroup (uid, gid) {
|
||||
// add the user
|
||||
const change = new ldap.Change({
|
||||
operation: "add",
|
||||
modification: {
|
||||
type: "member",
|
||||
values: [`uid=${uid},${this.#peopledn}`]
|
||||
}
|
||||
});
|
||||
return await this.#client.modify(`cn=${gid},${this.#groupsdn}`, change);
|
||||
}
|
||||
|
||||
async delUserFromGroup (uid, gid) {
|
||||
const change = new ldap.Change({
|
||||
operation: "delete",
|
||||
modification: {
|
||||
type: "member",
|
||||
values: [`uid=${uid},${this.#peopledn}`]
|
||||
}
|
||||
});
|
||||
return await this.#client.modify(`cn=${gid},${this.#groupsdn}`, change);
|
||||
}
|
||||
}
|
||||
|
||||
class LDAP_MULTIOP_LOGGER {
|
||||
op = null;
|
||||
ok = true;
|
||||
error = [];
|
||||
subops = [];
|
||||
constructor (op) {
|
||||
this.op = op;
|
||||
}
|
||||
|
||||
push (op) {
|
||||
if (!op.ok) {
|
||||
this.ok = false;
|
||||
this.error.push(op.error);
|
||||
}
|
||||
this.subops.push(op);
|
||||
}
|
||||
}
|
||||
|
||||
class LDAPJS_CLIENT_ASYNC_WRAPPER {
|
||||
#client = null;
|
||||
constructor (opts) {
|
||||
this.#client = ldap.createClient(opts);
|
||||
this.#client.on("error", (err) => {
|
||||
console.error(`An error occured:\n${err}`);
|
||||
});
|
||||
this.#client.on("connectError", (err) => {
|
||||
console.error(`Unable to connect to ${opts.url}:\n${err}`);
|
||||
});
|
||||
}
|
||||
|
||||
#parseError (err) {
|
||||
if (err) {
|
||||
return { code: err.code, name: err.name, message: err.message };
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
bind (dn, password, logger = null) {
|
||||
return new Promise((resolve) => {
|
||||
this.#client.bind(dn, password, (err) => {
|
||||
const result = { op: `bind ${dn}`, ok: err === null, error: this.#parseError(err) };
|
||||
if (logger) {
|
||||
logger.push(result);
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
add (dn, entry, logger = null) {
|
||||
return new Promise((resolve) => {
|
||||
this.#client.add(dn, entry, (err) => {
|
||||
const result = { op: `add ${dn}`, ok: err === null, error: this.#parseError(err) };
|
||||
if (logger) {
|
||||
logger.push(result);
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
search (base, options, logger = null) {
|
||||
return new Promise((resolve) => {
|
||||
this.#client.search(base, options, (err, res) => {
|
||||
if (err) {
|
||||
return resolve({ op: `search ${base}`, ok: false, error: err });
|
||||
}
|
||||
const result = { op: `search ${base}`, ok: false, error: null, entries: [] };
|
||||
res.on("searchRequest", (searchRequest) => { });
|
||||
res.on("searchEntry", (entry) => {
|
||||
const attributes = {};
|
||||
for (const element of entry.pojo.attributes) {
|
||||
attributes[element.type] = element.values;
|
||||
}
|
||||
result.entries.push({ dn: entry.pojo.objectName, attributes });
|
||||
});
|
||||
res.on("searchReference", (referral) => { });
|
||||
res.on("error", (err) => {
|
||||
result.ok = false;
|
||||
result.error = this.#parseError(err);
|
||||
if (logger) {
|
||||
logger.push(result);
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
res.on("end", (res) => {
|
||||
result.ok = true;
|
||||
result.error = null;
|
||||
if (logger) {
|
||||
logger.push(result);
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
modify (name, changes, logger = null) {
|
||||
return new Promise((resolve) => {
|
||||
this.#client.modify(name, changes, (err) => {
|
||||
const result = { op: `modify ${name} ${changes.operation} ${changes.modification.type}`, ok: err === null, error: this.#parseError(err) };
|
||||
if (logger) {
|
||||
logger.push(result);
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
del (dn, logger = null) {
|
||||
return new Promise((resolve) => {
|
||||
this.#client.del(dn, (err) => {
|
||||
const result = { op: `del ${dn}`, ok: err === null, error: this.#parseError(err) };
|
||||
if (logger) {
|
||||
logger.push(result);
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
347
src/main.js
347
src/main.js
@ -1,347 +0,0 @@
|
||||
import express from "express";
|
||||
import bodyParser from "body-parser";
|
||||
import cookieParser from "cookie-parser";
|
||||
import morgan from "morgan";
|
||||
import session from "express-session";
|
||||
import parseArgs from "minimist";
|
||||
|
||||
import * as utils from "./utils.js";
|
||||
import LDAP from "./ldap.js";
|
||||
|
||||
global.argv = parseArgs(process.argv.slice(2), {
|
||||
default: {
|
||||
package: "package.json",
|
||||
config: "config/config.json"
|
||||
}
|
||||
});
|
||||
|
||||
global.utils = utils;
|
||||
global.package = global.utils.readJSONFile(global.argv.package);
|
||||
global.config = global.utils.readJSONFile(global.argv.config);
|
||||
|
||||
const LDAPSessions = {};
|
||||
|
||||
const app = express();
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(cookieParser());
|
||||
app.use(morgan("combined"));
|
||||
app.use(session({
|
||||
secret: global.config.sessionSecretKey,
|
||||
name: global.config.sessionCookieName,
|
||||
cookie: global.config.sessionCookie,
|
||||
resave: false,
|
||||
saveUninitialized: true
|
||||
}));
|
||||
|
||||
app.listen(global.config.listenPort, () => {
|
||||
console.log(`proxmoxaas-ldap v${global.package.version} listening on port ${global.config.listenPort}`);
|
||||
});
|
||||
|
||||
/**
|
||||
* GET - get API version
|
||||
* responses:
|
||||
* - 200: {version: string}
|
||||
*/
|
||||
app.get("/version", (req, res) => {
|
||||
res.status(200).send({ version: global.package.version });
|
||||
});
|
||||
|
||||
/**
|
||||
* GET - echo request
|
||||
* responses:
|
||||
* - 200: {body: request.body, cookies: request.cookies}
|
||||
*/
|
||||
app.get("/echo", (req, res) => {
|
||||
res.status(200).send({ body: req.body, cookies: req.cookies });
|
||||
});
|
||||
|
||||
/**
|
||||
* POST - get session ticket by authenticating using user id and password
|
||||
*/
|
||||
app.post("/ticket", async (req, res) => {
|
||||
const params = {
|
||||
uid: req.body.uid,
|
||||
password: req.body.password
|
||||
};
|
||||
const newLDAPSession = new LDAP(global.config.ldapURL, global.config.basedn);
|
||||
const bindResult = await newLDAPSession.bindUser(params.uid, params.password);
|
||||
if (bindResult.ok) {
|
||||
LDAPSessions[req.session.id] = newLDAPSession;
|
||||
res.status(200).send({ auth: true });
|
||||
}
|
||||
else {
|
||||
res.status(403).send({
|
||||
ok: bindResult.ok,
|
||||
error: bindResult.error
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* DELETE - invalidate and remove session ticket
|
||||
*/
|
||||
app.delete("/ticket", async (req, res) => {
|
||||
req.session.ldap = null;
|
||||
req.session.destroy();
|
||||
const expire = new Date(0);
|
||||
res.cookie(global.config.sessionCookieName, "", { expires: expire });
|
||||
res.send({ auth: false });
|
||||
});
|
||||
|
||||
/**
|
||||
* GET - get user attributes for all users
|
||||
*/
|
||||
app.get("/users", async (req, res) => {
|
||||
if (req.session.id in LDAPSessions) {
|
||||
const ldap = LDAPSessions[req.session.id];
|
||||
const result = await ldap.getAllUsers();
|
||||
res.send({
|
||||
ok: result.ok,
|
||||
error: result.error,
|
||||
users: result.users
|
||||
});
|
||||
}
|
||||
else {
|
||||
res.status(403).send({ auth: false });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* POST - create a new user or modify existing user attributes
|
||||
* request:
|
||||
* - userid: user id
|
||||
* - cn: common name
|
||||
* - sn: surname
|
||||
* - userpassword: user password
|
||||
*/
|
||||
app.post("/users/:userid", async (req, res) => {
|
||||
const params = {
|
||||
userid: req.params.userid,
|
||||
userattrs: {
|
||||
cn: req.body.usercn,
|
||||
sn: req.body.usersn,
|
||||
userPassword: req.body.userpassword
|
||||
}
|
||||
};
|
||||
if (req.session.id in LDAPSessions) {
|
||||
const ldap = LDAPSessions[req.session.id];
|
||||
const checkUser = await ldap.getUser(params.userid);
|
||||
if (!checkUser.ok && checkUser.error.code === 32) { // the user does not exist, create new user
|
||||
const result = await ldap.addUser(params.userid, params.userattrs);
|
||||
res.send({
|
||||
ok: result.ok,
|
||||
error: result.error
|
||||
});
|
||||
}
|
||||
else if (checkUser.ok) { // the user does exist, modify the user entries
|
||||
const result = await ldap.modUser(params.userid, params.userattrs);
|
||||
res.send({
|
||||
ok: result.ok,
|
||||
error: result.error
|
||||
});
|
||||
}
|
||||
else { // some other error happened
|
||||
res.send({
|
||||
ok: checkUser.ok,
|
||||
error: checkUser.error
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
res.status(403).send({ auth: false });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET - get user attributes
|
||||
* request:
|
||||
* - userid: user id
|
||||
*/
|
||||
app.get("/users/:userid", async (req, res) => {
|
||||
const params = {
|
||||
userid: req.params.userid
|
||||
};
|
||||
if (req.session.id in LDAPSessions) {
|
||||
const ldap = LDAPSessions[req.session.id];
|
||||
const result = await ldap.getUser(params.userid);
|
||||
if (result.ok) {
|
||||
res.send({
|
||||
ok: result.ok,
|
||||
error: result.error,
|
||||
user: result.user
|
||||
});
|
||||
}
|
||||
else {
|
||||
res.send({
|
||||
ok: result.ok,
|
||||
error: result.error
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
res.status(403).send({ auth: false });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* DELETE - delete user
|
||||
* request:
|
||||
* - userid: user id
|
||||
*/
|
||||
app.delete("/users/:userid", async (req, res) => {
|
||||
const params = {
|
||||
userid: req.params.userid
|
||||
};
|
||||
if (req.session.id in LDAPSessions) {
|
||||
const ldap = LDAPSessions[req.session.id];
|
||||
const result = await ldap.delUser(params.userid);
|
||||
res.send({
|
||||
ok: result.ok,
|
||||
error: result.error
|
||||
});
|
||||
}
|
||||
else {
|
||||
res.status(403).send({ auth: false });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET - get group attributes including members for all groups
|
||||
* request:
|
||||
*/
|
||||
app.get("/groups", async (req, res) => {
|
||||
if (req.session.id in LDAPSessions) {
|
||||
const ldap = LDAPSessions[req.session.id];
|
||||
const result = await ldap.getAllGroups();
|
||||
res.send({
|
||||
ok: result.ok,
|
||||
error: result.error,
|
||||
groups: result.groups
|
||||
});
|
||||
}
|
||||
else {
|
||||
res.status(403).send({ auth: false });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* POST - create a new group
|
||||
* request:
|
||||
* - groupid: group id
|
||||
*/
|
||||
app.post("/groups/:groupid", async (req, res) => {
|
||||
const params = {
|
||||
groupid: req.params.groupid
|
||||
};
|
||||
if (req.session.id in LDAPSessions) {
|
||||
const ldap = LDAPSessions[req.session.id];
|
||||
const result = await ldap.addGroup(params.groupid);
|
||||
res.send({
|
||||
ok: result.ok,
|
||||
error: result.error
|
||||
});
|
||||
}
|
||||
else {
|
||||
res.status(403).send({ auth: false });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET - get group attributes including members
|
||||
* request:
|
||||
* - groupid: group id
|
||||
*/
|
||||
app.get("/groups/:groupid", async (req, res) => {
|
||||
const params = {
|
||||
groupid: req.params.groupid
|
||||
};
|
||||
if (req.session.id in LDAPSessions) {
|
||||
const ldap = LDAPSessions[req.session.id];
|
||||
const result = await ldap.getGroup(params.groupid);
|
||||
if (result.ok) {
|
||||
res.send({
|
||||
ok: result.ok,
|
||||
error: result.error,
|
||||
group: result.group
|
||||
});
|
||||
}
|
||||
else {
|
||||
res.send({
|
||||
ok: result.ok,
|
||||
error: result.error
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
res.status(403).send({ auth: false });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* DELETE - delete group
|
||||
* request:
|
||||
* - groupid: group id
|
||||
*/
|
||||
app.delete("/groups/:groupid", async (req, res) => {
|
||||
const params = {
|
||||
groupid: req.params.groupid
|
||||
};
|
||||
if (req.session.id in LDAPSessions) {
|
||||
const ldap = LDAPSessions[req.session.id];
|
||||
const result = await ldap.delGroup(params.groupid);
|
||||
res.send({
|
||||
ok: result.ok,
|
||||
error: result.error
|
||||
});
|
||||
}
|
||||
else {
|
||||
res.status(403).send({ auth: false });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* POST - add a member to the group
|
||||
* request:
|
||||
* - groupid: group id
|
||||
* - userid: user id
|
||||
*/
|
||||
app.post("/groups/:groupid/members/:userid", async (req, res) => {
|
||||
const params = {
|
||||
groupid: req.params.groupid,
|
||||
userid: req.params.userid
|
||||
};
|
||||
if (req.session.id in LDAPSessions) {
|
||||
const ldap = LDAPSessions[req.session.id];
|
||||
const result = await ldap.addUserToGroup(params.userid, params.groupid);
|
||||
res.send({
|
||||
ok: result.ok,
|
||||
error: result.error
|
||||
});
|
||||
}
|
||||
else {
|
||||
res.status(403).send({ auth: false });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* DELETE - remove a member from the group
|
||||
* - groupid: group id
|
||||
* - userid: user id
|
||||
*/
|
||||
app.delete("/groups/:groupid/members/:userid", async (req, res) => {
|
||||
const params = {
|
||||
groupid: req.params.groupid,
|
||||
userid: req.params.userid
|
||||
};
|
||||
if (req.session.id in LDAPSessions) {
|
||||
const ldap = LDAPSessions[req.session.id];
|
||||
const result = await ldap.delUserFromGroup(params.userid, params.groupid);
|
||||
res.send({
|
||||
ok: result.ok,
|
||||
error: result.error
|
||||
});
|
||||
}
|
||||
else {
|
||||
res.status(403).send({ auth: false });
|
||||
}
|
||||
});
|
12
src/utils.js
12
src/utils.js
@ -1,12 +0,0 @@
|
||||
import { readFileSync } from "fs";
|
||||
import { exit } from "process";
|
||||
|
||||
export function readJSONFile (path) {
|
||||
try {
|
||||
return JSON.parse(readFileSync(path));
|
||||
}
|
||||
catch (e) {
|
||||
console.log(`error: ${path} was not found.`);
|
||||
exit(1);
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user