mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-06-05 00:48:52 +00:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
e4efc039c9
12 changed files with 236 additions and 63 deletions
|
@ -116,6 +116,8 @@ secret=""
|
|||
|
||||
[gitlab]
|
||||
url=""
|
||||
client=""
|
||||
secret=""
|
||||
skip_verify=false
|
||||
|
||||
[gogs]
|
||||
|
@ -178,6 +180,8 @@ export DRONE_BITBUCKET_SECRET=""
|
|||
|
||||
# gitlab configuration
|
||||
export DRONE_GITLAB_URL=""
|
||||
export DRONE_GITLAB_CLIENT=""
|
||||
export DRONE_GITLAB_SECRET=""
|
||||
export DRONE_GITLAB_SKIP_VERIFY=false
|
||||
|
||||
# email configuration
|
||||
|
|
|
@ -44,6 +44,8 @@ datasource="/var/lib/drone/drone.sqlite"
|
|||
|
||||
# [gitlab]
|
||||
# url=""
|
||||
# client=""
|
||||
# secret=""
|
||||
# skip_verify=false
|
||||
# open=false
|
||||
|
||||
|
|
105
plugin/notify/flowdock/client.go
Normal file
105
plugin/notify/flowdock/client.go
Normal file
|
@ -0,0 +1,105 @@
|
|||
package flowdock
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
ENDPOINT = "https://api.flowdock.com/v1/messages/team_inbox/"
|
||||
)
|
||||
|
||||
var (
|
||||
// Required default client settings
|
||||
Token = ""
|
||||
Source = ""
|
||||
FromAddress = ""
|
||||
|
||||
// Optional default client settings
|
||||
FromName = ""
|
||||
ReplyTo = ""
|
||||
Project = ""
|
||||
Link = ""
|
||||
Tags = []string{}
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
// Required
|
||||
Token string
|
||||
Source string
|
||||
FromAddress string
|
||||
Subject string
|
||||
Content string
|
||||
|
||||
// Optional
|
||||
FromName string
|
||||
ReplyTo string
|
||||
Project string
|
||||
Link string
|
||||
Tags []string
|
||||
}
|
||||
|
||||
func (c *Client) Inbox(subject, content string) error {
|
||||
return send(c.Token, c.Source, c.FromAddress, subject, content, c.FromName, c.ReplyTo, c.Project, c.Link, c.Tags)
|
||||
}
|
||||
|
||||
func Inbox(subject, content string) error {
|
||||
return send(Token, Source, FromAddress, subject, content, FromName, ReplyTo, Project, Link, Tags)
|
||||
}
|
||||
|
||||
func send(token, source, fromAddress, subject, content, fromName, replyTo, project, link string, tags []string) error {
|
||||
// Required validation
|
||||
if len(token) == 0 {
|
||||
return fmt.Errorf(`"Token" is required`)
|
||||
}
|
||||
if len(source) == 0 {
|
||||
return fmt.Errorf(`"Source" is required`)
|
||||
}
|
||||
if len(fromAddress) == 0 {
|
||||
return fmt.Errorf(`"FromAddress" is required`)
|
||||
}
|
||||
if len(subject) == 0 {
|
||||
return fmt.Errorf(`"Subject" is required`)
|
||||
}
|
||||
|
||||
// Build payload
|
||||
payload := map[string]interface{}{
|
||||
"source": source,
|
||||
"from_address": fromAddress,
|
||||
"subject": subject,
|
||||
"content": content,
|
||||
}
|
||||
if len(fromName) > 0 {
|
||||
payload["from_name"] = fromName
|
||||
}
|
||||
if len(replyTo) > 0 {
|
||||
payload["reply_to"] = replyTo
|
||||
}
|
||||
if len(project) > 0 {
|
||||
payload["project"] = project
|
||||
}
|
||||
if len(link) > 0 {
|
||||
payload["link"] = link
|
||||
}
|
||||
if len(tags) > 0 {
|
||||
payload["tags"] = tags
|
||||
}
|
||||
jsonPayload, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Send to Flowdock
|
||||
resp, err := http.Post(ENDPOINT+token, "application/json", bytes.NewReader(jsonPayload))
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == 200 {
|
||||
return nil
|
||||
} else {
|
||||
bodyBytes, _ := ioutil.ReadAll(resp.Body)
|
||||
return fmt.Errorf("Unexpected response from Flowdock: %s %s", resp.Status, string(bodyBytes))
|
||||
}
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
package notify
|
||||
package flowdock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone/shared/model"
|
||||
"github.com/stvp/flowdock"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -78,7 +77,7 @@ func (f *Flowdock) sendSuccess(context *model.Request) error {
|
|||
|
||||
// helper function to send Flowdock requests
|
||||
func (f *Flowdock) send(fromAddress, subject, message string, tags []string) error {
|
||||
c := flowdock.Client{Token: f.Token, Source: f.Source, FromName: "drone.io", FromAddress: fromAddress, Tags: tags}
|
||||
c := Client{Token: f.Token, Source: f.Source, FromName: "drone.io", FromAddress: fromAddress, Tags: tags}
|
||||
go c.Inbox(subject, message)
|
||||
return nil
|
||||
}
|
|
@ -7,6 +7,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/drone/drone/plugin/notify/email"
|
||||
"github.com/drone/drone/plugin/notify/flowdock"
|
||||
"github.com/drone/drone/plugin/notify/github"
|
||||
"github.com/drone/drone/plugin/notify/irc"
|
||||
"github.com/drone/drone/plugin/notify/katoim"
|
||||
|
@ -22,14 +23,14 @@ type Sender interface {
|
|||
// for notifying a user, or group of users,
|
||||
// when their Build has completed.
|
||||
type Notification struct {
|
||||
Email *email.Email `yaml:"email,omitempty"`
|
||||
Webhook *webhook.Webhook `yaml:"webhook,omitempty"`
|
||||
Hipchat *Hipchat `yaml:"hipchat,omitempty"`
|
||||
Irc *irc.IRC `yaml:"irc,omitempty"`
|
||||
Slack *Slack `yaml:"slack,omitempty"`
|
||||
Gitter *Gitter `yaml:"gitter,omitempty"`
|
||||
Flowdock *Flowdock `yaml:"flowdock,omitempty"`
|
||||
KatoIM *katoim.KatoIM `yaml:"katoim,omitempty"`
|
||||
Email *email.Email `yaml:"email,omitempty"`
|
||||
Webhook *webhook.Webhook `yaml:"webhook,omitempty"`
|
||||
Hipchat *Hipchat `yaml:"hipchat,omitempty"`
|
||||
Irc *irc.IRC `yaml:"irc,omitempty"`
|
||||
Slack *Slack `yaml:"slack,omitempty"`
|
||||
Gitter *Gitter `yaml:"gitter,omitempty"`
|
||||
Flowdock *flowdock.Flowdock `yaml:"flowdock,omitempty"`
|
||||
KatoIM *katoim.KatoIM `yaml:"katoim,omitempty"`
|
||||
|
||||
GitHub github.GitHub `yaml:"--"`
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package gitlab
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/Bugagazavr/go-gitlab-client"
|
||||
"github.com/drone/drone/plugin/remote/github/oauth"
|
||||
"github.com/drone/drone/shared/httputil"
|
||||
"github.com/drone/drone/shared/model"
|
||||
)
|
||||
|
||||
|
@ -14,34 +17,66 @@ type Gitlab struct {
|
|||
url string
|
||||
SkipVerify bool
|
||||
Open bool
|
||||
Client string
|
||||
Secret string
|
||||
}
|
||||
|
||||
func New(url string, skipVerify, open bool) *Gitlab {
|
||||
func New(url string, skipVerify, open bool, client, secret string) *Gitlab {
|
||||
return &Gitlab{
|
||||
url: url,
|
||||
SkipVerify: skipVerify,
|
||||
Open: open,
|
||||
Client: client,
|
||||
Secret: secret,
|
||||
}
|
||||
}
|
||||
|
||||
// Authorize handles authentication with thrid party remote systems,
|
||||
// such as github or bitbucket, and returns user data.
|
||||
func (r *Gitlab) Authorize(res http.ResponseWriter, req *http.Request) (*model.Login, error) {
|
||||
var username = req.FormValue("username")
|
||||
var password = req.FormValue("password")
|
||||
var config = &oauth.Config{
|
||||
ClientId: r.Client,
|
||||
ClientSecret: r.Secret,
|
||||
Scope: "api",
|
||||
AuthURL: fmt.Sprintf("%s/oauth/authorize", r.url),
|
||||
TokenURL: fmt.Sprintf("%s/oauth/token", r.url),
|
||||
RedirectURL: fmt.Sprintf("%s/api/auth/%s", httputil.GetURL(req), r.GetKind()),
|
||||
}
|
||||
|
||||
var client = NewClient(r.url, "", r.SkipVerify)
|
||||
var session, err = client.GetSession(username, password)
|
||||
var code = req.FormValue("code")
|
||||
var state = req.FormValue("state")
|
||||
|
||||
if len(code) == 0 {
|
||||
var random = GetRandom()
|
||||
httputil.SetCookie(res, req, "gitlab_state", random)
|
||||
http.Redirect(res, req, config.AuthCodeURL(random), http.StatusSeeOther)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
cookieState := httputil.GetCookie(req, "gitlab_state")
|
||||
httputil.DelCookie(res, req, "gitlab_state")
|
||||
if cookieState != state {
|
||||
return nil, fmt.Errorf("Error matching state in OAuth2 redirect")
|
||||
}
|
||||
|
||||
var trans = &oauth.Transport{Config: config}
|
||||
var token, err = trans.Exchange(code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("Error exchanging token. %s", err)
|
||||
}
|
||||
|
||||
var client = NewClient(r.url, token.AccessToken, r.SkipVerify)
|
||||
|
||||
var user, errr = client.CurrentUser()
|
||||
if errr != nil {
|
||||
return nil, fmt.Errorf("Error retrieving current user. %s", errr)
|
||||
}
|
||||
|
||||
var login = new(model.Login)
|
||||
login.ID = int64(session.Id)
|
||||
login.Access = session.PrivateToken
|
||||
login.Login = session.UserName
|
||||
login.Name = session.Name
|
||||
login.Email = session.Email
|
||||
login.ID = int64(user.Id)
|
||||
login.Access = token.AccessToken
|
||||
login.Login = user.Username
|
||||
login.Email = user.Email
|
||||
return login, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package gitlab
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/plugin/remote/gitlab/testdata"
|
||||
|
@ -14,7 +16,7 @@ func Test_Github(t *testing.T) {
|
|||
var server = testdata.NewServer()
|
||||
defer server.Close()
|
||||
|
||||
var gitlab = New(server.URL, false, false)
|
||||
var gitlab = New(server.URL, false, false, "", "")
|
||||
var user = model.User{
|
||||
Access: "e3b0c44298fc1c149afbf4c8996fb",
|
||||
}
|
||||
|
@ -31,17 +33,6 @@ func Test_Github(t *testing.T) {
|
|||
g := goblin.Goblin(t)
|
||||
g.Describe("Gitlab Plugin", func() {
|
||||
|
||||
g.It("Should authorize user", func() {
|
||||
var req, _ = http.NewRequest("GET", "/login/gitlab", nil)
|
||||
var login, err = gitlab.Authorize(nil, req)
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(login.Email).Equal("john@example.com")
|
||||
g.Assert(login.Name).Equal("John Smith")
|
||||
g.Assert(login.Login).Equal("john_smith")
|
||||
g.Assert(login.Access).Equal("dd34asd13as")
|
||||
g.Assert(login.ID).Equal(int64(1))
|
||||
})
|
||||
|
||||
g.It("Should get the repo list", func() {
|
||||
var repos, err = gitlab.GetRepos(&user)
|
||||
g.Assert(err == nil).IsTrue()
|
||||
|
@ -55,6 +46,23 @@ func Test_Github(t *testing.T) {
|
|||
g.Assert(repos[0].Role.Read).Equal(true)
|
||||
g.Assert(repos[0].Role.Write).Equal(true)
|
||||
})
|
||||
|
||||
g.Describe("Authorize", func() {
|
||||
var resp = httptest.NewRecorder()
|
||||
var state = "validstate"
|
||||
var req, _ = http.NewRequest(
|
||||
"GET",
|
||||
fmt.Sprintf("%s/?code=sekret&state=%s", server.URL, state),
|
||||
nil,
|
||||
)
|
||||
req.AddCookie(&http.Cookie{Name: "gitlab_state", Value: state})
|
||||
|
||||
g.It("Should authorize a valid user", func() {
|
||||
var login, err = gitlab.Authorize(resp, req)
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(login == nil).IsFalse()
|
||||
})
|
||||
})
|
||||
/*
|
||||
g.It("Should get the build script", func() {
|
||||
var script, err = github.GetScript(&user, &repo, &commit)
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
package gitlab
|
||||
|
||||
import (
|
||||
"encoding/base32"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/Bugagazavr/go-gitlab-client"
|
||||
"github.com/gorilla/securecookie"
|
||||
)
|
||||
|
||||
// NewClient is a helper function that returns a new GitHub
|
||||
// client using the provided OAuth token.
|
||||
func NewClient(uri, token string, skipVerify bool) *gogitlab.Gitlab {
|
||||
return gogitlab.NewGitlabCert(uri, "/api/v3", token, skipVerify)
|
||||
client := gogitlab.NewGitlabCert(uri, "/api/v3", token, skipVerify)
|
||||
client.Bearer = true
|
||||
return client
|
||||
}
|
||||
|
||||
// IsRead is a helper function that returns true if the
|
||||
|
@ -76,3 +80,9 @@ func GetKeyTitle(rawurl string) (string, error) {
|
|||
func ns(owner, name string) string {
|
||||
return fmt.Sprintf("%s%%2F%s", owner, name)
|
||||
}
|
||||
|
||||
// GetRandom is a helper function that generates a 32-bit random
|
||||
// key, base32 encoded as a string value.
|
||||
func GetRandom() string {
|
||||
return base32.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(32))
|
||||
}
|
||||
|
|
|
@ -9,6 +9,9 @@ var (
|
|||
gitlabURL = config.String("gitlab-url", "")
|
||||
gitlabSkipVerify = config.Bool("gitlab-skip-verify", false)
|
||||
gitlabOpen = config.Bool("gitlab-open", false)
|
||||
|
||||
gitlabClient = config.String("gitlab-client", "")
|
||||
gitlabSecret = config.String("gitlab-secret", "")
|
||||
)
|
||||
|
||||
// Registers the Gitlab plugin using the default
|
||||
|
@ -23,6 +26,8 @@ func Register() {
|
|||
*gitlabURL,
|
||||
*gitlabSkipVerify,
|
||||
*gitlabOpen,
|
||||
*gitlabClient,
|
||||
*gitlabSecret,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
31
plugin/remote/gitlab/testdata/testdata.go
vendored
31
plugin/remote/gitlab/testdata/testdata.go
vendored
|
@ -27,6 +27,12 @@ func NewServer() *httptest.Server {
|
|||
case "/api/v3/session":
|
||||
w.Write(sessionPayload)
|
||||
return
|
||||
case "/oauth/token":
|
||||
w.Write(accessTokenPayload)
|
||||
return
|
||||
case "/api/v3/user":
|
||||
w.Write(currentUserPayload)
|
||||
return
|
||||
}
|
||||
|
||||
// else return a 404
|
||||
|
@ -231,3 +237,28 @@ var droneYamlPayload = []byte(`
|
|||
"content": "aW1hZ2U6IGdv"
|
||||
}
|
||||
`)
|
||||
|
||||
var accessTokenPayload = []byte(`access_token=sekret&scope=api&token_type=bearer`)
|
||||
|
||||
var currentUserPayload = []byte(`
|
||||
{
|
||||
"id": 1,
|
||||
"username": "john_smith",
|
||||
"email": "john@example.com",
|
||||
"name": "John Smith",
|
||||
"private_token": "dd34asd13as",
|
||||
"state": "active",
|
||||
"created_at": "2012-05-23T08:00:58Z",
|
||||
"bio": null,
|
||||
"skype": "",
|
||||
"linkedin": "",
|
||||
"twitter": "",
|
||||
"website_url": "",
|
||||
"theme_id": 1,
|
||||
"color_scheme_id": 2,
|
||||
"is_admin": false,
|
||||
"can_create_group": true,
|
||||
"can_create_project": true,
|
||||
"projects_limit": 100
|
||||
}
|
||||
`)
|
||||
|
|
|
@ -11,10 +11,7 @@ minor modifications to the style that only apply to this view
|
|||
<article id="loginpage">
|
||||
<div class="pure-g">
|
||||
<div class="pure-u-1" ng-if="state == 1 && remotes.length != 0" ng-repeat="remote in remotes">
|
||||
<a ng-href="/api/auth/{{ remote.type }}" target="_self" ng-if="remote.type != 'gitlab.com' && remote.type != 'gogs' ">
|
||||
<i class="fa {{ remote.type | remoteIcon }}"></i> {{ remote.type | remoteName }}
|
||||
</a>
|
||||
<a ng-href="/gitlab" ng-if="remote.type == 'gitlab.com' ">
|
||||
<a ng-href="/api/auth/{{ remote.type }}" target="_self" ng-if="remote.type != 'gogs' ">
|
||||
<i class="fa {{ remote.type | remoteIcon }}"></i> {{ remote.type | remoteName }}
|
||||
</a>
|
||||
<a ng-href="/gogs" ng-if="remote.type == 'gogs' ">
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
<!--
|
||||
minor modifications to the style that only apply to this view
|
||||
-->
|
||||
<style>
|
||||
#container { padding-top: 155px; }
|
||||
#header { height: 150px; }
|
||||
#header .user { display:none; }
|
||||
#header .brand { margin-top:55px ; }
|
||||
</style>
|
||||
|
||||
<article id="loginpage">
|
||||
<form class="pure-g" method="POST" action="/api/auth/gitlab.com">
|
||||
<div class="pure-u-1">
|
||||
<input type="text" name="username" placeholder="Email Address" />
|
||||
</div>
|
||||
<div class="pure-u-1">
|
||||
<input type="password" name="password" placeholder="Password" />
|
||||
</div>
|
||||
|
||||
<div class="pure-u-1">
|
||||
<input type="submit" value="Gitlab Login" />
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
Loading…
Reference in a new issue