From 98d7b1b5003b57e54c7b4ef8fc7091c1f7723ed6 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 9 Oct 2024 12:05:01 +0200 Subject: [PATCH] Use middleware to load org (#4208) Co-authored-by: Anbraten <6918444+anbraten@users.noreply.github.com> Co-authored-by: Anbraten --- server/api/agent.go | 27 ++------ server/api/org.go | 42 ++---------- server/api/org_registry.go | 61 ++++++----------- server/api/org_secret.go | 61 ++++++----------- server/router/api.go | 4 +- server/router/middleware/session/org.go | 86 ++++++++++++++++++++++++ server/router/middleware/session/user.go | 15 ++--- 7 files changed, 142 insertions(+), 154 deletions(-) create mode 100644 server/router/middleware/session/org.go diff --git a/server/api/agent.go b/server/api/agent.go index bf473c1d4..f6a542871 100644 --- a/server/api/agent.go +++ b/server/api/agent.go @@ -292,14 +292,9 @@ func PostOrgAgent(c *gin.Context) { // @Param perPage query int false "for response pagination, max items per page" default(50) func GetOrgAgents(c *gin.Context) { _store := store.FromContext(c) + org := session.Org(c) - orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64) - if err != nil { - c.String(http.StatusBadRequest, "Error parsing org id. %s", err) - return - } - - agents, err := _store.AgentListForOrg(orgID, session.Pagination(c)) + agents, err := _store.AgentListForOrg(org.ID, session.Pagination(c)) if err != nil { c.String(http.StatusInternalServerError, "Error getting agent list. %s", err) return @@ -321,12 +316,7 @@ func GetOrgAgents(c *gin.Context) { // @Param agent body Agent true "the agent's updated data" func PatchOrgAgent(c *gin.Context) { _store := store.FromContext(c) - - orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64) - if err != nil { - c.String(http.StatusBadRequest, "Invalid organization ID") - return - } + org := session.Org(c) agentID, err := strconv.ParseInt(c.Param("agent_id"), 10, 64) if err != nil { @@ -340,7 +330,7 @@ func PatchOrgAgent(c *gin.Context) { return } - if agent.OrgID != orgID { + if agent.OrgID != org.ID { c.String(http.StatusBadRequest, "Agent does not belong to this organization") return } @@ -378,12 +368,7 @@ func PatchOrgAgent(c *gin.Context) { // @Param agent_id path int true "the agent's id" func DeleteOrgAgent(c *gin.Context) { _store := store.FromContext(c) - - orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64) - if err != nil { - c.String(http.StatusBadRequest, "Invalid organization ID") - return - } + org := session.Org(c) agentID, err := strconv.ParseInt(c.Param("agent_id"), 10, 64) if err != nil { @@ -397,7 +382,7 @@ func DeleteOrgAgent(c *gin.Context) { return } - if agent.OrgID != orgID { + if agent.OrgID != org.ID { c.String(http.StatusBadRequest, "Agent does not belong to this organization") return } diff --git a/server/api/org.go b/server/api/org.go index 227859558..889398e7d 100644 --- a/server/api/org.go +++ b/server/api/org.go @@ -16,7 +16,6 @@ package api import ( "net/http" - "strconv" "strings" "github.com/gin-gonic/gin" @@ -58,20 +57,7 @@ func GetOrgs(c *gin.Context) { // @Param Authorization header string true "Insert your personal access token" default(Bearer ) // @Param org_id path string true "the organization's id" func GetOrg(c *gin.Context) { - _store := store.FromContext(c) - - orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64) - if err != nil { - c.String(http.StatusBadRequest, "Error parsing org id. %s", err) - return - } - - org, err := _store.OrgGet(orgID) - if err != nil { - handleDBError(c, err) - return - } - + org := session.Org(c) c.JSON(http.StatusOK, org) } @@ -86,7 +72,7 @@ func GetOrg(c *gin.Context) { // @Param org_id path string true "the organization's id" func GetOrgPermissions(c *gin.Context) { user := session.User(c) - _store := store.FromContext(c) + org := session.Org(c) _forge, err := server.Config.Services.Manager.ForgeFromUser(user) if err != nil { @@ -95,23 +81,11 @@ func GetOrgPermissions(c *gin.Context) { return } - orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64) - if err != nil { - c.String(http.StatusBadRequest, "Error parsing org id. %s", err) - return - } - if user == nil { c.JSON(http.StatusOK, &model.OrgPerm{}) return } - org, err := _store.OrgGet(orgID) - if err != nil { - c.String(http.StatusInternalServerError, "Error getting org %d. %s", orgID, err) - return - } - if (org.IsUser && org.Name == user.Login) || (user.Admin && !org.IsUser) { c.JSON(http.StatusOK, &model.OrgPerm{ Member: true, @@ -125,7 +99,7 @@ func GetOrgPermissions(c *gin.Context) { perm, err := server.Config.Services.Membership.Get(c, _forge, user, org.Name) if err != nil { - c.String(http.StatusInternalServerError, "Error getting membership for %d. %s", orgID, err) + c.String(http.StatusInternalServerError, "Error getting membership for %d. %s", org.ID, err) return } @@ -200,15 +174,9 @@ func LookupOrg(c *gin.Context) { // @Param id path string true "the org's id" func DeleteOrg(c *gin.Context) { _store := store.FromContext(c) + org := session.Org(c) - orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64) - if err != nil { - c.String(http.StatusBadRequest, "Error parsing org id. %s", err) - return - } - - err = _store.OrgDelete(orgID) - if err != nil { + if err := _store.OrgDelete(org.ID); err != nil { handleDBError(c, err) return } diff --git a/server/api/org_registry.go b/server/api/org_registry.go index b52782675..6476e2c53 100644 --- a/server/api/org_registry.go +++ b/server/api/org_registry.go @@ -16,7 +16,6 @@ package api import ( "net/http" - "strconv" "github.com/gin-gonic/gin" @@ -36,16 +35,11 @@ import ( // @Param org_id path string true "the org's id" // @Param registry path string true "the registry's address" func GetOrgRegistry(c *gin.Context) { + org := session.Org(c) addr := c.Param("registry") - orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64) - if err != nil { - c.String(http.StatusBadRequest, "Error parsing org id. %s", err) - return - } - registryService := server.Config.Services.Manager.RegistryService() - registry, err := registryService.OrgRegistryFind(orgID, addr) + registry, err := registryService.OrgRegistryFind(org.ID, addr) if err != nil { handleDBError(c, err) return @@ -65,16 +59,12 @@ func GetOrgRegistry(c *gin.Context) { // @Param page query int false "for response pagination, page offset number" default(1) // @Param perPage query int false "for response pagination, max items per page" default(50) func GetOrgRegistryList(c *gin.Context) { - orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64) - if err != nil { - c.String(http.StatusBadRequest, "Error parsing org id. %s", err) - return - } + org := session.Org(c) registryService := server.Config.Services.Manager.RegistryService() - list, err := registryService.OrgRegistryList(orgID, session.Pagination(c)) + list, err := registryService.OrgRegistryList(org.ID, session.Pagination(c)) if err != nil { - c.String(http.StatusInternalServerError, "Error getting registry list for %q. %s", orgID, err) + c.String(http.StatusInternalServerError, "Error getting registry list for %q. %s", org.ID, err) return } // copy the registry detail to remove the sensitive @@ -96,31 +86,27 @@ func GetOrgRegistryList(c *gin.Context) { // @Param org_id path string true "the org's id" // @Param registryData body Registry true "the new registry" func PostOrgRegistry(c *gin.Context) { - orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64) - if err != nil { - c.String(http.StatusBadRequest, "Error parsing org id. %s", err) - return - } + org := session.Org(c) in := new(model.Registry) if err := c.Bind(in); err != nil { - c.String(http.StatusBadRequest, "Error parsing org %q registry. %s", orgID, err) + c.String(http.StatusBadRequest, "Error parsing org %q registry. %s", org.ID, err) return } registry := &model.Registry{ - OrgID: orgID, + OrgID: org.ID, Address: in.Address, Username: in.Username, Password: in.Password, } if err := registry.Validate(); err != nil { - c.String(http.StatusUnprocessableEntity, "Error inserting org %q registry. %s", orgID, err) + c.String(http.StatusUnprocessableEntity, "Error inserting org %q registry. %s", org.ID, err) return } registryService := server.Config.Services.Manager.RegistryService() - if err := registryService.OrgRegistryCreate(orgID, registry); err != nil { - c.String(http.StatusInternalServerError, "Error inserting org %q registry %q. %s", orgID, in.Address, err) + if err := registryService.OrgRegistryCreate(org.ID, registry); err != nil { + c.String(http.StatusInternalServerError, "Error inserting org %q registry %q. %s", org.ID, in.Address, err) return } c.JSON(http.StatusOK, registry.Copy()) @@ -138,22 +124,17 @@ func PostOrgRegistry(c *gin.Context) { // @Param registry path string true "the registry's name" // @Param registryData body Registry true "the update registry data" func PatchOrgRegistry(c *gin.Context) { + org := session.Org(c) addr := c.Param("registry") - orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64) - if err != nil { - c.String(http.StatusBadRequest, "Error parsing org id. %s", err) - return - } in := new(model.Registry) - err = c.Bind(in) - if err != nil { + if err := c.Bind(in); err != nil { c.String(http.StatusBadRequest, "Error parsing registry. %s", err) return } registryService := server.Config.Services.Manager.RegistryService() - registry, err := registryService.OrgRegistryFind(orgID, addr) + registry, err := registryService.OrgRegistryFind(org.ID, addr) if err != nil { handleDBError(c, err) return @@ -169,12 +150,12 @@ func PatchOrgRegistry(c *gin.Context) { } if err := registry.Validate(); err != nil { - c.String(http.StatusUnprocessableEntity, "Error updating org %q registry. %s", orgID, err) + c.String(http.StatusUnprocessableEntity, "Error updating org %q registry. %s", org.ID, err) return } - if err := registryService.OrgRegistryUpdate(orgID, registry); err != nil { - c.String(http.StatusInternalServerError, "Error updating org %q registry %q. %s", orgID, in.Address, err) + if err := registryService.OrgRegistryUpdate(org.ID, registry); err != nil { + c.String(http.StatusInternalServerError, "Error updating org %q registry %q. %s", org.ID, in.Address, err) return } c.JSON(http.StatusOK, registry.Copy()) @@ -191,15 +172,11 @@ func PatchOrgRegistry(c *gin.Context) { // @Param org_id path string true "the org's id" // @Param registry path string true "the registry's name" func DeleteOrgRegistry(c *gin.Context) { + org := session.Org(c) addr := c.Param("registry") - orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64) - if err != nil { - c.String(http.StatusBadRequest, "Error parsing org id. %s", err) - return - } registryService := server.Config.Services.Manager.RegistryService() - if err := registryService.OrgRegistryDelete(orgID, addr); err != nil { + if err := registryService.OrgRegistryDelete(org.ID, addr); err != nil { handleDBError(c, err) return } diff --git a/server/api/org_secret.go b/server/api/org_secret.go index 98f67e089..c892e8780 100644 --- a/server/api/org_secret.go +++ b/server/api/org_secret.go @@ -16,7 +16,6 @@ package api import ( "net/http" - "strconv" "github.com/gin-gonic/gin" @@ -36,16 +35,11 @@ import ( // @Param org_id path string true "the org's id" // @Param secret path string true "the secret's name" func GetOrgSecret(c *gin.Context) { + org := session.Org(c) name := c.Param("secret") - orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64) - if err != nil { - c.String(http.StatusBadRequest, "Error parsing org id. %s", err) - return - } - secretService := server.Config.Services.Manager.SecretService() - secret, err := secretService.OrgSecretFind(orgID, name) + secret, err := secretService.OrgSecretFind(org.ID, name) if err != nil { handleDBError(c, err) return @@ -65,16 +59,12 @@ func GetOrgSecret(c *gin.Context) { // @Param page query int false "for response pagination, page offset number" default(1) // @Param perPage query int false "for response pagination, max items per page" default(50) func GetOrgSecretList(c *gin.Context) { - orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64) - if err != nil { - c.String(http.StatusBadRequest, "Error parsing org id. %s", err) - return - } + org := session.Org(c) secretService := server.Config.Services.Manager.SecretService() - list, err := secretService.OrgSecretList(orgID, session.Pagination(c)) + list, err := secretService.OrgSecretList(org.ID, session.Pagination(c)) if err != nil { - c.String(http.StatusInternalServerError, "Error getting secret list for %q. %s", orgID, err) + c.String(http.StatusInternalServerError, "Error getting secret list for %q. %s", org.ID, err) return } // copy the secret detail to remove the sensitive @@ -96,32 +86,28 @@ func GetOrgSecretList(c *gin.Context) { // @Param org_id path string true "the org's id" // @Param secretData body Secret true "the new secret" func PostOrgSecret(c *gin.Context) { - orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64) - if err != nil { - c.String(http.StatusBadRequest, "Error parsing org id. %s", err) - return - } + org := session.Org(c) in := new(model.Secret) if err := c.Bind(in); err != nil { - c.String(http.StatusBadRequest, "Error parsing org %q secret. %s", orgID, err) + c.String(http.StatusBadRequest, "Error parsing org %q secret. %s", org.ID, err) return } secret := &model.Secret{ - OrgID: orgID, + OrgID: org.ID, Name: in.Name, Value: in.Value, Events: in.Events, Images: in.Images, } if err := secret.Validate(); err != nil { - c.String(http.StatusUnprocessableEntity, "Error inserting org %q secret. %s", orgID, err) + c.String(http.StatusUnprocessableEntity, "Error inserting org %q secret. %s", org.ID, err) return } secretService := server.Config.Services.Manager.SecretService() - if err := secretService.OrgSecretCreate(orgID, secret); err != nil { - c.String(http.StatusInternalServerError, "Error inserting org %q secret %q. %s", orgID, in.Name, err) + if err := secretService.OrgSecretCreate(org.ID, secret); err != nil { + c.String(http.StatusInternalServerError, "Error inserting org %q secret %q. %s", org.ID, in.Name, err) return } c.JSON(http.StatusOK, secret.Copy()) @@ -139,22 +125,17 @@ func PostOrgSecret(c *gin.Context) { // @Param secret path string true "the secret's name" // @Param secretData body Secret true "the update secret data" func PatchOrgSecret(c *gin.Context) { + org := session.Org(c) name := c.Param("secret") - orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64) - if err != nil { - c.String(http.StatusBadRequest, "Error parsing org id. %s", err) - return - } in := new(model.Secret) - err = c.Bind(in) - if err != nil { + if err := c.Bind(in); err != nil { c.String(http.StatusBadRequest, "Error parsing secret. %s", err) return } secretService := server.Config.Services.Manager.SecretService() - secret, err := secretService.OrgSecretFind(orgID, name) + secret, err := secretService.OrgSecretFind(org.ID, name) if err != nil { handleDBError(c, err) return @@ -170,12 +151,12 @@ func PatchOrgSecret(c *gin.Context) { } if err := secret.Validate(); err != nil { - c.String(http.StatusUnprocessableEntity, "Error updating org %q secret. %s", orgID, err) + c.String(http.StatusUnprocessableEntity, "Error updating org %q secret. %s", org.ID, err) return } - if err := secretService.OrgSecretUpdate(orgID, secret); err != nil { - c.String(http.StatusInternalServerError, "Error updating org %q secret %q. %s", orgID, in.Name, err) + if err := secretService.OrgSecretUpdate(org.ID, secret); err != nil { + c.String(http.StatusInternalServerError, "Error updating org %q secret %q. %s", org.ID, in.Name, err) return } c.JSON(http.StatusOK, secret.Copy()) @@ -192,15 +173,11 @@ func PatchOrgSecret(c *gin.Context) { // @Param org_id path string true "the org's id" // @Param secret path string true "the secret's name" func DeleteOrgSecret(c *gin.Context) { + org := session.Org(c) name := c.Param("secret") - orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64) - if err != nil { - c.String(http.StatusBadRequest, "Error parsing org id. %s", err) - return - } secretService := server.Config.Services.Manager.SecretService() - if err := secretService.OrgSecretDelete(orgID, name); err != nil { + if err := secretService.OrgSecretDelete(org.ID, name); err != nil { handleDBError(c, err) return } diff --git a/server/router/api.go b/server/router/api.go index 8d3ca2bcb..2a514904d 100644 --- a/server/router/api.go +++ b/server/router/api.go @@ -52,13 +52,15 @@ func apiRoutes(e *gin.RouterGroup) { orgs.GET("/lookup/*org_full_name", api.LookupOrg) orgBase := orgs.Group("/:org_id") { + orgBase.Use(session.SetOrg()) + orgBase.Use(session.MustOrg()) orgBase.GET("/permissions", api.GetOrgPermissions) + orgBase.GET("", session.MustOrgMember(false), api.GetOrg) org := orgBase.Group("") { org.Use(session.MustOrgMember(true)) org.DELETE("", session.MustAdmin(), api.DeleteOrg) - org.GET("", api.GetOrg) org.GET("/secrets", api.GetOrgSecretList) org.POST("/secrets", api.PostOrgSecret) diff --git a/server/router/middleware/session/org.go b/server/router/middleware/session/org.go new file mode 100644 index 000000000..0ce2af2ab --- /dev/null +++ b/server/router/middleware/session/org.go @@ -0,0 +1,86 @@ +// Copyright 2024 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package session + +import ( + "errors" + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + + "go.woodpecker-ci.org/woodpecker/v2/server/model" + "go.woodpecker-ci.org/woodpecker/v2/server/store" + "go.woodpecker-ci.org/woodpecker/v2/server/store/types" +) + +func Org(c *gin.Context) *model.Org { + v, ok := c.Get("org") + if !ok { + return nil + } + r, ok := v.(*model.Org) + if !ok { + return nil + } + return r +} + +func SetOrg() gin.HandlerFunc { + return func(c *gin.Context) { + var ( + orgID int64 + err error + ) + + orgParam := c.Param("org_id") + if orgParam != "" { + orgID, err = strconv.ParseInt(orgParam, 10, 64) + if err != nil { + c.String(http.StatusBadRequest, "Invalid organization ID") + c.Abort() + return + } + } + + org, err := store.FromContext(c).OrgGet(orgID) + if err != nil && !errors.Is(err, types.RecordNotExist) { + _ = c.AbortWithError(http.StatusInternalServerError, err) + return + } + + if org == nil { + c.String(http.StatusNotFound, "Organization not found") + c.Abort() + return + } + + c.Set("org", org) + c.Next() + } +} + +func MustOrg() gin.HandlerFunc { + return func(c *gin.Context) { + org := Org(c) + switch { + case org == nil: + c.String(http.StatusNotFound, "Organization not loaded") + c.Abort() + default: + c.Next() + } + } +} diff --git a/server/router/middleware/session/user.go b/server/router/middleware/session/user.go index 2f098b5cb..862d82df6 100644 --- a/server/router/middleware/session/user.go +++ b/server/router/middleware/session/user.go @@ -122,8 +122,6 @@ func MustUser() gin.HandlerFunc { func MustOrgMember(admin bool) gin.HandlerFunc { return func(c *gin.Context) { - _store := store.FromContext(c) - user := User(c) if user == nil { c.String(http.StatusUnauthorized, "User not authorized") @@ -131,15 +129,10 @@ func MustOrgMember(admin bool) gin.HandlerFunc { return } - orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64) - if err != nil { - c.String(http.StatusBadRequest, "Error parsing org id. %s", err) - return - } - - org, err := _store.OrgGet(orgID) - if err != nil { - c.String(http.StatusNotFound, "Organization not found") + org := Org(c) + if org == nil { + c.String(http.StatusBadRequest, "Organization not loaded") + c.Abort() return }