mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2026-04-15 01:41:56 +00:00
Use id to access orgs (#1873)
closes #1743 fixes: setting secrets for own user namespace - create org in database - use orgID for org related APIs Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
+11
-2
@@ -86,7 +86,7 @@ func HandleAuth(c *gin.Context) {
|
||||
if len(config.Orgs) != 0 {
|
||||
teams, terr := _forge.Teams(c, tmpuser)
|
||||
if terr != nil || !config.IsMember(teams) {
|
||||
log.Error().Msgf("cannot verify team membership for %s.", u.Login)
|
||||
log.Error().Err(terr).Msgf("cannot verify team membership for %s.", u.Login)
|
||||
c.Redirect(303, "/login?error=access_denied")
|
||||
return
|
||||
}
|
||||
@@ -111,6 +111,15 @@ func HandleAuth(c *gin.Context) {
|
||||
c.Redirect(http.StatusSeeOther, "/login?error=internal_error")
|
||||
return
|
||||
}
|
||||
|
||||
// if another user already have activated repos on behave of that user,
|
||||
// the user was stored as org. now we adopt it to the user.
|
||||
if org, err := _store.OrgFindByName(u.Login); err == nil && org != nil {
|
||||
org.IsUser = true
|
||||
if err := _store.OrgUpdate(org); err != nil {
|
||||
log.Error().Err(err).Msgf("on user creation, could not mark org as user")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the user meta data and authorization data.
|
||||
@@ -127,7 +136,7 @@ func HandleAuth(c *gin.Context) {
|
||||
if len(config.Orgs) != 0 {
|
||||
teams, terr := _forge.Teams(c, u)
|
||||
if terr != nil || !config.IsMember(teams) {
|
||||
log.Error().Msgf("cannot verify team membership for %s.", u.Login)
|
||||
log.Error().Err(terr).Msgf("cannot verify team membership for %s.", u.Login)
|
||||
c.Redirect(http.StatusSeeOther, "/login?error=access_denied")
|
||||
return
|
||||
}
|
||||
|
||||
+117
-9
@@ -15,41 +15,149 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/woodpecker-ci/woodpecker/server"
|
||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||
"github.com/woodpecker-ci/woodpecker/server/router/middleware/session"
|
||||
"github.com/woodpecker-ci/woodpecker/server/store"
|
||||
"github.com/woodpecker-ci/woodpecker/server/store/types"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// GetOrg
|
||||
//
|
||||
// @Summary Get organization by id
|
||||
// @Router /orgs/{org_id} [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} Org
|
||||
// @Tags Organization
|
||||
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||
// @Param org_id path string true "the organziation'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 {
|
||||
if errors.Is(err, types.RecordNotExist) {
|
||||
c.AbortWithStatus(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, org)
|
||||
}
|
||||
|
||||
// GetOrgPermissions
|
||||
//
|
||||
// @Summary Get the permissions of the current user in the given organization
|
||||
// @Router /orgs/{owner}/permissions [get]
|
||||
// @Router /orgs/{org_id}/permissions [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} OrgPerm
|
||||
// @Tags Organization permissions
|
||||
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||
// @Param owner path string true "the owner's name"
|
||||
// @Param org_id path string true "the organziation's id"
|
||||
func GetOrgPermissions(c *gin.Context) {
|
||||
var (
|
||||
err error
|
||||
user = session.User(c)
|
||||
owner = c.Param("owner")
|
||||
)
|
||||
user := session.User(c)
|
||||
_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
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
c.JSON(http.StatusOK, &model.OrgPerm{})
|
||||
return
|
||||
}
|
||||
|
||||
perm, err := server.Config.Services.Membership.Get(c, user, owner)
|
||||
org, err := _store.OrgGet(orgID)
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error getting membership for %q. %s", owner, err)
|
||||
c.String(http.StatusInternalServerError, "Error getting org %d. %s", orgID, err)
|
||||
return
|
||||
}
|
||||
|
||||
if (org.IsUser && org.Name == user.Login) || user.Admin {
|
||||
c.JSON(http.StatusOK, &model.OrgPerm{
|
||||
Member: true,
|
||||
Admin: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
perm, err := server.Config.Services.Membership.Get(c, user, org.Name)
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error getting membership for %d. %s", orgID, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, perm)
|
||||
}
|
||||
|
||||
// LookupOrg
|
||||
//
|
||||
// @Summary Lookup organization by full-name
|
||||
// @Router /org/lookup/{org_full_name} [get]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Org
|
||||
// @Tags Organizations
|
||||
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||
// @Param org_full_name path string true "the organizations full-name / slug"
|
||||
func LookupOrg(c *gin.Context) {
|
||||
_store := store.FromContext(c)
|
||||
|
||||
orgFullName := strings.TrimLeft(c.Param("org_full_name"), "/")
|
||||
|
||||
org, err := _store.OrgFindByName(orgFullName)
|
||||
if err != nil {
|
||||
if errors.Is(err, types.RecordNotExist) {
|
||||
c.AbortWithStatus(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
// don't leak private org infos
|
||||
if org.Private {
|
||||
user := session.User(c)
|
||||
if user == nil {
|
||||
c.AbortWithStatus(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if !user.Admin && org.Name != user.Login {
|
||||
c.AbortWithStatus(http.StatusNotFound)
|
||||
return
|
||||
} else if !user.Admin {
|
||||
perm, err := server.Config.Services.Membership.Get(c, user, org.Name)
|
||||
if err != nil {
|
||||
log.Error().Msgf("Failed to check membership: %v", err)
|
||||
c.String(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if perm == nil || !perm.Member {
|
||||
c.AbortWithStatus(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, org)
|
||||
}
|
||||
|
||||
+62
-43
@@ -16,6 +16,7 @@ package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/woodpecker-ci/woodpecker/server/router/middleware/session"
|
||||
@@ -27,19 +28,23 @@ import (
|
||||
// GetOrgSecret
|
||||
//
|
||||
// @Summary Get the named organization secret
|
||||
// @Router /orgs/{owner}/secrets/{secret} [get]
|
||||
// @Router /orgs/{org_id}/secrets/{secret} [get]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Secret
|
||||
// @Tags Organization secrets
|
||||
// @Tags Organization secrets
|
||||
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||
// @Param owner path string true "the owner's name"
|
||||
// @Param org_id path string true "the org's id"
|
||||
// @Param secret path string true "the secret's name"
|
||||
func GetOrgSecret(c *gin.Context) {
|
||||
var (
|
||||
owner = c.Param("owner")
|
||||
name = c.Param("secret")
|
||||
)
|
||||
secret, err := server.Config.Services.Secrets.OrgSecretFind(owner, name)
|
||||
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
|
||||
}
|
||||
|
||||
secret, err := server.Config.Services.Secrets.OrgSecretFind(orgID, name)
|
||||
if err != nil {
|
||||
handleDbGetError(c, err)
|
||||
return
|
||||
@@ -50,19 +55,24 @@ func GetOrgSecret(c *gin.Context) {
|
||||
// GetOrgSecretList
|
||||
//
|
||||
// @Summary Get the organization secret list
|
||||
// @Router /orgs/{owner}/secrets [get]
|
||||
// @Router /orgs/{org_id}/secrets [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} Secret
|
||||
// @Tags Organization secrets
|
||||
// @Tags Organization secrets
|
||||
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||
// @Param owner path string true "the owner's name"
|
||||
// @Param org_id path string true "the org's id"
|
||||
// @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)
|
||||
// @Param perPage query int false "for response pagination, max items per page" default(50)
|
||||
func GetOrgSecretList(c *gin.Context) {
|
||||
owner := c.Param("owner")
|
||||
list, err := server.Config.Services.Secrets.OrgSecretList(owner, session.Pagination(c))
|
||||
orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64)
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error getting secret list for %q. %s", owner, err)
|
||||
c.String(http.StatusBadRequest, "Error parsing org id. %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
list, err := server.Config.Services.Secrets.OrgSecretList(orgID, session.Pagination(c))
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error getting secret list for %q. %s", orgID, err)
|
||||
return
|
||||
}
|
||||
// copy the secret detail to remove the sensitive
|
||||
@@ -79,20 +89,24 @@ func GetOrgSecretList(c *gin.Context) {
|
||||
// @Router /orgs/{owner}/secrets [post]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Secret
|
||||
// @Tags Organization secrets
|
||||
// @Tags Organization secrets
|
||||
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||
// @Param owner path string true "the owner's name"
|
||||
// @Param org_id path string true "the org's id"
|
||||
// @Param secretData body Secret true "the new secret"
|
||||
func PostOrgSecret(c *gin.Context) {
|
||||
owner := c.Param("owner")
|
||||
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)
|
||||
if err := c.Bind(in); err != nil {
|
||||
c.String(http.StatusBadRequest, "Error parsing org %q secret. %s", owner, err)
|
||||
c.String(http.StatusBadRequest, "Error parsing org %q secret. %s", orgID, err)
|
||||
return
|
||||
}
|
||||
secret := &model.Secret{
|
||||
Owner: owner,
|
||||
OrgID: orgID,
|
||||
Name: in.Name,
|
||||
Value: in.Value,
|
||||
Events: in.Events,
|
||||
@@ -100,11 +114,11 @@ func PostOrgSecret(c *gin.Context) {
|
||||
PluginsOnly: in.PluginsOnly,
|
||||
}
|
||||
if err := secret.Validate(); err != nil {
|
||||
c.String(http.StatusUnprocessableEntity, "Error inserting org %q secret. %s", owner, err)
|
||||
c.String(http.StatusUnprocessableEntity, "Error inserting org %q secret. %s", orgID, err)
|
||||
return
|
||||
}
|
||||
if err := server.Config.Services.Secrets.OrgSecretCreate(owner, secret); err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error inserting org %q secret %q. %s", owner, in.Name, err)
|
||||
if err := server.Config.Services.Secrets.OrgSecretCreate(orgID, secret); err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error inserting org %q secret %q. %s", orgID, in.Name, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, secret.Copy())
|
||||
@@ -113,28 +127,30 @@ func PostOrgSecret(c *gin.Context) {
|
||||
// PatchOrgSecret
|
||||
//
|
||||
// @Summary Update an organization secret
|
||||
// @Router /orgs/{owner}/secrets/{secret} [patch]
|
||||
// @Router /orgs/{org_id}/secrets/{secret} [patch]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Secret
|
||||
// @Tags Organization secrets
|
||||
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||
// @Param owner path string true "the owner's name"
|
||||
// @Param org_id path string true "the org's id"
|
||||
// @Param secret path string true "the secret's name"
|
||||
// @Param secretData body Secret true "the update secret data"
|
||||
// @Param secretData body Secret true "the update secret data"
|
||||
func PatchOrgSecret(c *gin.Context) {
|
||||
var (
|
||||
owner = c.Param("owner")
|
||||
name = c.Param("secret")
|
||||
)
|
||||
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)
|
||||
err = c.Bind(in)
|
||||
if err != nil {
|
||||
c.String(http.StatusBadRequest, "Error parsing secret. %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
secret, err := server.Config.Services.Secrets.OrgSecretFind(owner, name)
|
||||
secret, err := server.Config.Services.Secrets.OrgSecretFind(orgID, name)
|
||||
if err != nil {
|
||||
handleDbGetError(c, err)
|
||||
return
|
||||
@@ -151,11 +167,11 @@ func PatchOrgSecret(c *gin.Context) {
|
||||
secret.PluginsOnly = in.PluginsOnly
|
||||
|
||||
if err := secret.Validate(); err != nil {
|
||||
c.String(http.StatusUnprocessableEntity, "Error updating org %q secret. %s", owner, err)
|
||||
c.String(http.StatusUnprocessableEntity, "Error updating org %q secret. %s", orgID, err)
|
||||
return
|
||||
}
|
||||
if err := server.Config.Services.Secrets.OrgSecretUpdate(owner, secret); err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error updating org %q secret %q. %s", owner, in.Name, err)
|
||||
if err := server.Config.Services.Secrets.OrgSecretUpdate(orgID, secret); err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error updating org %q secret %q. %s", orgID, in.Name, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, secret.Copy())
|
||||
@@ -164,19 +180,22 @@ func PatchOrgSecret(c *gin.Context) {
|
||||
// DeleteOrgSecret
|
||||
//
|
||||
// @Summary Delete the named secret from an organization
|
||||
// @Router /orgs/{owner}/secrets/{secret} [delete]
|
||||
// @Router /orgs/{org_id}/secrets/{secret} [delete]
|
||||
// @Produce plain
|
||||
// @Success 200
|
||||
// @Tags Organization secrets
|
||||
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||
// @Param owner path string true "the owner's name"
|
||||
// @Param secret path string true "the secret's name"
|
||||
// @Param org_id path string true "the org's id"
|
||||
// @Param secret path string true "the secret's name"
|
||||
func DeleteOrgSecret(c *gin.Context) {
|
||||
var (
|
||||
owner = c.Param("owner")
|
||||
name = c.Param("secret")
|
||||
)
|
||||
if err := server.Config.Services.Secrets.OrgSecretDelete(owner, name); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
if err := server.Config.Services.Secrets.OrgSecretDelete(orgID, name); err != nil {
|
||||
handleDbGetError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -119,6 +119,31 @@ func PostRepo(c *gin.Context) {
|
||||
sig,
|
||||
)
|
||||
|
||||
// find org of repo
|
||||
var org *model.Org
|
||||
org, err = _store.OrgFindByName(repo.Owner)
|
||||
if err != nil && !errors.Is(err, types.RecordNotExist) {
|
||||
c.String(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// create an org if it doesn't exist yet
|
||||
if errors.Is(err, types.RecordNotExist) {
|
||||
org, err = forge.Org(c, user, repo.Owner)
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "Could not fetch organization from forge.")
|
||||
return
|
||||
}
|
||||
|
||||
err = _store.OrgCreate(org)
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
repo.OrgID = org.ID
|
||||
|
||||
err = forge.Activate(c, user, repo, link)
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, err.Error())
|
||||
|
||||
Reference in New Issue
Block a user