mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-23 08:56:29 +00:00
Gitlab oauth login
This commit is contained in:
parent
44c5ca2b55
commit
f47ab27702
9 changed files with 120 additions and 52 deletions
|
@ -116,6 +116,8 @@ secret=""
|
||||||
|
|
||||||
[gitlab]
|
[gitlab]
|
||||||
url=""
|
url=""
|
||||||
|
client=""
|
||||||
|
secret=""
|
||||||
skip_verify=false
|
skip_verify=false
|
||||||
|
|
||||||
[gogs]
|
[gogs]
|
||||||
|
@ -178,6 +180,8 @@ export DRONE_BITBUCKET_SECRET=""
|
||||||
|
|
||||||
# gitlab configuration
|
# gitlab configuration
|
||||||
export DRONE_GITLAB_URL=""
|
export DRONE_GITLAB_URL=""
|
||||||
|
export DRONE_GITLAB_CLIENT=""
|
||||||
|
export DRONE_GITLAB_SECRET=""
|
||||||
export DRONE_GITLAB_SKIP_VERIFY=false
|
export DRONE_GITLAB_SKIP_VERIFY=false
|
||||||
|
|
||||||
# email configuration
|
# email configuration
|
||||||
|
|
|
@ -44,6 +44,8 @@ datasource="/var/lib/drone/drone.sqlite"
|
||||||
|
|
||||||
# [gitlab]
|
# [gitlab]
|
||||||
# url=""
|
# url=""
|
||||||
|
# client=""
|
||||||
|
# secret=""
|
||||||
# skip_verify=false
|
# skip_verify=false
|
||||||
# open=false
|
# open=false
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
package gitlab
|
package gitlab
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/Bugagazavr/go-gitlab-client"
|
"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"
|
"github.com/drone/drone/shared/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,34 +17,66 @@ type Gitlab struct {
|
||||||
url string
|
url string
|
||||||
SkipVerify bool
|
SkipVerify bool
|
||||||
Open 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{
|
return &Gitlab{
|
||||||
url: url,
|
url: url,
|
||||||
SkipVerify: skipVerify,
|
SkipVerify: skipVerify,
|
||||||
Open: open,
|
Open: open,
|
||||||
|
Client: client,
|
||||||
|
Secret: secret,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authorize handles authentication with thrid party remote systems,
|
// Authorize handles authentication with thrid party remote systems,
|
||||||
// such as github or bitbucket, and returns user data.
|
// such as github or bitbucket, and returns user data.
|
||||||
func (r *Gitlab) Authorize(res http.ResponseWriter, req *http.Request) (*model.Login, error) {
|
func (r *Gitlab) Authorize(res http.ResponseWriter, req *http.Request) (*model.Login, error) {
|
||||||
var username = req.FormValue("username")
|
var config = &oauth.Config{
|
||||||
var password = req.FormValue("password")
|
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 code = req.FormValue("code")
|
||||||
var session, err = client.GetSession(username, password)
|
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 {
|
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)
|
var login = new(model.Login)
|
||||||
login.ID = int64(session.Id)
|
login.ID = int64(user.Id)
|
||||||
login.Access = session.PrivateToken
|
login.Access = token.AccessToken
|
||||||
login.Login = session.UserName
|
login.Login = user.Username
|
||||||
login.Name = session.Name
|
login.Email = user.Email
|
||||||
login.Email = session.Email
|
|
||||||
return login, nil
|
return login, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package gitlab
|
package gitlab
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/drone/drone/plugin/remote/gitlab/testdata"
|
"github.com/drone/drone/plugin/remote/gitlab/testdata"
|
||||||
|
@ -14,7 +16,7 @@ func Test_Github(t *testing.T) {
|
||||||
var server = testdata.NewServer()
|
var server = testdata.NewServer()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
var gitlab = New(server.URL, false, false)
|
var gitlab = New(server.URL, false, false, "", "")
|
||||||
var user = model.User{
|
var user = model.User{
|
||||||
Access: "e3b0c44298fc1c149afbf4c8996fb",
|
Access: "e3b0c44298fc1c149afbf4c8996fb",
|
||||||
}
|
}
|
||||||
|
@ -31,17 +33,6 @@ func Test_Github(t *testing.T) {
|
||||||
g := goblin.Goblin(t)
|
g := goblin.Goblin(t)
|
||||||
g.Describe("Gitlab Plugin", func() {
|
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() {
|
g.It("Should get the repo list", func() {
|
||||||
var repos, err = gitlab.GetRepos(&user)
|
var repos, err = gitlab.GetRepos(&user)
|
||||||
g.Assert(err == nil).IsTrue()
|
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.Read).Equal(true)
|
||||||
g.Assert(repos[0].Role.Write).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() {
|
g.It("Should get the build script", func() {
|
||||||
var script, err = github.GetScript(&user, &repo, &commit)
|
var script, err = github.GetScript(&user, &repo, &commit)
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
package gitlab
|
package gitlab
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base32"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/Bugagazavr/go-gitlab-client"
|
"github.com/Bugagazavr/go-gitlab-client"
|
||||||
|
"github.com/gorilla/securecookie"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewClient is a helper function that returns a new GitHub
|
// NewClient is a helper function that returns a new GitHub
|
||||||
// client using the provided OAuth token.
|
// client using the provided OAuth token.
|
||||||
func NewClient(uri, token string, skipVerify bool) *gogitlab.Gitlab {
|
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
|
// 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 {
|
func ns(owner, name string) string {
|
||||||
return fmt.Sprintf("%s%%2F%s", owner, name)
|
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", "")
|
gitlabURL = config.String("gitlab-url", "")
|
||||||
gitlabSkipVerify = config.Bool("gitlab-skip-verify", false)
|
gitlabSkipVerify = config.Bool("gitlab-skip-verify", false)
|
||||||
gitlabOpen = config.Bool("gitlab-open", false)
|
gitlabOpen = config.Bool("gitlab-open", false)
|
||||||
|
|
||||||
|
gitlabClient = config.String("gitlab-client", "")
|
||||||
|
gitlabSecret = config.String("gitlab-secret", "")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Registers the Gitlab plugin using the default
|
// Registers the Gitlab plugin using the default
|
||||||
|
@ -23,6 +26,8 @@ func Register() {
|
||||||
*gitlabURL,
|
*gitlabURL,
|
||||||
*gitlabSkipVerify,
|
*gitlabSkipVerify,
|
||||||
*gitlabOpen,
|
*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":
|
case "/api/v3/session":
|
||||||
w.Write(sessionPayload)
|
w.Write(sessionPayload)
|
||||||
return
|
return
|
||||||
|
case "/oauth/token":
|
||||||
|
w.Write(accessTokenPayload)
|
||||||
|
return
|
||||||
|
case "/api/v3/user":
|
||||||
|
w.Write(currentUserPayload)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// else return a 404
|
// else return a 404
|
||||||
|
@ -231,3 +237,28 @@ var droneYamlPayload = []byte(`
|
||||||
"content": "aW1hZ2U6IGdv"
|
"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">
|
<article id="loginpage">
|
||||||
<div class="pure-g">
|
<div class="pure-g">
|
||||||
<div class="pure-u-1" ng-if="state == 1 && remotes.length != 0" ng-repeat="remote in remotes">
|
<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' ">
|
<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="/gitlab" ng-if="remote.type == 'gitlab.com' ">
|
|
||||||
<i class="fa {{ remote.type | remoteIcon }}"></i> {{ remote.type | remoteName }}
|
<i class="fa {{ remote.type | remoteIcon }}"></i> {{ remote.type | remoteName }}
|
||||||
</a>
|
</a>
|
||||||
<a ng-href="/gogs" ng-if="remote.type == 'gogs' ">
|
<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