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())
|
||||
|
||||
Vendored
+13
-12
@@ -16,6 +16,7 @@ package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/woodpecker-ci/woodpecker/server/forge"
|
||||
@@ -27,37 +28,37 @@ import (
|
||||
// MembershipService is a service to check for user membership.
|
||||
type MembershipService interface {
|
||||
// Get returns if the user is a member of the organization.
|
||||
Get(ctx context.Context, u *model.User, name string) (*model.OrgPerm, error)
|
||||
Get(ctx context.Context, u *model.User, org string) (*model.OrgPerm, error)
|
||||
}
|
||||
|
||||
type membershipCache struct {
|
||||
Forge forge.Forge
|
||||
Cache *ttlcache.Cache[string, *model.OrgPerm]
|
||||
TTL time.Duration
|
||||
forge forge.Forge
|
||||
cache *ttlcache.Cache[string, *model.OrgPerm]
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
// NewMembershipService creates a new membership service.
|
||||
func NewMembershipService(f forge.Forge) MembershipService {
|
||||
return &membershipCache{
|
||||
TTL: 10 * time.Minute,
|
||||
Forge: f,
|
||||
Cache: ttlcache.New(ttlcache.WithDisableTouchOnHit[string, *model.OrgPerm]()),
|
||||
ttl: 10 * time.Minute,
|
||||
forge: f,
|
||||
cache: ttlcache.New(ttlcache.WithDisableTouchOnHit[string, *model.OrgPerm]()),
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns if the user is a member of the organization.
|
||||
func (c *membershipCache) Get(ctx context.Context, u *model.User, name string) (*model.OrgPerm, error) {
|
||||
key := u.Login + "/" + name
|
||||
func (c *membershipCache) Get(ctx context.Context, u *model.User, org string) (*model.OrgPerm, error) {
|
||||
key := fmt.Sprintf("%s-%s", u.ForgeRemoteID, org)
|
||||
// Error can be safely ignored, as cache can only return error from loaders.
|
||||
item, _ := c.Cache.Get(key)
|
||||
item, _ := c.cache.Get(key)
|
||||
if item != nil && !item.IsExpired() {
|
||||
return item.Value(), nil
|
||||
}
|
||||
|
||||
perm, err := c.Forge.OrgMembership(ctx, u, name)
|
||||
perm, err := c.forge.OrgMembership(ctx, u, org)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Cache.Set(key, perm, c.TTL)
|
||||
c.cache.Set(key, perm, c.ttl)
|
||||
return perm, nil
|
||||
}
|
||||
|
||||
@@ -334,6 +334,18 @@ func (c *config) OrgMembership(ctx context.Context, u *model.User, owner string)
|
||||
return &model.OrgPerm{Member: perm != "", Admin: perm == "owner"}, nil
|
||||
}
|
||||
|
||||
func (c *config) Org(ctx context.Context, u *model.User, owner string) (*model.Org, error) {
|
||||
workspace, err := c.newClient(ctx, u).GetWorkspace(owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.Org{
|
||||
Name: workspace.Slug,
|
||||
IsUser: false, // bitbucket uses workspaces (similar to orgs) for teams and single users so we can not distinguish between them
|
||||
}, nil
|
||||
}
|
||||
|
||||
// helper function to return the bitbucket oauth2 client
|
||||
func (c *config) newClient(ctx context.Context, u *model.User) *internal.Client {
|
||||
if u == nil {
|
||||
|
||||
@@ -226,6 +226,13 @@ func (c *Client) ListPullRequests(owner, name string, opts *ListOpts) ([]*PullRe
|
||||
return out.Values, err
|
||||
}
|
||||
|
||||
func (c *Client) GetWorkspace(name string) (*Workspace, error) {
|
||||
out := new(Workspace)
|
||||
uri := fmt.Sprintf(pathWorkspace, c.base, name)
|
||||
_, err := c.do(uri, get, nil, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (c *Client) do(rawurl, method string, in, out interface{}) (*string, error) {
|
||||
uri, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
|
||||
@@ -52,7 +52,7 @@ type Forge interface {
|
||||
// Repos fetches a list of repos from the forge.
|
||||
Repos(ctx context.Context, u *model.User) ([]*model.Repo, error)
|
||||
|
||||
// File fetches a file from the forge repository and returns in string
|
||||
// File fetches a file from the forge repository and returns it in string
|
||||
// format.
|
||||
File(ctx context.Context, u *model.User, r *model.Repo, b *model.Pipeline, f string) ([]byte, error)
|
||||
|
||||
@@ -89,7 +89,10 @@ type Forge interface {
|
||||
|
||||
// OrgMembership returns if user is member of organization and if user
|
||||
// is admin/owner in that organization.
|
||||
OrgMembership(ctx context.Context, u *model.User, owner string) (*model.OrgPerm, error)
|
||||
OrgMembership(ctx context.Context, u *model.User, org string) (*model.OrgPerm, error)
|
||||
|
||||
// Org fetches the organization from the forge by name. If the name is a user an org with type user is returned.
|
||||
Org(ctx context.Context, u *model.User, org string) (*model.Org, error)
|
||||
}
|
||||
|
||||
// Refresher refreshes an oauth token and expiration for the given user. It
|
||||
|
||||
@@ -528,6 +528,32 @@ func (c *Gitea) OrgMembership(ctx context.Context, u *model.User, owner string)
|
||||
return &model.OrgPerm{Member: member, Admin: perm.IsAdmin || perm.IsOwner}, nil
|
||||
}
|
||||
|
||||
func (c *Gitea) Org(ctx context.Context, u *model.User, owner string) (*model.Org, error) {
|
||||
client, err := c.newClientToken(ctx, u.Token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user, _, err := client.GetUserInfo(owner)
|
||||
if user != nil && err == nil {
|
||||
return &model.Org{
|
||||
Name: user.UserName,
|
||||
IsUser: true,
|
||||
Private: user.Visibility != gitea.VisibleTypePublic,
|
||||
}, nil
|
||||
}
|
||||
|
||||
org, _, err := client.GetOrg(owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.Org{
|
||||
Name: org.UserName,
|
||||
Private: gitea.VisibleType(org.Visibility) != gitea.VisibleTypePublic,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// helper function to return the Gitea client with Token
|
||||
func (c *Gitea) newClientToken(ctx context.Context, token string) (*gitea.Client, error) {
|
||||
httpClient := &http.Client{}
|
||||
|
||||
@@ -350,6 +350,27 @@ func (c *client) OrgMembership(ctx context.Context, u *model.User, owner string)
|
||||
return &model.OrgPerm{Member: org.GetState() == "active", Admin: org.GetRole() == "admin"}, nil
|
||||
}
|
||||
|
||||
func (c *client) Org(ctx context.Context, u *model.User, owner string) (*model.Org, error) {
|
||||
client := c.newClientToken(ctx, u.Token)
|
||||
|
||||
user, _, err := client.Users.Get(ctx, owner)
|
||||
if user != nil && err == nil {
|
||||
return &model.Org{
|
||||
Name: user.GetName(),
|
||||
IsUser: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
org, _, err := client.Organizations.Get(ctx, owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.Org{
|
||||
Name: org.GetName(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// helper function to return the GitHub oauth2 context using an HTTPClient that
|
||||
// disables TLS verification if disabled in the forge settings.
|
||||
func (c *client) newContext(ctx context.Context) context.Context {
|
||||
|
||||
@@ -680,6 +680,48 @@ func (g *GitLab) OrgMembership(ctx context.Context, u *model.User, owner string)
|
||||
return &model.OrgPerm{}, nil
|
||||
}
|
||||
|
||||
func (g *GitLab) Org(ctx context.Context, u *model.User, owner string) (*model.Org, error) {
|
||||
client, err := newClient(g.url, u.Token, g.SkipVerify)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
users, _, err := client.Users.ListUsers(&gitlab.ListUsersOptions{
|
||||
ListOptions: gitlab.ListOptions{
|
||||
Page: 1,
|
||||
PerPage: 1,
|
||||
},
|
||||
Username: gitlab.String(owner),
|
||||
})
|
||||
if len(users) == 1 && err == nil {
|
||||
return &model.Org{
|
||||
Name: users[0].Username,
|
||||
IsUser: true,
|
||||
Private: users[0].PrivateProfile,
|
||||
}, nil
|
||||
}
|
||||
|
||||
groups, _, err := client.Groups.ListGroups(&gitlab.ListGroupsOptions{
|
||||
ListOptions: gitlab.ListOptions{
|
||||
Page: 1,
|
||||
PerPage: 1,
|
||||
},
|
||||
Search: gitlab.String(owner),
|
||||
}, gitlab.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(groups) != 1 {
|
||||
return nil, fmt.Errorf("could not find org %s", owner)
|
||||
}
|
||||
|
||||
return &model.Org{
|
||||
Name: groups[0].Name,
|
||||
Private: groups[0].Visibility != gitlab.PublicVisibility,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g *GitLab) loadChangedFilesFromMergeRequest(ctx context.Context, tmpRepo *model.Repo, pipeline *model.Pipeline, mergeIID int) (*model.Pipeline, error) {
|
||||
_store, ok := store.TryFromContext(ctx)
|
||||
if !ok {
|
||||
|
||||
@@ -274,17 +274,43 @@ func (_m *Forge) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// OrgMembership provides a mock function with given fields: ctx, u, owner
|
||||
func (_m *Forge) OrgMembership(ctx context.Context, u *model.User, owner string) (*model.OrgPerm, error) {
|
||||
ret := _m.Called(ctx, u, owner)
|
||||
// Org provides a mock function with given fields: ctx, u, org
|
||||
func (_m *Forge) Org(ctx context.Context, u *model.User, org string) (*model.Org, error) {
|
||||
ret := _m.Called(ctx, u, org)
|
||||
|
||||
var r0 *model.Org
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *model.User, string) (*model.Org, error)); ok {
|
||||
return rf(ctx, u, org)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *model.User, string) *model.Org); ok {
|
||||
r0 = rf(ctx, u, org)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.Org)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *model.User, string) error); ok {
|
||||
r1 = rf(ctx, u, org)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// OrgMembership provides a mock function with given fields: ctx, u, org
|
||||
func (_m *Forge) OrgMembership(ctx context.Context, u *model.User, org string) (*model.OrgPerm, error) {
|
||||
ret := _m.Called(ctx, u, org)
|
||||
|
||||
var r0 *model.OrgPerm
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *model.User, string) (*model.OrgPerm, error)); ok {
|
||||
return rf(ctx, u, owner)
|
||||
return rf(ctx, u, org)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *model.User, string) *model.OrgPerm); ok {
|
||||
r0 = rf(ctx, u, owner)
|
||||
r0 = rf(ctx, u, org)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.OrgPerm)
|
||||
@@ -292,7 +318,7 @@ func (_m *Forge) OrgMembership(ctx context.Context, u *model.User, owner string)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *model.User, string) error); ok {
|
||||
r1 = rf(ctx, u, owner)
|
||||
r1 = rf(ctx, u, org)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2023 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 model
|
||||
|
||||
// Org represents an organization.
|
||||
type Org struct {
|
||||
ID int64 `json:"id,omitempty" xorm:"pk autoincr 'id'"`
|
||||
Name string `json:"name" xorm:"UNIQUE 'name'"`
|
||||
IsUser bool `json:"is_user" xorm:"is_user"`
|
||||
// if name lookup has to check for membership or not
|
||||
Private bool `json:"-" xorm:"private"`
|
||||
} // @name Org
|
||||
|
||||
// TableName return database table name for xorm
|
||||
func (Org) TableName() string {
|
||||
return "orgs"
|
||||
}
|
||||
@@ -26,6 +26,7 @@ type Repo struct {
|
||||
UserID int64 `json:"-" xorm:"repo_user_id"`
|
||||
// ForgeRemoteID is the unique identifier for the repository on the forge.
|
||||
ForgeRemoteID ForgeRemoteID `json:"forge_remote_id" xorm:"forge_remote_id"`
|
||||
OrgID int64 `json:"org_id" xorm:"repo_org_id"`
|
||||
Owner string `json:"owner" xorm:"UNIQUE(name) 'repo_owner'"`
|
||||
Name string `json:"name" xorm:"UNIQUE(name) 'repo_name'"`
|
||||
FullName string `json:"full_name" xorm:"UNIQUE 'repo_full_name'"`
|
||||
|
||||
+11
-11
@@ -40,11 +40,11 @@ type SecretService interface {
|
||||
SecretUpdate(*Repo, *Secret) error
|
||||
SecretDelete(*Repo, string) error
|
||||
// Organization secrets
|
||||
OrgSecretFind(string, string) (*Secret, error)
|
||||
OrgSecretList(string, *ListOptions) ([]*Secret, error)
|
||||
OrgSecretCreate(string, *Secret) error
|
||||
OrgSecretUpdate(string, *Secret) error
|
||||
OrgSecretDelete(string, string) error
|
||||
OrgSecretFind(int64, string) (*Secret, error)
|
||||
OrgSecretList(int64, *ListOptions) ([]*Secret, error)
|
||||
OrgSecretCreate(int64, *Secret) error
|
||||
OrgSecretUpdate(int64, *Secret) error
|
||||
OrgSecretDelete(int64, string) error
|
||||
// Global secrets
|
||||
GlobalSecretFind(string) (*Secret, error)
|
||||
GlobalSecretList(*ListOptions) ([]*Secret, error)
|
||||
@@ -60,8 +60,8 @@ type SecretStore interface {
|
||||
SecretCreate(*Secret) error
|
||||
SecretUpdate(*Secret) error
|
||||
SecretDelete(*Secret) error
|
||||
OrgSecretFind(string, string) (*Secret, error)
|
||||
OrgSecretList(string, *ListOptions) ([]*Secret, error)
|
||||
OrgSecretFind(int64, string) (*Secret, error)
|
||||
OrgSecretList(int64, *ListOptions) ([]*Secret, error)
|
||||
GlobalSecretFind(string) (*Secret, error)
|
||||
GlobalSecretList(*ListOptions) ([]*Secret, error)
|
||||
SecretListAll() ([]*Secret, error)
|
||||
@@ -70,7 +70,7 @@ type SecretStore interface {
|
||||
// Secret represents a secret variable, such as a password or token.
|
||||
type Secret struct {
|
||||
ID int64 `json:"id" xorm:"pk autoincr 'secret_id'"`
|
||||
Owner string `json:"-" xorm:"NOT NULL DEFAULT '' UNIQUE(s) INDEX 'secret_owner'"`
|
||||
OrgID int64 `json:"-" xorm:"NOT NULL DEFAULT 0 UNIQUE(s) INDEX 'secret_org_id'"`
|
||||
RepoID int64 `json:"-" xorm:"NOT NULL DEFAULT 0 UNIQUE(s) INDEX 'secret_repo_id'"`
|
||||
Name string `json:"name" xorm:"NOT NULL UNIQUE(s) INDEX 'secret_name'"`
|
||||
Value string `json:"value,omitempty" xorm:"TEXT 'secret_value'"`
|
||||
@@ -93,12 +93,12 @@ func (s *Secret) BeforeInsert() {
|
||||
|
||||
// Global secret.
|
||||
func (s Secret) Global() bool {
|
||||
return s.RepoID == 0 && s.Owner == ""
|
||||
return s.RepoID == 0 && s.OrgID == 0
|
||||
}
|
||||
|
||||
// Organization secret.
|
||||
func (s Secret) Organization() bool {
|
||||
return s.RepoID == 0 && s.Owner != ""
|
||||
return s.RepoID == 0 && s.OrgID != 0
|
||||
}
|
||||
|
||||
// Match returns true if an image and event match the restricted list.
|
||||
@@ -155,7 +155,7 @@ func (s *Secret) Validate() error {
|
||||
func (s *Secret) Copy() *Secret {
|
||||
return &Secret{
|
||||
ID: s.ID,
|
||||
Owner: s.Owner,
|
||||
OrgID: s.OrgID,
|
||||
RepoID: s.RepoID,
|
||||
Name: s.Name,
|
||||
Images: s.Images,
|
||||
|
||||
@@ -99,7 +99,7 @@ func (wrapper *EncryptedSecretStore) SecretDelete(secret *model.Secret) error {
|
||||
return wrapper.store.SecretDelete(secret)
|
||||
}
|
||||
|
||||
func (wrapper *EncryptedSecretStore) OrgSecretFind(s, s2 string) (*model.Secret, error) {
|
||||
func (wrapper *EncryptedSecretStore) OrgSecretFind(s int64, s2 string) (*model.Secret, error) {
|
||||
result, err := wrapper.store.OrgSecretFind(s, s2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -112,7 +112,7 @@ func (wrapper *EncryptedSecretStore) OrgSecretFind(s, s2 string) (*model.Secret,
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (wrapper *EncryptedSecretStore) OrgSecretList(s string, p *model.ListOptions) ([]*model.Secret, error) {
|
||||
func (wrapper *EncryptedSecretStore) OrgSecretList(s int64, p *model.ListOptions) ([]*model.Secret, error) {
|
||||
results, err := wrapper.store.OrgSecretList(s, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -86,23 +86,23 @@ func (b *builtin) SecretDelete(repo *model.Repo, name string) error {
|
||||
return b.store.SecretDelete(secret)
|
||||
}
|
||||
|
||||
func (b *builtin) OrgSecretFind(owner, name string) (*model.Secret, error) {
|
||||
func (b *builtin) OrgSecretFind(owner int64, name string) (*model.Secret, error) {
|
||||
return b.store.OrgSecretFind(owner, name)
|
||||
}
|
||||
|
||||
func (b *builtin) OrgSecretList(owner string, p *model.ListOptions) ([]*model.Secret, error) {
|
||||
func (b *builtin) OrgSecretList(owner int64, p *model.ListOptions) ([]*model.Secret, error) {
|
||||
return b.store.OrgSecretList(owner, p)
|
||||
}
|
||||
|
||||
func (b *builtin) OrgSecretCreate(_ string, in *model.Secret) error {
|
||||
func (b *builtin) OrgSecretCreate(_ int64, in *model.Secret) error {
|
||||
return b.store.SecretCreate(in)
|
||||
}
|
||||
|
||||
func (b *builtin) OrgSecretUpdate(_ string, in *model.Secret) error {
|
||||
func (b *builtin) OrgSecretUpdate(_ int64, in *model.Secret) error {
|
||||
return b.store.SecretUpdate(in)
|
||||
}
|
||||
|
||||
func (b *builtin) OrgSecretDelete(owner, name string) error {
|
||||
func (b *builtin) OrgSecretDelete(owner int64, name string) error {
|
||||
secret, err := b.store.OrgSecretFind(owner, name)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -46,13 +46,15 @@ func apiRoutes(e *gin.Engine) {
|
||||
users.DELETE("/:login", api.DeleteUser)
|
||||
}
|
||||
|
||||
orgBase := apiBase.Group("/orgs/:owner")
|
||||
apiBase.GET("/orgs/lookup/*org_full_name", api.LookupOrg)
|
||||
orgBase := apiBase.Group("/orgs/:org_id")
|
||||
{
|
||||
orgBase.GET("/permissions", api.GetOrgPermissions)
|
||||
|
||||
org := orgBase.Group("")
|
||||
{
|
||||
org.Use(session.MustOrgMember(true))
|
||||
org.GET("", api.GetOrg)
|
||||
org.GET("/secrets", api.GetOrgSecretList)
|
||||
org.POST("/secrets", api.PostOrgSecret)
|
||||
org.GET("/secrets/:secret", api.GetOrgSecret)
|
||||
|
||||
@@ -16,6 +16,7 @@ package session
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/woodpecker-ci/woodpecker/server"
|
||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||
@@ -117,36 +118,47 @@ func MustUser() gin.HandlerFunc {
|
||||
|
||||
func MustOrgMember(admin bool) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
_store := store.FromContext(c)
|
||||
|
||||
user := User(c)
|
||||
owner := c.Param("owner")
|
||||
if user == nil {
|
||||
c.String(http.StatusUnauthorized, "User not authorized")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if owner == "" {
|
||||
c.String(http.StatusForbidden, "User not authorized")
|
||||
c.Abort()
|
||||
|
||||
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")
|
||||
return
|
||||
}
|
||||
|
||||
// User can access his own, admin can access all
|
||||
if user.Login == owner || user.Admin {
|
||||
if (org.Name == user.Login) || user.Admin {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
perm, err := server.Config.Services.Membership.Get(c, user, owner)
|
||||
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))
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if perm == nil || (!admin && !perm.Member) || (admin && !perm.Admin) {
|
||||
c.String(http.StatusForbidden, "User not authorized")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2018 Drone.IO Inc.
|
||||
// Copyright 2023 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
// Copyright 2022 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 migration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
|
||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||
)
|
||||
|
||||
type oldSecret021 struct {
|
||||
ID int64 `json:"id" xorm:"pk autoincr 'secret_id'"`
|
||||
Owner string `json:"-" xorm:"'secret_owner'"`
|
||||
OrgID int64 `json:"-" xorm:"NOT NULL DEFAULT 0 UNIQUE(s) INDEX 'secret_org_id'"`
|
||||
RepoID int64 `json:"-" xorm:"NOT NULL DEFAULT 0 UNIQUE(s) INDEX 'secret_repo_id'"`
|
||||
Name string `json:"name" xorm:"NOT NULL UNIQUE(s) INDEX 'secret_name'"`
|
||||
}
|
||||
|
||||
func (oldSecret021) TableName() string {
|
||||
return "secrets"
|
||||
}
|
||||
|
||||
var addOrgs = task{
|
||||
name: "add-orgs",
|
||||
required: true,
|
||||
fn: func(sess *xorm.Session) error {
|
||||
if exist, err := sess.IsTableExist("orgs"); exist && err == nil {
|
||||
if err := sess.DropTable("orgs"); err != nil {
|
||||
return fmt.Errorf("drop old orgs table failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := sess.Sync(new(model.Org), new(model.Repo), new(model.User)); err != nil {
|
||||
return fmt.Errorf("sync new models failed: %w", err)
|
||||
}
|
||||
|
||||
// make sure the columns exist before removing them
|
||||
if err := sess.Sync(new(oldSecret021)); err != nil {
|
||||
return fmt.Errorf("sync old secrets models failed: %w", err)
|
||||
}
|
||||
|
||||
// get all org names from repos
|
||||
var repos []*model.Repo
|
||||
if err := sess.Find(&repos); err != nil {
|
||||
return fmt.Errorf("find all repos failed: %w", err)
|
||||
}
|
||||
|
||||
orgs := make(map[string]*model.Org)
|
||||
users := make(map[string]bool)
|
||||
for _, repo := range repos {
|
||||
orgName := strings.ToLower(repo.Owner)
|
||||
|
||||
// check if it's a registered user
|
||||
if _, ok := users[orgName]; !ok {
|
||||
exist, err := sess.Where("user_login = ?", orgName).Exist(new(model.User))
|
||||
if err != nil {
|
||||
return fmt.Errorf("check if user '%s' exist failed: %w", orgName, err)
|
||||
}
|
||||
users[orgName] = exist
|
||||
}
|
||||
|
||||
// create org if not already created
|
||||
if _, ok := orgs[orgName]; !ok {
|
||||
org := &model.Org{
|
||||
Name: orgName,
|
||||
IsUser: users[orgName],
|
||||
}
|
||||
if _, err := sess.Insert(org); err != nil {
|
||||
return fmt.Errorf("insert org %#v failed: %w", org, err)
|
||||
}
|
||||
orgs[orgName] = org
|
||||
|
||||
// update org secrets
|
||||
var secrets []*oldSecret021
|
||||
if err := sess.Where(builder.Eq{"secret_owner": orgName, "secret_repo_id": 0}).Find(&secrets); err != nil {
|
||||
return fmt.Errorf("get org secrets failed: %w", err)
|
||||
}
|
||||
|
||||
for _, secret := range secrets {
|
||||
secret.OrgID = org.ID
|
||||
if _, err := sess.ID(secret.ID).Cols("secret_org_id").Update(secret); err != nil {
|
||||
return fmt.Errorf("update org secret %d failed: %w", secret.ID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the repo
|
||||
repo.OrgID = orgs[orgName].ID
|
||||
if _, err := sess.ID(repo.ID).Cols("repo_org_id").Update(repo); err != nil {
|
||||
return fmt.Errorf("update repos failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return dropTableColumns(sess, "secrets", "secret_owner")
|
||||
},
|
||||
}
|
||||
@@ -53,6 +53,7 @@ var migrationTasks = []*task{
|
||||
&initLogsEntriesTable,
|
||||
&migrateLogs2LogEntries,
|
||||
&parentStepsToWorkflows,
|
||||
&addOrgs,
|
||||
}
|
||||
|
||||
var allBeans = []interface{}{
|
||||
@@ -72,6 +73,7 @@ var allBeans = []interface{}{
|
||||
new(model.Cron),
|
||||
new(model.Redirection),
|
||||
new(model.Workflow),
|
||||
new(model.Org),
|
||||
}
|
||||
|
||||
type migrations struct {
|
||||
@@ -106,39 +108,47 @@ func Migrate(e *xorm.Engine) error {
|
||||
e.SetDisableGlobalCache(true)
|
||||
|
||||
if err := e.Sync(new(migrations)); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error to create migrations table: %w", err)
|
||||
}
|
||||
|
||||
sess := e.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("could not create initial migration session: %w", err)
|
||||
}
|
||||
|
||||
// check if we have a fresh installation or need to check for migrations
|
||||
c, err := sess.Count(new(migrations))
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("could not count migrations: %w", err)
|
||||
}
|
||||
|
||||
if c == 0 {
|
||||
if err := initNew(sess); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("could not init a new database: %w", err)
|
||||
}
|
||||
return sess.Commit()
|
||||
if err := sess.Commit(); err != nil {
|
||||
return fmt.Errorf("could not commit initial migration session: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := sess.Commit(); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("could not commit initial migration session: %w", err)
|
||||
}
|
||||
|
||||
if err := runTasks(e, migrationTasks); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("run tasks failed: %w", err)
|
||||
}
|
||||
|
||||
e.SetDisableGlobalCache(false)
|
||||
|
||||
return syncAll(e)
|
||||
if err := syncAll(e); err != nil {
|
||||
return fmt.Errorf("msg: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runTasks(e *xorm.Engine, tasks []*task) error {
|
||||
@@ -166,17 +176,16 @@ func runTasks(e *xorm.Engine, tasks []*task) error {
|
||||
sess := e.NewSession().NoCache()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("could not begin session for '%s': %w", task.name, err)
|
||||
}
|
||||
|
||||
if taskErr = task.fn(sess); taskErr != nil {
|
||||
aliveMsgCancel(nil)
|
||||
if err2 := sess.Rollback(); err2 != nil {
|
||||
taskErr = errors.Join(taskErr, err2)
|
||||
if err := sess.Rollback(); err != nil {
|
||||
taskErr = errors.Join(taskErr, err)
|
||||
}
|
||||
}
|
||||
if err := sess.Commit(); err != nil {
|
||||
return err
|
||||
} else if err := sess.Commit(); err != nil {
|
||||
return fmt.Errorf("could not commit session for '%s': %w", task.name, err)
|
||||
}
|
||||
} else if task.engineFn != nil {
|
||||
taskErr = task.engineFn(e)
|
||||
@@ -189,7 +198,7 @@ func runTasks(e *xorm.Engine, tasks []*task) error {
|
||||
aliveMsgCancel(nil)
|
||||
if taskErr != nil {
|
||||
if task.required {
|
||||
return taskErr
|
||||
return fmt.Errorf("migration task '%s' failed: %w", task.name, taskErr)
|
||||
}
|
||||
log.Error().Err(taskErr).Msgf("migration task '%s' failed but is not required", task.name)
|
||||
continue
|
||||
@@ -197,7 +206,7 @@ func runTasks(e *xorm.Engine, tasks []*task) error {
|
||||
log.Debug().Msgf("migration task '%s' done", task.name)
|
||||
|
||||
if _, err := e.Insert(&migrations{task.name}); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("migration task '%s' could not be marked as finished: %w", task.name, err)
|
||||
}
|
||||
|
||||
migCache[task.name] = true
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2022 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 datastore
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||
)
|
||||
|
||||
func (s storage) OrgCreate(org *model.Org) error {
|
||||
// sanitize
|
||||
org.Name = strings.ToLower(org.Name)
|
||||
// insert
|
||||
_, err := s.engine.Insert(org)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s storage) OrgGet(id int64) (*model.Org, error) {
|
||||
org := new(model.Org)
|
||||
return org, wrapGet(s.engine.ID(id).Get(org))
|
||||
}
|
||||
|
||||
func (s storage) OrgUpdate(org *model.Org) error {
|
||||
// sanitize
|
||||
org.Name = strings.ToLower(org.Name)
|
||||
// update
|
||||
_, err := s.engine.ID(org.ID).AllCols().Update(org)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s storage) OrgDelete(id int64) error {
|
||||
return wrapDelete(s.engine.ID(id).Delete(new(model.Org)))
|
||||
}
|
||||
|
||||
func (s storage) OrgFindByName(name string) (*model.Org, error) {
|
||||
// sanitize
|
||||
name = strings.ToLower(name)
|
||||
// find
|
||||
org := new(model.Org)
|
||||
return org, wrapGet(s.engine.Where("name = ?", name).Get(org))
|
||||
}
|
||||
|
||||
func (s storage) OrgRepoList(org *model.Org, p *model.ListOptions) ([]*model.Repo, error) {
|
||||
var repos []*model.Repo
|
||||
return repos, s.paginate(p).OrderBy("repo_id").Where("repo_org_id = ?", org.ID).Find(&repos)
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright 2023 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 datastore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||
)
|
||||
|
||||
func TestOrgCRUD(t *testing.T) {
|
||||
store, closer := newTestStore(t, new(model.Org), new(model.Repo))
|
||||
defer closer()
|
||||
|
||||
org1 := &model.Org{
|
||||
Name: "someAwesomeOrg",
|
||||
IsUser: false,
|
||||
Private: true,
|
||||
}
|
||||
|
||||
// create first org to play with
|
||||
assert.NoError(t, store.OrgCreate(org1))
|
||||
assert.EqualValues(t, "someawesomeorg", org1.Name)
|
||||
|
||||
// retrieve it
|
||||
orgOne, err := store.OrgGet(org1.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, org1, orgOne)
|
||||
|
||||
// change name
|
||||
assert.NoError(t, store.OrgUpdate(&model.Org{ID: org1.ID, Name: "RenamedOrg"}))
|
||||
|
||||
// force a name duplication and fail
|
||||
assert.Error(t, store.OrgCreate(&model.Org{Name: "reNamedorg"}))
|
||||
|
||||
// find updated org by name
|
||||
orgOne, err = store.OrgFindByName("renamedorG")
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqualValues(t, org1, orgOne)
|
||||
assert.EqualValues(t, org1.ID, orgOne.ID)
|
||||
assert.EqualValues(t, false, orgOne.IsUser)
|
||||
assert.EqualValues(t, false, orgOne.Private)
|
||||
assert.EqualValues(t, "renamedorg", orgOne.Name)
|
||||
|
||||
// create two more orgs and repos
|
||||
someUser := &model.Org{Name: "some_other_u", IsUser: true}
|
||||
assert.NoError(t, store.OrgCreate(someUser))
|
||||
assert.NoError(t, store.OrgCreate(&model.Org{Name: "some_other_org"}))
|
||||
assert.NoError(t, store.CreateRepo(&model.Repo{UserID: 1, Owner: "some_other_u", Name: "abc", FullName: "some_other_u/abc", OrgID: someUser.ID}))
|
||||
assert.NoError(t, store.CreateRepo(&model.Repo{UserID: 1, Owner: "some_other_u", Name: "xyz", FullName: "some_other_u/xyz", OrgID: someUser.ID}))
|
||||
assert.NoError(t, store.CreateRepo(&model.Repo{UserID: 1, Owner: "renamedorg", Name: "567", FullName: "renamedorg/567", OrgID: orgOne.ID}))
|
||||
|
||||
// get all repos for a specific org
|
||||
repos, err := store.OrgRepoList(someUser, &model.ListOptions{All: true})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, repos, 2)
|
||||
|
||||
// delete an org and check if it's gone
|
||||
assert.NoError(t, store.OrgDelete(org1.ID))
|
||||
assert.Error(t, store.OrgDelete(org1.ID))
|
||||
}
|
||||
@@ -33,8 +33,8 @@ func (s storage) SecretList(repo *model.Repo, includeGlobalAndOrgSecrets bool, p
|
||||
var secrets []*model.Secret
|
||||
var cond builder.Cond = builder.Eq{"secret_repo_id": repo.ID}
|
||||
if includeGlobalAndOrgSecrets {
|
||||
cond = cond.Or(builder.Eq{"secret_owner": repo.Owner}).
|
||||
Or(builder.And(builder.Eq{"secret_owner": ""}, builder.Eq{"secret_repo_id": 0}))
|
||||
cond = cond.Or(builder.Eq{"secret_org_id": repo.OrgID}).
|
||||
Or(builder.And(builder.Eq{"secret_org_id": 0}, builder.Eq{"secret_repo_id": 0}))
|
||||
}
|
||||
return secrets, s.paginate(p).Where(cond).OrderBy(orderSecretsBy).Find(&secrets)
|
||||
}
|
||||
@@ -59,28 +59,28 @@ func (s storage) SecretDelete(secret *model.Secret) error {
|
||||
return wrapDelete(s.engine.ID(secret.ID).Delete(new(model.Secret)))
|
||||
}
|
||||
|
||||
func (s storage) OrgSecretFind(owner, name string) (*model.Secret, error) {
|
||||
func (s storage) OrgSecretFind(orgID int64, name string) (*model.Secret, error) {
|
||||
secret := new(model.Secret)
|
||||
return secret, wrapGet(s.engine.Where(
|
||||
builder.Eq{"secret_owner": owner, "secret_name": name},
|
||||
builder.Eq{"secret_org_id": orgID, "secret_name": name},
|
||||
).Get(secret))
|
||||
}
|
||||
|
||||
func (s storage) OrgSecretList(owner string, p *model.ListOptions) ([]*model.Secret, error) {
|
||||
func (s storage) OrgSecretList(orgID int64, p *model.ListOptions) ([]*model.Secret, error) {
|
||||
secrets := make([]*model.Secret, 0)
|
||||
return secrets, s.paginate(p).Where("secret_owner = ?", owner).OrderBy(orderSecretsBy).Find(&secrets)
|
||||
return secrets, s.paginate(p).Where("secret_org_id = ?", orgID).OrderBy(orderSecretsBy).Find(&secrets)
|
||||
}
|
||||
|
||||
func (s storage) GlobalSecretFind(name string) (*model.Secret, error) {
|
||||
secret := new(model.Secret)
|
||||
return secret, wrapGet(s.engine.Where(
|
||||
builder.Eq{"secret_owner": "", "secret_repo_id": 0, "secret_name": name},
|
||||
builder.Eq{"secret_org_id": 0, "secret_repo_id": 0, "secret_name": name},
|
||||
).Get(secret))
|
||||
}
|
||||
|
||||
func (s storage) GlobalSecretList(p *model.ListOptions) ([]*model.Secret, error) {
|
||||
secrets := make([]*model.Secret, 0)
|
||||
return secrets, s.paginate(p).Where(
|
||||
builder.Eq{"secret_owner": "", "secret_repo_id": 0},
|
||||
builder.Eq{"secret_org_id": 0, "secret_repo_id": 0},
|
||||
).OrderBy(orderSecretsBy).Find(&secrets)
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func TestSecretList(t *testing.T) {
|
||||
|
||||
createTestSecrets(t, store)
|
||||
|
||||
list, err := store.SecretList(&model.Repo{ID: 1, Owner: "org"}, false, &model.ListOptions{Page: 1, PerPage: 50})
|
||||
list, err := store.SecretList(&model.Repo{ID: 1, OrgID: 12}, false, &model.ListOptions{Page: 1, PerPage: 50})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, list, 2)
|
||||
}
|
||||
@@ -95,7 +95,7 @@ func TestSecretPipelineList(t *testing.T) {
|
||||
|
||||
createTestSecrets(t, store)
|
||||
|
||||
list, err := store.SecretList(&model.Repo{ID: 1, Owner: "org"}, true, &model.ListOptions{Page: 1, PerPage: 50})
|
||||
list, err := store.SecretList(&model.Repo{ID: 1, OrgID: 12}, true, &model.ListOptions{Page: 1, PerPage: 50})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, list, 4)
|
||||
}
|
||||
@@ -179,7 +179,7 @@ func TestSecretIndexes(t *testing.T) {
|
||||
|
||||
func createTestSecrets(t *testing.T, store *storage) {
|
||||
assert.NoError(t, store.SecretCreate(&model.Secret{
|
||||
Owner: "org",
|
||||
OrgID: 12,
|
||||
Name: "usr",
|
||||
Value: "sec",
|
||||
}))
|
||||
@@ -204,7 +204,7 @@ func TestOrgSecretFind(t *testing.T) {
|
||||
defer closer()
|
||||
|
||||
err := store.SecretCreate(&model.Secret{
|
||||
Owner: "org",
|
||||
OrgID: 12,
|
||||
Name: "password",
|
||||
Value: "correct-horse-battery-staple",
|
||||
Images: []string{"golang", "node"},
|
||||
@@ -215,13 +215,13 @@ func TestOrgSecretFind(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
secret, err := store.OrgSecretFind("org", "password")
|
||||
secret, err := store.OrgSecretFind(12, "password")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if got, want := secret.Owner, "org"; got != want {
|
||||
t.Errorf("Want owner %s, got %s", want, got)
|
||||
if got, want := secret.OrgID, int64(12); got != want {
|
||||
t.Errorf("Want org_id %d, got %d", want, got)
|
||||
}
|
||||
if got, want := secret.Name, "password"; got != want {
|
||||
t.Errorf("Want secret name %s, got %s", want, got)
|
||||
@@ -249,7 +249,7 @@ func TestOrgSecretList(t *testing.T) {
|
||||
|
||||
createTestSecrets(t, store)
|
||||
|
||||
list, err := store.OrgSecretList("org", &model.ListOptions{All: true})
|
||||
list, err := store.OrgSecretList(12, &model.ListOptions{All: true})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, list, 1)
|
||||
|
||||
|
||||
+128
-8
@@ -1169,16 +1169,122 @@ func (_m *Store) Migrate() error {
|
||||
return r0
|
||||
}
|
||||
|
||||
// OrgCreate provides a mock function with given fields: _a0
|
||||
func (_m *Store) OrgCreate(_a0 *model.Org) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*model.Org) error); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// OrgDelete provides a mock function with given fields: _a0
|
||||
func (_m *Store) OrgDelete(_a0 int64) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(int64) error); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// OrgFindByName provides a mock function with given fields: _a0
|
||||
func (_m *Store) OrgFindByName(_a0 string) (*model.Org, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *model.Org
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string) (*model.Org, error)); ok {
|
||||
return rf(_a0)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string) *model.Org); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.Org)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// OrgGet provides a mock function with given fields: _a0
|
||||
func (_m *Store) OrgGet(_a0 int64) (*model.Org, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *model.Org
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(int64) (*model.Org, error)); ok {
|
||||
return rf(_a0)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(int64) *model.Org); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.Org)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(int64) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// OrgRepoList provides a mock function with given fields: _a0, _a1
|
||||
func (_m *Store) OrgRepoList(_a0 *model.Org, _a1 *model.ListOptions) ([]*model.Repo, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 []*model.Repo
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(*model.Org, *model.ListOptions) ([]*model.Repo, error)); ok {
|
||||
return rf(_a0, _a1)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(*model.Org, *model.ListOptions) []*model.Repo); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*model.Repo)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(*model.Org, *model.ListOptions) error); ok {
|
||||
r1 = rf(_a0, _a1)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// OrgSecretFind provides a mock function with given fields: _a0, _a1
|
||||
func (_m *Store) OrgSecretFind(_a0 string, _a1 string) (*model.Secret, error) {
|
||||
func (_m *Store) OrgSecretFind(_a0 int64, _a1 string) (*model.Secret, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 *model.Secret
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string, string) (*model.Secret, error)); ok {
|
||||
if rf, ok := ret.Get(0).(func(int64, string) (*model.Secret, error)); ok {
|
||||
return rf(_a0, _a1)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string, string) *model.Secret); ok {
|
||||
if rf, ok := ret.Get(0).(func(int64, string) *model.Secret); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
@@ -1186,7 +1292,7 @@ func (_m *Store) OrgSecretFind(_a0 string, _a1 string) (*model.Secret, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string, string) error); ok {
|
||||
if rf, ok := ret.Get(1).(func(int64, string) error); ok {
|
||||
r1 = rf(_a0, _a1)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
@@ -1196,15 +1302,15 @@ func (_m *Store) OrgSecretFind(_a0 string, _a1 string) (*model.Secret, error) {
|
||||
}
|
||||
|
||||
// OrgSecretList provides a mock function with given fields: _a0, _a1
|
||||
func (_m *Store) OrgSecretList(_a0 string, _a1 *model.ListOptions) ([]*model.Secret, error) {
|
||||
func (_m *Store) OrgSecretList(_a0 int64, _a1 *model.ListOptions) ([]*model.Secret, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 []*model.Secret
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string, *model.ListOptions) ([]*model.Secret, error)); ok {
|
||||
if rf, ok := ret.Get(0).(func(int64, *model.ListOptions) ([]*model.Secret, error)); ok {
|
||||
return rf(_a0, _a1)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string, *model.ListOptions) []*model.Secret); ok {
|
||||
if rf, ok := ret.Get(0).(func(int64, *model.ListOptions) []*model.Secret); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
@@ -1212,7 +1318,7 @@ func (_m *Store) OrgSecretList(_a0 string, _a1 *model.ListOptions) ([]*model.Sec
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string, *model.ListOptions) error); ok {
|
||||
if rf, ok := ret.Get(1).(func(int64, *model.ListOptions) error); ok {
|
||||
r1 = rf(_a0, _a1)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
@@ -1221,6 +1327,20 @@ func (_m *Store) OrgSecretList(_a0 string, _a1 *model.ListOptions) ([]*model.Sec
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// OrgUpdate provides a mock function with given fields: _a0
|
||||
func (_m *Store) OrgUpdate(_a0 *model.Org) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*model.Org) error); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// PermDelete provides a mock function with given fields: perm
|
||||
func (_m *Store) PermDelete(perm *model.Perm) error {
|
||||
ret := _m.Called(perm)
|
||||
|
||||
+12
-2
@@ -121,8 +121,8 @@ type Store interface {
|
||||
SecretCreate(*model.Secret) error
|
||||
SecretUpdate(*model.Secret) error
|
||||
SecretDelete(*model.Secret) error
|
||||
OrgSecretFind(string, string) (*model.Secret, error)
|
||||
OrgSecretList(string, *model.ListOptions) ([]*model.Secret, error)
|
||||
OrgSecretFind(int64, string) (*model.Secret, error)
|
||||
OrgSecretList(int64, *model.ListOptions) ([]*model.Secret, error)
|
||||
GlobalSecretFind(string) (*model.Secret, error)
|
||||
GlobalSecretList(*model.ListOptions) ([]*model.Secret, error)
|
||||
|
||||
@@ -183,6 +183,16 @@ type Store interface {
|
||||
WorkflowLoad(int64) (*model.Workflow, error)
|
||||
WorkflowUpdate(*model.Workflow) error
|
||||
|
||||
// Org
|
||||
OrgCreate(*model.Org) error
|
||||
OrgGet(int64) (*model.Org, error)
|
||||
OrgFindByName(string) (*model.Org, error)
|
||||
OrgUpdate(*model.Org) error
|
||||
OrgDelete(int64) error
|
||||
|
||||
// Org repos
|
||||
OrgRepoList(*model.Org, *model.ListOptions) ([]*model.Repo, error)
|
||||
|
||||
// Store operations
|
||||
Ping() error
|
||||
Close() error
|
||||
|
||||
Reference in New Issue
Block a user