diff --git a/user-manager-api.go b/access-manager-api.go similarity index 100% rename from user-manager-api.go rename to access-manager-api.go diff --git a/app/common/schema.go b/app/common/schema.go index c455574..5992240 100644 --- a/app/common/schema.go +++ b/app/common/schema.go @@ -1,7 +1,7 @@ package app type Login struct { // login body struct - UsernameRaw string `json:"username" binding:"required"` + UsernameRaw string `json:"username"` Username Username - Password string `json:"password" binding:"required"` + Password string `json:"password"` } diff --git a/app/ldap/utils.go b/app/ldap/utils.go index 9ec625e..0969592 100644 --- a/app/ldap/utils.go +++ b/app/ldap/utils.go @@ -5,7 +5,6 @@ import ( "net/http" "regexp" - "github.com/gin-gonic/gin" "github.com/go-ldap/ldap/v3" common "access-manager-api/app/common" @@ -23,17 +22,17 @@ func LDAPEntryToGroup(entry *ldap.Entry) common.Group { return common.Group{} } -func ParseLDAPError(err error) gin.H { +func ParseLDAPError(err error) map[string]any { if err != nil { LDAPerr := err.(*ldap.Error) - return gin.H{ + return map[string]any{ "ok": false, "code": LDAPerr.ResultCode, "result": ldap.LDAPResultCodeMap[LDAPerr.ResultCode], "message": LDAPerr.Err.Error(), } } else { - return gin.H{ + return map[string]any{ "ok": true, "code": http.StatusOK, "result": "OK", diff --git a/app/main.go b/app/main.go index 3f32728..c20ad07 100644 --- a/app/main.go +++ b/app/main.go @@ -7,13 +7,13 @@ import ( "log" "net/http" "strconv" + "time" common "access-manager-api/app/common" "access-manager-api/app/pve" + paas "proxmoxaas-common-lib" - "github.com/gin-contrib/sessions" - "github.com/gin-contrib/sessions/cookie" - "github.com/gin-gonic/gin" + "github.com/gorilla/sessions" ) type UserSession struct { @@ -30,6 +30,7 @@ var Config common.Config var RootSession *UserSession var UserSessions map[string]*UserSession var Realms map[string]common.Realm +var Store *sessions.CookieStore func Run() { configPath := flag.String("config", "config.json", "path to config.json file") @@ -38,14 +39,13 @@ func Run() { // load config values Config = common.GetConfig(*configPath) // already exits if failed - log.Printf("Read in config from %s\n", *configPath) + log.Printf("[INFO] Read in config from %s\n", *configPath) - // setup gin - gin.SetMode(gin.ReleaseMode) - router := gin.Default() + // setup http router + router := http.NewServeMux() // setup api auth cookies - SetupAPISessionStore(router, &Config) + SetupAPISessionStore(&Config) // setup root api token client, code, err := pve.NewClientFromAPIToken(Config.PVE, Config.PVE.Token) @@ -63,35 +63,33 @@ func Run() { // make global session map UserSessions = make(map[string]*UserSession) - router.GET("/version", func(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{"version": Version}) + router.HandleFunc("GET /version", func(w http.ResponseWriter, r *http.Request) { + paas.HTTPWriteJson(w, http.StatusOK, map[string]any{"version": Version}) }) - router.POST("/ticket", POST_Ticket) - router.DELETE("/ticket", DELETE_Ticket) - router.GET("/pools/:poolid", GET_Pool) - router.POST("/pools/:poolid", POST_Pool) - router.DELETE("/pools/:poolid", DELETE_Pool) - router.GET("/groups/:groupname", GET_Group) - router.POST("/groups/:groupname", POST_Group) - router.DELETE("/groups/:groupname", DELETE_Group) - router.POST("/pools/:poolid/groups/:groupname", POST_Pool_Group) - router.DELETE("/pools/:poolid/groups/:groupname") - router.GET("/users/:username", GET_User) - router.POST("/users/:username", POST_User) - router.DELETE("/users/:username", DELETE_User) - router.POST("/groups/:groupname/users/:username", POST_Group_User) - router.DELETE("/groups/:groupname/users/:username", DELETE_Group_User) + router.HandleFunc("POST /ticket", POST_Ticket) + router.HandleFunc("DELETE /ticket", DELETE_Ticket) + router.HandleFunc("GET /pools/{poolid}", GET_Pool) + router.HandleFunc("POST /pools/{poolid}", POST_Pool) + router.HandleFunc("DELETE /pools/{poolid}", DELETE_Pool) + router.HandleFunc("GET /groups/{groupname}", GET_Group) + router.HandleFunc("POST /groups/{groupname}", POST_Group) + router.HandleFunc("DELETE /groups/{groupname}", DELETE_Group) + router.HandleFunc("POST /pools/{poolid}/groups/{groupname}", POST_Pool_Group) + router.HandleFunc("DELETE /pools/{poolid}/groups/{groupname}", DELETE_Pool_Group) + router.HandleFunc("GET /users/{username}", GET_User) + router.HandleFunc("POST /users/{username}", POST_User) + router.HandleFunc("DELETE /users/{username}", DELETE_User) + router.HandleFunc("POST /groups/{groupname}/users/{username}", POST_Group_User) + router.HandleFunc("DELETE /groups/{groupname}/users/{username}", DELETE_Group_User) - log.Printf("Starting Access Manager API on port %s\n", strconv.Itoa(Config.ListenPort)) + server := LoggingMiddleware(router) - err = router.Run("0.0.0.0:" + strconv.Itoa(Config.ListenPort)) - if err != nil { - log.Fatalf("Error starting router: %s", err.Error()) - } + log.Printf("[INFO] starting API listening on 0.0.0.0:%d", Config.ListenPort) + log.Fatalf("[ERR ] Error starting router: %s", http.ListenAndServe("0.0.0.0:"+strconv.Itoa(Config.ListenPort), server)) } -func SetupAPISessionStore(router *gin.Engine, config *common.Config) { +func SetupAPISessionStore(config *common.Config) { authKey := make([]byte, 32) encrKey := make([]byte, 32) n, _ := rand.Read(authKey) // rand Read never returns an error, always crashes on error @@ -99,21 +97,29 @@ func SetupAPISessionStore(router *gin.Engine, config *common.Config) { n, _ = rand.Read(encrKey) // rand Read never returns an error, always crashes on error log.Printf("Generated cookie session encryption key of length %d\n", n) - store := cookie.NewStore(authKey, encrKey) - store.Options(sessions.Options{ + Store = sessions.NewCookieStore(authKey, encrKey) + 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)) + } log.Printf("Started cookie store (Name: %s Params: %+v)\n", config.SessionCookieName, config.SessionCookie) } -func GetUserSessionFromContext(c *gin.Context) (*UserSession, int, error) { - session := sessions.Default(c) - SessionUUID := session.Get("SessionUUID") +// get the cookie session from the request, returns a new session if the cookie is missing or invalid +func GetCookieSession(r *http.Request) *sessions.Session { + session, err := Store.Get(r, Config.SessionCookieName) + if err != nil { + log.Printf("Error getting session cookie: %s\n", err.Error()) + } + return session +} + +func GetUserSessionFromRequest(r *http.Request) (*UserSession, int, error) { + session := GetCookieSession(r) + SessionUUID := session.Values["SessionUUID"] if SessionUUID == nil { return nil, http.StatusUnauthorized, fmt.Errorf("no auth session found") } @@ -121,3 +127,11 @@ func GetUserSessionFromContext(c *gin.Context) (*UserSession, int, error) { usersession := UserSessions[uuid] return usersession, http.StatusOK, nil } + +func LoggingMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + next.ServeHTTP(w, r) + log.Printf("%s %s %s", r.Method, r.URL.Path, time.Since(start)) + }) +} diff --git a/app/routes.go b/app/routes.go index 6fed95b..f2e3b54 100644 --- a/app/routes.go +++ b/app/routes.go @@ -5,27 +5,31 @@ import ( ldap "access-manager-api/app/ldap" "access-manager-api/app/localdb" pve "access-manager-api/app/pve" + "encoding/json" "fmt" "net/http" paas "proxmoxaas-common-lib" "uuid" - "github.com/gin-contrib/sessions" - "github.com/gin-gonic/gin" + "github.com/gorilla/sessions" ) -func POST_Ticket(c *gin.Context) { +func POST_Ticket(w http.ResponseWriter, r *http.Request) { body := common.Login{} - err := c.ShouldBindJSON(&body) - if err != nil { // bad request from binding - c.JSON(http.StatusBadRequest, gin.H{"auth": false, "error": err.Error()}) + err := json.NewDecoder(r.Body).Decode(&body) + if err != nil { // bad request from decoding + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"auth": false, "error": err.Error()}) + return + } + if body.UsernameRaw == "" || body.Password == "" { // username and password are required + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"auth": false, "error": "missing required field username or password"}) return } // attempt to parse username body.Username, err = paas.ParseUsername(body.UsernameRaw) if err != nil { // username format incorrect - c.JSON(http.StatusBadRequest, gin.H{"auth": false, "error": err.Error()}) + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"auth": false, "error": err.Error()}) return } handler := Realms[body.Username.Realm].Type @@ -35,7 +39,7 @@ func POST_Ticket(c *gin.Context) { // always bind proxmox backend PVEClient, code, err := pve.NewClientFromCredentials(Config.PVE, body.Username, body.Password) if err != nil { // pve client failed to bind - c.JSON(code, gin.H{"auth": false, "error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"auth": false, "error": err.Error()}) return } userbackends.PVE = PVEClient @@ -47,102 +51,102 @@ func POST_Ticket(c *gin.Context) { config := Realms[body.Username.Realm].Config.(common.LDAPConfig) LDAPClient, code, err := ldap.NewClientFromCredentials(config, body.Username, body.Password) if err != nil { // ldap client failed to bind - c.JSON(code, gin.H{"auth": false, "error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"auth": false, "error": err.Error()}) return } userbackends.Realm.Name = body.Username.Realm userbackends.Realm.Handler = LDAPClient default: - c.JSON(code, gin.H{"auth": false, "error": fmt.Errorf("user realm %s is not supported", body.Username.Realm)}) + paas.HTTPWriteJson(w, code, map[string]any{"auth": false, "error": fmt.Errorf("user realm %s is not supported", body.Username.Realm)}) return } // failing to load lcoaldb is a fatal error userbackends.DB, code, err = localdb.NewClientFromCredentials(Config.LocalDB, body.Username, body.Password) if err != nil { - c.JSON(code, gin.H{"auth": false, "error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"auth": false, "error": err.Error()}) return } // successful binding at this point // create new session - session := sessions.Default(c) + session := GetCookieSession(r) // create random uuid to map user to backends uuid := uuid.NewV4() // set uuid mapping in session - session.Set("SessionUUID", uuid.String()) + session.Values["SessionUUID"] = uuid.String() // set uuid mapping in LDAPSessions UserSessions[uuid.String()] = &userbackends // save the session - err = session.Save() + err = session.Save(r, w) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"auth": false, "error": err.Error()}) + paas.HTTPWriteJson(w, http.StatusInternalServerError, map[string]any{"auth": false, "error": err.Error()}) } else { // return successful auth - c.JSON(http.StatusOK, gin.H{"auth": true}) + paas.HTTPWriteJson(w, http.StatusOK, map[string]any{"auth": true}) } } -func DELETE_Ticket(c *gin.Context) { +func DELETE_Ticket(w http.ResponseWriter, r *http.Request) { // get session uuid from session cookie - session := sessions.Default(c) - SessionUUID := session.Get("SessionUUID") + session := GetCookieSession(r) + SessionUUID := session.Values["SessionUUID"] if SessionUUID == nil { - c.JSON(http.StatusUnauthorized, gin.H{"auth": false}) + paas.HTTPWriteJson(w, http.StatusUnauthorized, map[string]any{"auth": false}) return } uuid := SessionUUID.(string) // delete uuid entry from user sessions - delete(UserSessions, uuid) // deletes uuid mapping - session.Options(sessions.Options{MaxAge: -1}) // set max age to -1 so session cookie is deleted - err := session.Save() // if save somehow fails, it should be ok since the uuid mapping is already deleted + delete(UserSessions, uuid) // deletes uuid mapping + session.Options = &sessions.Options{MaxAge: -1} // set max age to -1 so session cookie is deleted + err := session.Save(r, w) // if save somehow fails, it should be ok since the uuid mapping is already deleted if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"auth": false}) + paas.HTTPWriteJson(w, http.StatusInternalServerError, map[string]any{"auth": false}) } else { - c.JSON(http.StatusUnauthorized, gin.H{"auth": false}) + paas.HTTPWriteJson(w, http.StatusUnauthorized, map[string]any{"auth": false}) } } -func GET_Pool(c *gin.Context) { - poolid, ok := c.Params.Get("poolid") - if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")}) +func GET_Pool(w http.ResponseWriter, r *http.Request) { + poolid := r.PathValue("poolid") + if poolid == "" { + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": fmt.Errorf("missing required path parameter poolid")}) return } - backends, code, err := GetUserSessionFromContext(c) + backends, code, err := GetUserSessionFromRequest(r) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) return } pool, code, err := GetPool(backends, poolid) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) } else { - c.JSON(http.StatusOK, gin.H{"pool": pool}) + paas.HTTPWriteJson(w, http.StatusOK, map[string]any{"pool": pool}) } } -func POST_Pool(c *gin.Context) { - poolid, ok := c.Params.Get("poolid") - if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")}) +func POST_Pool(w http.ResponseWriter, r *http.Request) { + poolid := r.PathValue("poolid") + if poolid == "" { + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": fmt.Errorf("missing required path parameter poolid")}) return } - backends, code, err := GetUserSessionFromContext(c) + backends, code, err := GetUserSessionFromRequest(r) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) return } // read request body pool := common.Pool{} - err = c.ShouldBindJSON(&pool) - if err != nil { // failed binding, usually missing user field - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + err = json.NewDecoder(r.Body).Decode(&pool) + if err != nil { // failed decoding, usually malformed json + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": err.Error()}) return } @@ -151,91 +155,91 @@ func POST_Pool(c *gin.Context) { if code == http.StatusNotFound { // pool does not already exist, create new pool code, err = NewPool(backends, poolid, pool) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) } else { - c.Status(http.StatusOK) + w.WriteHeader(http.StatusOK) } } else { code, err = ModPool(backends, poolid, pool) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) } else { - c.Status(http.StatusOK) + w.WriteHeader(http.StatusOK) } } } -func DELETE_Pool(c *gin.Context) { - poolid, ok := c.Params.Get("poolid") - if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")}) +func DELETE_Pool(w http.ResponseWriter, r *http.Request) { + poolid := r.PathValue("poolid") + if poolid == "" { + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": fmt.Errorf("missing required path parameter poolid")}) return } - backends, code, err := GetUserSessionFromContext(c) + backends, code, err := GetUserSessionFromRequest(r) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) return } code, err = DelPool(backends, poolid) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) } else { - c.Status(http.StatusOK) + w.WriteHeader(http.StatusOK) } } -func GET_Group(c *gin.Context) { - groupname_str, ok := c.Params.Get("groupname") - if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")}) +func GET_Group(w http.ResponseWriter, r *http.Request) { + groupname_str := r.PathValue("groupname") + if groupname_str == "" { + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": fmt.Errorf("missing required path parameter poolid")}) return } groupname, err := common.ParseGroupname(groupname_str) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": err.Error()}) } - backends, code, err := GetUserSessionFromContext(c) + backends, code, err := GetUserSessionFromRequest(r) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) return } group, code, err := GetGroup(backends, groupname) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) } else { - c.JSON(http.StatusOK, gin.H{"group": group}) + paas.HTTPWriteJson(w, http.StatusOK, map[string]any{"group": group}) } } -func POST_Group(c *gin.Context) { - groupname_str, ok := c.Params.Get("groupname") - if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")}) +func POST_Group(w http.ResponseWriter, r *http.Request) { + groupname_str := r.PathValue("groupname") + if groupname_str == "" { + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": fmt.Errorf("missing required path parameter groupname")}) return } groupname, err := paas.ParseGroupname(groupname_str) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err}) + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": err}) return } - backends, code, err := GetUserSessionFromContext(c) + backends, code, err := GetUserSessionFromRequest(r) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) return } // read request body group := common.Group{} - err = c.ShouldBindJSON(&group) - if err != nil { // failed binding, usually missing user field - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + err = json.NewDecoder(r.Body).Decode(&group) + if err != nil { // failed decoding, usually malformed json + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": err.Error()}) return } @@ -244,163 +248,163 @@ func POST_Group(c *gin.Context) { if code == http.StatusNotFound { // group does not already exist, create new group code, err = NewGroup(backends, groupname, group) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) } else { - c.Status(http.StatusOK) + w.WriteHeader(http.StatusOK) } } else { code, err = ModGroup(backends, groupname, group) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) } else { - c.Status(http.StatusOK) + w.WriteHeader(http.StatusOK) } } } -func DELETE_Group(c *gin.Context) { - groupname_str, ok := c.Params.Get("groupname") - if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")}) +func DELETE_Group(w http.ResponseWriter, r *http.Request) { + groupname_str := r.PathValue("groupname") + if groupname_str == "" { + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": fmt.Errorf("missing required path parameter groupname")}) return } groupname, err := paas.ParseGroupname(groupname_str) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err}) + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": err}) return } - backends, code, err := GetUserSessionFromContext(c) + backends, code, err := GetUserSessionFromRequest(r) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) return } code, err = DelGroup(backends, groupname) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) } else { - c.Status(http.StatusOK) + w.WriteHeader(http.StatusOK) } } -func POST_Pool_Group(c *gin.Context) { - poolid, ok := c.Params.Get("poolid") - if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")}) +func POST_Pool_Group(w http.ResponseWriter, r *http.Request) { + poolid := r.PathValue("poolid") + if poolid == "" { + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": fmt.Errorf("missing required path parameter poolid")}) return } - groupname_str, ok := c.Params.Get("groupname") - if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")}) + groupname_str := r.PathValue("groupname") + if groupname_str == "" { + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": fmt.Errorf("missing required path parameter groupname")}) return } groupname, err := paas.ParseGroupname(groupname_str) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err}) + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": err}) return } - backends, code, err := GetUserSessionFromContext(c) + backends, code, err := GetUserSessionFromRequest(r) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) return } code, err = AddGroupToPool(backends, groupname, poolid) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) } else { - c.Status(http.StatusOK) + w.WriteHeader(http.StatusOK) } } -func DELETE_Pool_Group(c *gin.Context) { - poolid, ok := c.Params.Get("poolid") - if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")}) +func DELETE_Pool_Group(w http.ResponseWriter, r *http.Request) { + poolid := r.PathValue("poolid") + if poolid == "" { + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": fmt.Errorf("missing required path parameter poolid")}) return } - groupname_str, ok := c.Params.Get("groupname") - if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")}) + groupname_str := r.PathValue("groupname") + if groupname_str == "" { + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": fmt.Errorf("missing required path parameter groupname")}) return } groupname, err := paas.ParseGroupname(groupname_str) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err}) + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": err}) return } - backends, code, err := GetUserSessionFromContext(c) + backends, code, err := GetUserSessionFromRequest(r) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) return } code, err = DelGroupFromPool(backends, groupname, poolid) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) } else { - c.Status(http.StatusOK) + w.WriteHeader(http.StatusOK) } } -func GET_User(c *gin.Context) { - username_str, ok := c.Params.Get("username") - if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")}) +func GET_User(w http.ResponseWriter, r *http.Request) { + username_str := r.PathValue("username") + if username_str == "" { + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": fmt.Errorf("missing required path parameter poolid")}) return } username, err := common.ParseUsername(username_str) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": err.Error()}) } - backends, code, err := GetUserSessionFromContext(c) + backends, code, err := GetUserSessionFromRequest(r) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) return } user, code, err := GetUser(backends, username) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) } else { - c.JSON(http.StatusOK, gin.H{"user": user}) + paas.HTTPWriteJson(w, http.StatusOK, map[string]any{"user": user}) } } -func POST_User(c *gin.Context) { - username_str, ok := c.Params.Get("username") - if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")}) +func POST_User(w http.ResponseWriter, r *http.Request) { + username_str := r.PathValue("username") + if username_str == "" { + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": fmt.Errorf("missing required path parameter groupname")}) return } username, err := paas.ParseUsername(username_str) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err}) + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": err}) return } - backends, code, err := GetUserSessionFromContext(c) + backends, code, err := GetUserSessionFromRequest(r) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) return } // read request body user := common.User{} - err = c.ShouldBindJSON(&user) - if err != nil { // failed binding, usually missing user field - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + err = json.NewDecoder(r.Body).Decode(&user) + if err != nil { // failed decoding, usually malformed json + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": err.Error()}) return } @@ -409,121 +413,121 @@ func POST_User(c *gin.Context) { if code == http.StatusNotFound { // user does not already exist, create new user code, err = NewUser(backends, username, user) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) } else { - c.Status(http.StatusOK) + w.WriteHeader(http.StatusOK) } } else { code, err = ModUser(backends, username, user) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) } else { - c.Status(http.StatusOK) + w.WriteHeader(http.StatusOK) } } } -func DELETE_User(c *gin.Context) { - username_str, ok := c.Params.Get("username") - if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")}) +func DELETE_User(w http.ResponseWriter, r *http.Request) { + username_str := r.PathValue("username") + if username_str == "" { + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": fmt.Errorf("missing required path parameter groupname")}) return } username, err := paas.ParseUsername(username_str) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err}) + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": err}) return } - backends, code, err := GetUserSessionFromContext(c) + backends, code, err := GetUserSessionFromRequest(r) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) return } code, err = DelUser(backends, username) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) } else { - c.Status(http.StatusOK) + w.WriteHeader(http.StatusOK) } } -func POST_Group_User(c *gin.Context) { - groupname_str, ok := c.Params.Get("groupname") - if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")}) +func POST_Group_User(w http.ResponseWriter, r *http.Request) { + groupname_str := r.PathValue("groupname") + if groupname_str == "" { + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": fmt.Errorf("missing required path parameter groupname")}) return } - username_str, ok := c.Params.Get("username") - if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter username")}) + username_str := r.PathValue("username") + if username_str == "" { + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": fmt.Errorf("missing required path parameter username")}) return } groupname, err := paas.ParseGroupname(groupname_str) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err}) + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": err}) return } username, err := paas.ParseUsername(username_str) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err}) + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": err}) return } - backends, code, err := GetUserSessionFromContext(c) + backends, code, err := GetUserSessionFromRequest(r) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) return } code, err = AddUserToGroup(backends, username, groupname) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) } else { - c.Status(http.StatusOK) + w.WriteHeader(http.StatusOK) } } -func DELETE_Group_User(c *gin.Context) { - groupname_str, ok := c.Params.Get("groupname") - if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")}) +func DELETE_Group_User(w http.ResponseWriter, r *http.Request) { + groupname_str := r.PathValue("groupname") + if groupname_str == "" { + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": fmt.Errorf("missing required path parameter groupname")}) return } - username_str, ok := c.Params.Get("username") - if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter username")}) + username_str := r.PathValue("username") + if username_str == "" { + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": fmt.Errorf("missing required path parameter username")}) return } groupname, err := paas.ParseGroupname(groupname_str) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err}) + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": err}) return } username, err := paas.ParseUsername(username_str) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err}) + paas.HTTPWriteJson(w, http.StatusBadRequest, map[string]any{"error": err}) return } - backends, code, err := GetUserSessionFromContext(c) + backends, code, err := GetUserSessionFromRequest(r) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) return } code, err = DelUserFromGroup(backends, username, groupname) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + paas.HTTPWriteJson(w, code, map[string]any{"error": err.Error()}) } else { - c.Status(http.StatusOK) + w.WriteHeader(http.StatusOK) } } diff --git a/go.mod b/go.mod index 644399b..476a4b4 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,11 @@ module access-manager-api -go 1.27.0 +go 1.27.3 require ( - github.com/gin-contrib/sessions v1.1.0 - github.com/gin-gonic/gin v1.12.0 - github.com/go-ldap/ldap/v3 v3.4.14 - github.com/luthermonson/go-proxmox v0.8.1 + github.com/go-ldap/ldap/v3 v3.4.15 + github.com/gorilla/sessions v1.4.0 + github.com/luthermonson/go-proxmox v0.8.2 proxmoxaas-common-lib v0.0.0 ) @@ -15,43 +14,15 @@ replace proxmoxaas-common-lib => ./proxmoxaas-common-lib require ( github.com/Azure/go-ntlmssp v0.1.1 // indirect github.com/buger/goterm v1.0.4 // indirect - github.com/bytedance/gopkg v0.1.4 // indirect - github.com/bytedance/sonic v1.15.2 // indirect - github.com/bytedance/sonic/loader v0.5.2 // indirect - github.com/cloudwego/base64x v0.1.7 // indirect github.com/diskfs/go-diskfs v1.9.4 // indirect github.com/djherbis/times v1.6.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.15 // indirect - github.com/gin-contrib/sse v1.1.1 // indirect github.com/go-asn1-ber/asn1-ber v1.5.8 // 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.30.3 // indirect - github.com/goccy/go-json v0.10.6 // indirect - github.com/goccy/go-yaml v1.19.2 // 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.4.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/jinzhu/copier v0.4.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.4.0 // indirect - github.com/leodido/go-urn v1.5.0 // indirect github.com/magefile/mage v1.17.2 // indirect - github.com/mattn/go-isatty v0.0.24 // 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.4.3 // indirect - github.com/quic-go/qpack v0.6.0 // indirect - github.com/quic-go/quic-go v0.61.0 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.3.2 // indirect - go.mongodb.org/mongo-driver/v2 v2.8.1 // indirect - golang.org/x/arch v0.30.0 // indirect - golang.org/x/crypto v0.55.0 // indirect - golang.org/x/net v0.58.0 // indirect - golang.org/x/sys v0.47.0 // indirect - golang.org/x/text v0.41.0 // indirect - google.golang.org/protobuf v1.36.12 // indirect + github.com/stretchr/testify v1.11.1 // indirect + golang.org/x/crypto v0.58.0 // indirect + golang.org/x/sys v0.49.0 // indirect )