Allow login using multiple forges (#3822)

This commit is contained in:
Anbraten 2024-07-13 10:41:35 +02:00 committed by GitHub
parent a26c7a475b
commit b12d676546
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 1182 additions and 97 deletions

View file

@ -265,13 +265,6 @@ func run(c *cli.Context) error {
}
func setupEvilGlobals(c *cli.Context, s store.Store) error {
// secrets
var err error
server.Config.Server.JWTSecret, err = setupJWTSecret(s)
if err != nil {
return fmt.Errorf("could not setup jwt secret: %w", err)
}
// services
server.Config.Services.Queue = setupQueue(c, s)
server.Config.Services.Logs = logging.New()
@ -319,6 +312,10 @@ func setupEvilGlobals(c *cli.Context, s store.Store) error {
server.Config.Pipeline.Proxy.HTTPS = c.String("backend-https-proxy")
// server configuration
server.Config.Server.JWTSecret, err = setupJWTSecret(s)
if err != nil {
return fmt.Errorf("could not setup jwt secret: %w", err)
}
server.Config.Server.Cert = c.String("server-cert")
server.Config.Server.Key = c.String("server-key")
server.Config.Server.AgentToken = c.String("agent-secret")

View file

@ -15,7 +15,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.34.2
// protoc-gen-go v1.34.1
// protoc v4.25.1
// source: woodpecker.proto
@ -1312,7 +1312,7 @@ func file_woodpecker_proto_rawDescGZIP() []byte {
}
var file_woodpecker_proto_msgTypes = make([]protoimpl.MessageInfo, 21)
var file_woodpecker_proto_goTypes = []any{
var file_woodpecker_proto_goTypes = []interface{}{
(*StepState)(nil), // 0: proto.StepState
(*WorkflowState)(nil), // 1: proto.WorkflowState
(*LogEntry)(nil), // 2: proto.LogEntry
@ -1380,7 +1380,7 @@ func file_woodpecker_proto_init() {
return
}
if !protoimpl.UnsafeEnabled {
file_woodpecker_proto_msgTypes[0].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*StepState); i {
case 0:
return &v.state
@ -1392,7 +1392,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[1].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*WorkflowState); i {
case 0:
return &v.state
@ -1404,7 +1404,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[2].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*LogEntry); i {
case 0:
return &v.state
@ -1416,7 +1416,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[3].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Filter); i {
case 0:
return &v.state
@ -1428,7 +1428,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[4].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Workflow); i {
case 0:
return &v.state
@ -1440,7 +1440,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[5].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*NextRequest); i {
case 0:
return &v.state
@ -1452,7 +1452,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[6].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*InitRequest); i {
case 0:
return &v.state
@ -1464,7 +1464,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[7].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*WaitRequest); i {
case 0:
return &v.state
@ -1476,7 +1476,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[8].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DoneRequest); i {
case 0:
return &v.state
@ -1488,7 +1488,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[9].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ExtendRequest); i {
case 0:
return &v.state
@ -1500,7 +1500,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[10].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UpdateRequest); i {
case 0:
return &v.state
@ -1512,7 +1512,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[11].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*LogRequest); i {
case 0:
return &v.state
@ -1524,7 +1524,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[12].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Empty); i {
case 0:
return &v.state
@ -1536,7 +1536,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[13].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ReportHealthRequest); i {
case 0:
return &v.state
@ -1548,7 +1548,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[14].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RegisterAgentRequest); i {
case 0:
return &v.state
@ -1560,7 +1560,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[15].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*VersionResponse); i {
case 0:
return &v.state
@ -1572,7 +1572,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[16].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*NextResponse); i {
case 0:
return &v.state
@ -1584,7 +1584,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[17].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RegisterAgentResponse); i {
case 0:
return &v.state
@ -1596,7 +1596,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[18].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AuthRequest); i {
case 0:
return &v.state
@ -1608,7 +1608,7 @@ func file_woodpecker_proto_init() {
return nil
}
}
file_woodpecker_proto_msgTypes[19].Exporter = func(v any, i int) any {
file_woodpecker_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AuthResponse); i {
case 0:
return &v.state

View file

@ -104,18 +104,47 @@ func BlockTilQueueHasRunningItem(c *gin.Context) {
func PostHook(c *gin.Context) {
_store := store.FromContext(c)
_forge, err := server.Config.Services.Manager.ForgeByID(1) // TODO: get the forge for the specific repo somehow
//
// 1. Check if the webhook is valid and authorized
//
var repo *model.Repo
_, err := token.ParseRequest([]token.Type{token.HookToken}, c.Request, func(t *token.Token) (string, error) {
var err error
repo, err = getRepoFromToken(_store, t)
if err != nil {
log.Error().Err(err).Msg("Cannot get main forge")
return "", err
}
return repo.Hash, nil
})
if err != nil {
msg := "failure to parse token from hook"
log.Error().Err(err).Msg(msg)
c.String(http.StatusBadRequest, msg)
return
}
if repo == nil {
msg := "failure to get repo from token"
log.Error().Msg(msg)
c.String(http.StatusBadRequest, msg)
return
}
_forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
if err != nil {
log.Error().Err(err).Int64("repo-id", repo.ID).Msgf("Cannot get forge with id: %d", repo.ForgeID)
c.AbortWithStatus(http.StatusInternalServerError)
return
}
//
// 1. Parse webhook
// 2. Parse the webhook data
//
tmpRepo, tmpPipeline, err := _forge.Hook(c, c.Request)
repoFromForge, pipelineFromForge, err := _forge.Hook(c, c.Request)
if err != nil {
if errors.Is(err, &types.ErrIgnoreEvent{}) {
msg := fmt.Sprintf("forge driver: %s", err)
@ -130,13 +159,13 @@ func PostHook(c *gin.Context) {
return
}
if tmpPipeline == nil {
if pipelineFromForge == nil {
msg := "ignoring hook: hook parsing resulted in empty pipeline"
log.Debug().Msg(msg)
c.String(http.StatusOK, msg)
return
}
if tmpRepo == nil {
if repoFromForge == nil {
msg := "failure to ascertain repo from hook"
log.Debug().Msg(msg)
c.String(http.StatusBadRequest, msg)
@ -144,21 +173,24 @@ func PostHook(c *gin.Context) {
}
//
// 2. Get related repo from store and take repo renaming into account
// 3. Check the repo from the token is matching the repo returned by the forge
//
repo, err := _store.GetRepoNameFallback(tmpRepo.ForgeRemoteID, tmpRepo.FullName)
if err != nil {
log.Error().Err(err).Msgf("failure to get repo %s from store", tmpRepo.FullName)
handleDBError(c, err)
if repo.ForgeRemoteID != repoFromForge.ForgeRemoteID {
log.Warn().Msgf("ignoring hook: repo %s does not match the repo from the token", repo.FullName)
c.String(http.StatusBadRequest, "failure to parse token from hook")
return
}
//
// 4. Check if the repo is active and has an owner
//
if !repo.IsActive {
log.Debug().Msgf("ignoring hook: repo %s is inactive", tmpRepo.FullName)
log.Debug().Msgf("ignoring hook: repo %s is inactive", repoFromForge.FullName)
c.Status(http.StatusNoContent)
return
}
currentRepoFullName := repo.FullName
if repo.UserID == 0 {
log.Warn().Msgf("ignoring hook. repo %s has no owner.", repo.FullName)
@ -174,44 +206,10 @@ func PostHook(c *gin.Context) {
forge.Refresh(c, _forge, _store, user)
//
// 3. Check if the webhook is a valid and authorized one
// 4. Update the repo
//
// get the token and verify the hook is authorized
parsedToken, err := token.ParseRequest([]token.Type{token.HookToken}, c.Request, func(_ *token.Token) (string, error) {
return repo.Hash, nil
})
if err != nil {
msg := fmt.Sprintf("failure to parse token from hook for %s", repo.FullName)
log.Error().Err(err).Msg(msg)
c.String(http.StatusBadRequest, msg)
return
}
// TODO: remove fallback for text full name in next major release
verifiedKey := parsedToken.Get("repo-id") == strconv.FormatInt(repo.ID, 10) || parsedToken.Get("text") == currentRepoFullName
if !verifiedKey {
verifiedKey, err = _store.HasRedirectionForRepo(repo.ID, repo.FullName)
if err != nil {
msg := "failure to verify token from hook. Could not check for redirections of the repo"
log.Error().Err(err).Msg(msg)
c.String(http.StatusInternalServerError, msg)
return
}
}
if !verifiedKey {
msg := fmt.Sprintf("failure to verify token from hook. Expected %s, got %s", repo.FullName, parsedToken.Get("text"))
log.Debug().Msg(msg)
c.String(http.StatusForbidden, msg)
return
}
//
// 4. Update repo
//
if currentRepoFullName != tmpRepo.FullName {
if repo.FullName != repoFromForge.FullName {
// create a redirection
err = _store.CreateRedirection(&model.Redirection{RepoID: repo.ID, FullName: repo.FullName})
if err != nil {
@ -220,7 +218,7 @@ func PostHook(c *gin.Context) {
}
}
repo.Update(tmpRepo)
repo.Update(repoFromForge)
err = _store.UpdateRepo(repo)
if err != nil {
c.String(http.StatusInternalServerError, err.Error())
@ -231,7 +229,7 @@ func PostHook(c *gin.Context) {
// 5. Check if pull requests are allowed for this repo
//
if (tmpPipeline.Event == model.EventPull || tmpPipeline.Event == model.EventPullClosed) && !repo.AllowPull {
if (pipelineFromForge.Event == model.EventPull || pipelineFromForge.Event == model.EventPullClosed) && !repo.AllowPull {
log.Debug().Str("repo", repo.FullName).Msg("ignoring hook: pull requests are disabled for this repo in woodpecker")
c.Status(http.StatusNoContent)
return
@ -241,10 +239,22 @@ func PostHook(c *gin.Context) {
// 6. Finally create a pipeline
//
pl, err := pipeline.Create(c, _store, repo, tmpPipeline)
pl, err := pipeline.Create(c, _store, repo, pipelineFromForge)
if err != nil {
handlePipelineErr(c, err)
} else {
c.JSON(http.StatusOK, pl)
}
}
func getRepoFromToken(store store.Store, t *token.Token) (*model.Repo, error) {
// try to get the repo by the repo-id
repoID, err := strconv.ParseInt(t.Get("repo-id"), 10, 64)
if err == nil {
return store.GetRepo(repoID)
}
// try to get the repo by the repo name or by its redirection
repoName := t.Get("text")
return store.GetRepoName(repoName)
}

104
server/api/hook_test.go Normal file
View file

@ -0,0 +1,104 @@
package api_test
import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/franela/goblin"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"go.woodpecker-ci.org/woodpecker/v2/server"
"go.woodpecker-ci.org/woodpecker/v2/server/api"
mocks_forge "go.woodpecker-ci.org/woodpecker/v2/server/forge/mocks"
"go.woodpecker-ci.org/woodpecker/v2/server/model"
mocks_config_service "go.woodpecker-ci.org/woodpecker/v2/server/services/config/mocks"
mocks_services "go.woodpecker-ci.org/woodpecker/v2/server/services/mocks"
"go.woodpecker-ci.org/woodpecker/v2/server/services/permissions"
mocks_registry_service "go.woodpecker-ci.org/woodpecker/v2/server/services/registry/mocks"
mocks_secret_service "go.woodpecker-ci.org/woodpecker/v2/server/services/secret/mocks"
mocks_store "go.woodpecker-ci.org/woodpecker/v2/server/store/mocks"
"go.woodpecker-ci.org/woodpecker/v2/shared/token"
)
func TestHook(t *testing.T) {
gin.SetMode(gin.TestMode)
g := goblin.Goblin(t)
g.Describe("Hook", func() {
g.It("should handle a correct webhook payload", func() {
_manager := mocks_services.NewManager(t)
_forge := mocks_forge.NewForge(t)
_store := mocks_store.NewStore(t)
_configService := mocks_config_service.NewService(t)
_secretService := mocks_secret_service.NewService(t)
_registryService := mocks_registry_service.NewService(t)
server.Config.Services.Manager = _manager
server.Config.Permissions.Open = true
server.Config.Permissions.Orgs = permissions.NewOrgs(nil)
server.Config.Permissions.Admins = permissions.NewAdmins(nil)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Set("store", _store)
user := &model.User{
ID: 123,
}
repo := &model.Repo{
ID: 123,
ForgeRemoteID: "123",
Owner: "owner",
Name: "name",
IsActive: true,
UserID: user.ID,
Hash: "secret-123-this-is-a-secret",
}
pipeline := &model.Pipeline{
ID: 123,
RepoID: repo.ID,
Event: model.EventPush,
}
repoToken := token.New(token.HookToken)
repoToken.Set("repo-id", fmt.Sprintf("%d", repo.ID))
signedToken, err := repoToken.Sign("secret-123-this-is-a-secret")
if err != nil {
g.Fail(err)
}
header := http.Header{}
header.Set("Authorization", fmt.Sprintf("Bearer %s", signedToken))
c.Request = &http.Request{
Header: header,
URL: &url.URL{
Scheme: "https",
},
}
_manager.On("ForgeFromRepo", repo).Return(_forge, nil)
_forge.On("Hook", mock.Anything, mock.Anything).Return(repo, pipeline, nil)
_store.On("GetRepo", repo.ID).Return(repo, nil)
_store.On("GetUser", user.ID).Return(user, nil)
_store.On("UpdateRepo", repo).Return(nil)
_store.On("CreatePipeline", mock.Anything).Return(nil)
_manager.On("ConfigServiceFromRepo", repo).Return(_configService)
_configService.On("Fetch", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil)
_forge.On("Netrc", mock.Anything, mock.Anything).Return(&model.Netrc{}, nil)
_store.On("GetPipelineLastBefore", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil)
_manager.On("SecretServiceFromRepo", repo).Return(_secretService)
_secretService.On("SecretListPipeline", repo, mock.Anything, mock.Anything).Return(nil, nil)
_manager.On("RegistryServiceFromRepo", repo).Return(_registryService)
_registryService.On("RegistryListPipeline", repo, mock.Anything).Return(nil, nil)
_manager.On("EnvironmentService").Return(nil)
_store.On("DeletePipeline", mock.Anything).Return(nil)
api.PostHook(c)
assert.Equal(g, http.StatusNoContent, c.Writer.Status())
assert.Equal(g, "true", w.Header().Get("Pipeline-Filtered"))
})
})
}

View file

@ -62,10 +62,10 @@ func HandleAuth(c *gin.Context) {
code := c.Request.FormValue("code")
state := c.Request.FormValue("state")
isCallback := code != "" && state != ""
forgeID := int64(1) // TODO: replace with forge id when multiple forges are supported
var forgeID int64
if isCallback { // validate the state token
_, err := token.Parse([]token.Type{token.OAuthStateToken}, state, func(_ *token.Token) (string, error) {
stateToken, err := token.Parse([]token.Type{token.OAuthStateToken}, state, func(_ *token.Token) (string, error) {
return server.Config.Server.JWTSecret, nil
})
if err != nil {
@ -73,8 +73,29 @@ func HandleAuth(c *gin.Context) {
c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/login?error=invalid_state")
return
}
_forgeID := stateToken.Get("forge-id")
forgeID, err = strconv.ParseInt(_forgeID, 10, 64)
if err != nil {
log.Error().Err(err).Msg("forge-id of state token invalid")
c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/login?error=invalid_state")
return
}
} else { // only generate a state token if not a callback
var err error
_forgeID := c.Request.FormValue("forge_id")
if _forgeID == "" {
forgeID = 1 // fallback to main forge
} else {
forgeID, err = strconv.ParseInt(_forgeID, 10, 64)
if err != nil {
log.Error().Err(err).Msg("forge-id of state token invalid")
c.Redirect(http.StatusSeeOther, server.Config.Server.RootPath+"/login?error=invalid_state")
return
}
}
jwtSecret := server.Config.Server.JWTSecret
exp := time.Now().Add(stateTokenDuration).Unix()
stateToken := token.New(token.OAuthStateToken)
@ -208,6 +229,7 @@ func HandleAuth(c *gin.Context) {
user.Secret = userFromForge.Secret
user.Email = userFromForge.Email
user.Avatar = userFromForge.Avatar
user.ForgeID = forgeID
user.ForgeRemoteID = userFromForge.ForgeRemoteID
user.Login = userFromForge.Login
user.Admin = user.Admin || server.Config.Permissions.Admins.IsAdmin(userFromForge)
@ -280,7 +302,7 @@ func GetLogout(c *gin.Context) {
func DeprecatedGetLoginToken(c *gin.Context) {
_store := store.FromContext(c)
_forge, err := server.Config.Services.Manager.ForgeByID(1) // TODO: get selected forge from auth request
_forge, err := server.Config.Services.Manager.ForgeByID(1)
if err != nil {
log.Error().Err(err).Msg("Cannot get main forge")
c.AbortWithStatus(http.StatusInternalServerError)

View file

@ -0,0 +1,63 @@
// Code generated by mockery. DO NOT EDIT.
package mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
forge "go.woodpecker-ci.org/woodpecker/v2/server/forge"
model "go.woodpecker-ci.org/woodpecker/v2/server/model"
types "go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
)
// Service is an autogenerated mock type for the Service type
type Service struct {
mock.Mock
}
// Fetch provides a mock function with given fields: ctx, _a1, user, repo, pipeline, oldConfigData, restart
func (_m *Service) Fetch(ctx context.Context, _a1 forge.Forge, user *model.User, repo *model.Repo, pipeline *model.Pipeline, oldConfigData []*types.FileMeta, restart bool) ([]*types.FileMeta, error) {
ret := _m.Called(ctx, _a1, user, repo, pipeline, oldConfigData, restart)
if len(ret) == 0 {
panic("no return value specified for Fetch")
}
var r0 []*types.FileMeta
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, forge.Forge, *model.User, *model.Repo, *model.Pipeline, []*types.FileMeta, bool) ([]*types.FileMeta, error)); ok {
return rf(ctx, _a1, user, repo, pipeline, oldConfigData, restart)
}
if rf, ok := ret.Get(0).(func(context.Context, forge.Forge, *model.User, *model.Repo, *model.Pipeline, []*types.FileMeta, bool) []*types.FileMeta); ok {
r0 = rf(ctx, _a1, user, repo, pipeline, oldConfigData, restart)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*types.FileMeta)
}
}
if rf, ok := ret.Get(1).(func(context.Context, forge.Forge, *model.User, *model.Repo, *model.Pipeline, []*types.FileMeta, bool) error); ok {
r1 = rf(ctx, _a1, user, repo, pipeline, oldConfigData, restart)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewService creates a new instance of Service. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewService(t interface {
mock.TestingT
Cleanup(func())
}) *Service {
mock := &Service{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View file

@ -22,6 +22,8 @@ import (
"go.woodpecker-ci.org/woodpecker/v2/server/model"
)
//go:generate mockery --name Service --output mocks --case underscore
type Service interface {
Fetch(ctx context.Context, forge forge.Forge, user *model.User, repo *model.Repo, pipeline *model.Pipeline, oldConfigData []*types.FileMeta, restart bool) (configData []*types.FileMeta, err error)
}

View file

@ -0,0 +1,57 @@
// Code generated by mockery. DO NOT EDIT.
package mocks
import (
mock "github.com/stretchr/testify/mock"
model "go.woodpecker-ci.org/woodpecker/v2/server/model"
)
// Service is an autogenerated mock type for the Service type
type Service struct {
mock.Mock
}
// EnvironList provides a mock function with given fields: _a0
func (_m *Service) EnvironList(_a0 *model.Repo) ([]*model.Environ, error) {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for EnvironList")
}
var r0 []*model.Environ
var r1 error
if rf, ok := ret.Get(0).(func(*model.Repo) ([]*model.Environ, error)); ok {
return rf(_a0)
}
if rf, ok := ret.Get(0).(func(*model.Repo) []*model.Environ); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Environ)
}
}
if rf, ok := ret.Get(1).(func(*model.Repo) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewService creates a new instance of Service. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewService(t interface {
mock.TestingT
Cleanup(func())
}) *Service {
mock := &Service{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View file

@ -16,6 +16,8 @@ package environment
import "go.woodpecker-ci.org/woodpecker/v2/server/model"
//go:generate mockery --name Service --output mocks --case underscore
// Service defines a service for managing environment variables.
type Service interface {
EnvironList(*model.Repo) ([]*model.Environ, error)

View file

@ -46,7 +46,7 @@ type Manager interface {
EnvironmentService() environment.Service
ForgeFromRepo(repo *model.Repo) (forge.Forge, error)
ForgeFromUser(user *model.User) (forge.Forge, error)
ForgeByID(id int64) (forge.Forge, error)
ForgeByID(forgeID int64) (forge.Forge, error)
}
type manager struct {

View file

@ -68,9 +68,9 @@ func (_m *Manager) EnvironmentService() environment.Service {
return r0
}
// ForgeByID provides a mock function with given fields: id
func (_m *Manager) ForgeByID(id int64) (forge.Forge, error) {
ret := _m.Called(id)
// ForgeByID provides a mock function with given fields: forgeID
func (_m *Manager) ForgeByID(forgeID int64) (forge.Forge, error) {
ret := _m.Called(forgeID)
if len(ret) == 0 {
panic("no return value specified for ForgeByID")
@ -79,10 +79,10 @@ func (_m *Manager) ForgeByID(id int64) (forge.Forge, error) {
var r0 forge.Forge
var r1 error
if rf, ok := ret.Get(0).(func(int64) (forge.Forge, error)); ok {
return rf(id)
return rf(forgeID)
}
if rf, ok := ret.Get(0).(func(int64) forge.Forge); ok {
r0 = rf(id)
r0 = rf(forgeID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(forge.Forge)
@ -90,7 +90,7 @@ func (_m *Manager) ForgeByID(id int64) (forge.Forge, error) {
}
if rf, ok := ret.Get(1).(func(int64) error); ok {
r1 = rf(id)
r1 = rf(forgeID)
} else {
r1 = ret.Error(1)
}

View file

@ -0,0 +1,399 @@
// Code generated by mockery. DO NOT EDIT.
package mocks
import (
mock "github.com/stretchr/testify/mock"
model "go.woodpecker-ci.org/woodpecker/v2/server/model"
)
// Service is an autogenerated mock type for the Service type
type Service struct {
mock.Mock
}
// GlobalRegistryCreate provides a mock function with given fields: _a0
func (_m *Service) GlobalRegistryCreate(_a0 *model.Registry) error {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for GlobalRegistryCreate")
}
var r0 error
if rf, ok := ret.Get(0).(func(*model.Registry) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
}
return r0
}
// GlobalRegistryDelete provides a mock function with given fields: _a0
func (_m *Service) GlobalRegistryDelete(_a0 string) error {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for GlobalRegistryDelete")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
}
return r0
}
// GlobalRegistryFind provides a mock function with given fields: _a0
func (_m *Service) GlobalRegistryFind(_a0 string) (*model.Registry, error) {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for GlobalRegistryFind")
}
var r0 *model.Registry
var r1 error
if rf, ok := ret.Get(0).(func(string) (*model.Registry, error)); ok {
return rf(_a0)
}
if rf, ok := ret.Get(0).(func(string) *model.Registry); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Registry)
}
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GlobalRegistryList provides a mock function with given fields: _a0
func (_m *Service) GlobalRegistryList(_a0 *model.ListOptions) ([]*model.Registry, error) {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for GlobalRegistryList")
}
var r0 []*model.Registry
var r1 error
if rf, ok := ret.Get(0).(func(*model.ListOptions) ([]*model.Registry, error)); ok {
return rf(_a0)
}
if rf, ok := ret.Get(0).(func(*model.ListOptions) []*model.Registry); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Registry)
}
}
if rf, ok := ret.Get(1).(func(*model.ListOptions) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GlobalRegistryUpdate provides a mock function with given fields: _a0
func (_m *Service) GlobalRegistryUpdate(_a0 *model.Registry) error {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for GlobalRegistryUpdate")
}
var r0 error
if rf, ok := ret.Get(0).(func(*model.Registry) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
}
return r0
}
// OrgRegistryCreate provides a mock function with given fields: _a0, _a1
func (_m *Service) OrgRegistryCreate(_a0 int64, _a1 *model.Registry) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for OrgRegistryCreate")
}
var r0 error
if rf, ok := ret.Get(0).(func(int64, *model.Registry) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// OrgRegistryDelete provides a mock function with given fields: _a0, _a1
func (_m *Service) OrgRegistryDelete(_a0 int64, _a1 string) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for OrgRegistryDelete")
}
var r0 error
if rf, ok := ret.Get(0).(func(int64, string) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// OrgRegistryFind provides a mock function with given fields: _a0, _a1
func (_m *Service) OrgRegistryFind(_a0 int64, _a1 string) (*model.Registry, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for OrgRegistryFind")
}
var r0 *model.Registry
var r1 error
if rf, ok := ret.Get(0).(func(int64, string) (*model.Registry, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(int64, string) *model.Registry); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Registry)
}
}
if rf, ok := ret.Get(1).(func(int64, string) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// OrgRegistryList provides a mock function with given fields: _a0, _a1
func (_m *Service) OrgRegistryList(_a0 int64, _a1 *model.ListOptions) ([]*model.Registry, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for OrgRegistryList")
}
var r0 []*model.Registry
var r1 error
if rf, ok := ret.Get(0).(func(int64, *model.ListOptions) ([]*model.Registry, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(int64, *model.ListOptions) []*model.Registry); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Registry)
}
}
if rf, ok := ret.Get(1).(func(int64, *model.ListOptions) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// OrgRegistryUpdate provides a mock function with given fields: _a0, _a1
func (_m *Service) OrgRegistryUpdate(_a0 int64, _a1 *model.Registry) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for OrgRegistryUpdate")
}
var r0 error
if rf, ok := ret.Get(0).(func(int64, *model.Registry) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// RegistryCreate provides a mock function with given fields: _a0, _a1
func (_m *Service) RegistryCreate(_a0 *model.Repo, _a1 *model.Registry) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for RegistryCreate")
}
var r0 error
if rf, ok := ret.Get(0).(func(*model.Repo, *model.Registry) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// RegistryDelete provides a mock function with given fields: _a0, _a1
func (_m *Service) RegistryDelete(_a0 *model.Repo, _a1 string) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for RegistryDelete")
}
var r0 error
if rf, ok := ret.Get(0).(func(*model.Repo, string) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// RegistryFind provides a mock function with given fields: _a0, _a1
func (_m *Service) RegistryFind(_a0 *model.Repo, _a1 string) (*model.Registry, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for RegistryFind")
}
var r0 *model.Registry
var r1 error
if rf, ok := ret.Get(0).(func(*model.Repo, string) (*model.Registry, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(*model.Repo, string) *model.Registry); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Registry)
}
}
if rf, ok := ret.Get(1).(func(*model.Repo, string) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RegistryList provides a mock function with given fields: _a0, _a1
func (_m *Service) RegistryList(_a0 *model.Repo, _a1 *model.ListOptions) ([]*model.Registry, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for RegistryList")
}
var r0 []*model.Registry
var r1 error
if rf, ok := ret.Get(0).(func(*model.Repo, *model.ListOptions) ([]*model.Registry, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(*model.Repo, *model.ListOptions) []*model.Registry); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Registry)
}
}
if rf, ok := ret.Get(1).(func(*model.Repo, *model.ListOptions) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RegistryListPipeline provides a mock function with given fields: _a0, _a1
func (_m *Service) RegistryListPipeline(_a0 *model.Repo, _a1 *model.Pipeline) ([]*model.Registry, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for RegistryListPipeline")
}
var r0 []*model.Registry
var r1 error
if rf, ok := ret.Get(0).(func(*model.Repo, *model.Pipeline) ([]*model.Registry, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(*model.Repo, *model.Pipeline) []*model.Registry); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Registry)
}
}
if rf, ok := ret.Get(1).(func(*model.Repo, *model.Pipeline) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RegistryUpdate provides a mock function with given fields: _a0, _a1
func (_m *Service) RegistryUpdate(_a0 *model.Repo, _a1 *model.Registry) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for RegistryUpdate")
}
var r0 error
if rf, ok := ret.Get(0).(func(*model.Repo, *model.Registry) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// NewService creates a new instance of Service. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewService(t interface {
mock.TestingT
Cleanup(func())
}) *Service {
mock := &Service{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View file

@ -16,6 +16,8 @@ package registry
import "go.woodpecker-ci.org/woodpecker/v2/server/model"
//go:generate mockery --name Service --output mocks --case underscore
// Service defines a service for managing registries.
type Service interface {
RegistryListPipeline(*model.Repo, *model.Pipeline) ([]*model.Registry, error)

View file

@ -0,0 +1,399 @@
// Code generated by mockery. DO NOT EDIT.
package mocks
import (
mock "github.com/stretchr/testify/mock"
model "go.woodpecker-ci.org/woodpecker/v2/server/model"
)
// Service is an autogenerated mock type for the Service type
type Service struct {
mock.Mock
}
// GlobalSecretCreate provides a mock function with given fields: _a0
func (_m *Service) GlobalSecretCreate(_a0 *model.Secret) error {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for GlobalSecretCreate")
}
var r0 error
if rf, ok := ret.Get(0).(func(*model.Secret) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
}
return r0
}
// GlobalSecretDelete provides a mock function with given fields: _a0
func (_m *Service) GlobalSecretDelete(_a0 string) error {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for GlobalSecretDelete")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
}
return r0
}
// GlobalSecretFind provides a mock function with given fields: _a0
func (_m *Service) GlobalSecretFind(_a0 string) (*model.Secret, error) {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for GlobalSecretFind")
}
var r0 *model.Secret
var r1 error
if rf, ok := ret.Get(0).(func(string) (*model.Secret, error)); ok {
return rf(_a0)
}
if rf, ok := ret.Get(0).(func(string) *model.Secret); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Secret)
}
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GlobalSecretList provides a mock function with given fields: _a0
func (_m *Service) GlobalSecretList(_a0 *model.ListOptions) ([]*model.Secret, error) {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for GlobalSecretList")
}
var r0 []*model.Secret
var r1 error
if rf, ok := ret.Get(0).(func(*model.ListOptions) ([]*model.Secret, error)); ok {
return rf(_a0)
}
if rf, ok := ret.Get(0).(func(*model.ListOptions) []*model.Secret); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Secret)
}
}
if rf, ok := ret.Get(1).(func(*model.ListOptions) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GlobalSecretUpdate provides a mock function with given fields: _a0
func (_m *Service) GlobalSecretUpdate(_a0 *model.Secret) error {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for GlobalSecretUpdate")
}
var r0 error
if rf, ok := ret.Get(0).(func(*model.Secret) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
}
return r0
}
// OrgSecretCreate provides a mock function with given fields: _a0, _a1
func (_m *Service) OrgSecretCreate(_a0 int64, _a1 *model.Secret) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for OrgSecretCreate")
}
var r0 error
if rf, ok := ret.Get(0).(func(int64, *model.Secret) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// OrgSecretDelete provides a mock function with given fields: _a0, _a1
func (_m *Service) OrgSecretDelete(_a0 int64, _a1 string) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for OrgSecretDelete")
}
var r0 error
if rf, ok := ret.Get(0).(func(int64, string) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// OrgSecretFind provides a mock function with given fields: _a0, _a1
func (_m *Service) OrgSecretFind(_a0 int64, _a1 string) (*model.Secret, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for OrgSecretFind")
}
var r0 *model.Secret
var r1 error
if rf, ok := ret.Get(0).(func(int64, string) (*model.Secret, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(int64, string) *model.Secret); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Secret)
}
}
if rf, ok := ret.Get(1).(func(int64, string) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// OrgSecretList provides a mock function with given fields: _a0, _a1
func (_m *Service) OrgSecretList(_a0 int64, _a1 *model.ListOptions) ([]*model.Secret, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for OrgSecretList")
}
var r0 []*model.Secret
var r1 error
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(int64, *model.ListOptions) []*model.Secret); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Secret)
}
}
if rf, ok := ret.Get(1).(func(int64, *model.ListOptions) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// OrgSecretUpdate provides a mock function with given fields: _a0, _a1
func (_m *Service) OrgSecretUpdate(_a0 int64, _a1 *model.Secret) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for OrgSecretUpdate")
}
var r0 error
if rf, ok := ret.Get(0).(func(int64, *model.Secret) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// SecretCreate provides a mock function with given fields: _a0, _a1
func (_m *Service) SecretCreate(_a0 *model.Repo, _a1 *model.Secret) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for SecretCreate")
}
var r0 error
if rf, ok := ret.Get(0).(func(*model.Repo, *model.Secret) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// SecretDelete provides a mock function with given fields: _a0, _a1
func (_m *Service) SecretDelete(_a0 *model.Repo, _a1 string) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for SecretDelete")
}
var r0 error
if rf, ok := ret.Get(0).(func(*model.Repo, string) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// SecretFind provides a mock function with given fields: _a0, _a1
func (_m *Service) SecretFind(_a0 *model.Repo, _a1 string) (*model.Secret, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for SecretFind")
}
var r0 *model.Secret
var r1 error
if rf, ok := ret.Get(0).(func(*model.Repo, string) (*model.Secret, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(*model.Repo, string) *model.Secret); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Secret)
}
}
if rf, ok := ret.Get(1).(func(*model.Repo, string) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SecretList provides a mock function with given fields: _a0, _a1
func (_m *Service) SecretList(_a0 *model.Repo, _a1 *model.ListOptions) ([]*model.Secret, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for SecretList")
}
var r0 []*model.Secret
var r1 error
if rf, ok := ret.Get(0).(func(*model.Repo, *model.ListOptions) ([]*model.Secret, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(*model.Repo, *model.ListOptions) []*model.Secret); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Secret)
}
}
if rf, ok := ret.Get(1).(func(*model.Repo, *model.ListOptions) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SecretListPipeline provides a mock function with given fields: _a0, _a1
func (_m *Service) SecretListPipeline(_a0 *model.Repo, _a1 *model.Pipeline) ([]*model.Secret, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for SecretListPipeline")
}
var r0 []*model.Secret
var r1 error
if rf, ok := ret.Get(0).(func(*model.Repo, *model.Pipeline) ([]*model.Secret, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(*model.Repo, *model.Pipeline) []*model.Secret); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Secret)
}
}
if rf, ok := ret.Get(1).(func(*model.Repo, *model.Pipeline) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SecretUpdate provides a mock function with given fields: _a0, _a1
func (_m *Service) SecretUpdate(_a0 *model.Repo, _a1 *model.Secret) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for SecretUpdate")
}
var r0 error
if rf, ok := ret.Get(0).(func(*model.Repo, *model.Secret) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// NewService creates a new instance of Service. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewService(t interface {
mock.TestingT
Cleanup(func())
}) *Service {
mock := &Service{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View file

@ -16,6 +16,8 @@ package secret
import "go.woodpecker-ci.org/woodpecker/v2/server/model"
//go:generate mockery --name Service --output mocks --case underscore
// Service defines a service for managing secrets.
type Service interface {
SecretListPipeline(*model.Repo, *model.Pipeline) ([]*model.Secret, error)

View file

@ -30,7 +30,7 @@ func (s storage) GetUserRemoteID(remoteID model.ForgeRemoteID, login string) (*m
user := new(model.User)
err := wrapGet(sess.Where("forge_remote_id = ?", remoteID).Get(user))
if err != nil {
user, err = s.getUserLogin(sess, login)
return s.getUserLogin(sess, login)
}
return user, err
}

View file

@ -1,5 +1,6 @@
{
"cancel": "Cancel",
"login_with": "Login with {forge}",
"login": "Login",
"welcome": "Welcome to Woodpecker",
"repos": "Repos",

View file

@ -7,11 +7,11 @@ export default () =>
user: useConfig().user,
authenticate(url?: string) {
authenticate(url?: string, forgeId?: number) {
if (url !== undefined) {
const config = useUserConfig();
config.setUserConfig('redirectUrl', url);
}
window.location.href = `${useConfig().rootPath}/authorize`;
window.location.href = `${useConfig().rootPath}/authorize?forge_id=${forgeId}`;
},
}) as const;

View file

@ -21,7 +21,16 @@
</div>
<div class="flex justify-center items-center flex-col md:w-2/5 min-h-48 gap-4 text-center">
<h1 class="text-xl text-wp-text-100">{{ $t('welcome') }}</h1>
<Button @click="doLogin">{{ $t('login') }}</Button>
<div class="flex flex-col gap-2">
<Button
v-for="forge in forges"
:key="forge.id"
:start-icon="forge.type === 'addon' ? 'repo' : forge.type"
@click="doLogin(forge.id)"
>
{{ $t('login_with', { forge: getHostFromUrl(forge) }) }}
</Button>
</div>
</div>
</div>
</main>
@ -35,16 +44,30 @@ import { useRoute, useRouter } from 'vue-router';
import WoodpeckerLogo from '~/assets/logo.svg?component';
import Button from '~/components/atomic/Button.vue';
import Error from '~/components/atomic/Error.vue';
import useApiClient from '~/compositions/useApiClient';
import useAuthentication from '~/compositions/useAuthentication';
import type { Forge } from '~/lib/api/types';
const route = useRoute();
const router = useRouter();
const authentication = useAuthentication();
const i18n = useI18n();
const apiClient = useApiClient();
function doLogin() {
const forges = ref<Forge[]>([]);
function getHostFromUrl(forge: Forge) {
if (!forge.url) {
return forge.type.charAt(0).toUpperCase() + forge.type.slice(1);
}
const url = new URL(forge.url);
return url.hostname;
}
function doLogin(forgeId?: number) {
const url = typeof route.query.url === 'string' ? route.query.url : '';
authentication.authenticate(url);
authentication.authenticate(url, forgeId);
}
const authErrorMessages = {
@ -65,6 +88,8 @@ onMounted(async () => {
return;
}
forges.value = (await apiClient.getForges()) ?? [];
if (route.query.error) {
const error = route.query.error as keyof typeof authErrorMessages;
errorMessage.value = authErrorMessages[error] ?? error;