refactoring remotes to remove deprecated variables, adding tests

This commit is contained in:
Brad Rydzewski 2016-05-01 16:30:00 -07:00
parent b978ed12eb
commit ebd547deac
21 changed files with 797 additions and 696 deletions

View file

@ -2,13 +2,11 @@ package api
import ( import (
"fmt" "fmt"
"io/ioutil"
"net/http" "net/http"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/drone/drone/cache" "github.com/drone/drone/cache"
"github.com/drone/drone/model"
"github.com/drone/drone/remote" "github.com/drone/drone/remote"
"github.com/drone/drone/router/middleware/session" "github.com/drone/drone/router/middleware/session"
"github.com/drone/drone/shared/crypto" "github.com/drone/drone/shared/crypto"
@ -72,19 +70,9 @@ func PostRepo(c *gin.Context) {
sig, sig,
) )
// generate an RSA key and add to the repo
key, err := crypto.GeneratePrivateKey()
if err != nil {
c.String(500, err.Error())
return
}
keys := new(model.Key)
keys.Public = string(crypto.MarshalPublicKey(&key.PublicKey))
keys.Private = string(crypto.MarshalPrivateKey(key))
// activate the repository before we make any // activate the repository before we make any
// local changes to the database. // local changes to the database.
err = remote.Activate(user, r, keys, link) err = remote.Activate(user, r, link)
if err != nil { if err != nil {
c.String(500, err.Error()) c.String(500, err.Error())
return return
@ -96,12 +84,6 @@ func PostRepo(c *gin.Context) {
c.String(500, err.Error()) c.String(500, err.Error())
return return
} }
keys.RepoID = r.ID
err = store.CreateKey(c, keys)
if err != nil {
c.String(500, err.Error())
return
}
c.JSON(200, r) c.JSON(200, r)
} }
@ -155,45 +137,6 @@ func GetRepo(c *gin.Context) {
c.JSON(http.StatusOK, session.Repo(c)) c.JSON(http.StatusOK, session.Repo(c))
} }
func GetRepoKey(c *gin.Context) {
repo := session.Repo(c)
keys, err := store.GetKey(c, repo)
if err != nil {
c.String(404, "Error fetching repository key")
} else {
c.String(http.StatusOK, keys.Public)
}
}
func PostRepoKey(c *gin.Context) {
repo := session.Repo(c)
keys, err := store.GetKey(c, repo)
if err != nil {
c.String(404, "Error fetching repository key")
return
}
body, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
c.String(500, "Error reading private key from body. %s", err)
return
}
pkey := crypto.UnmarshalPrivateKey(body)
if pkey == nil {
c.String(500, "Cannot unmarshal private key. Invalid format.")
return
}
keys.Public = string(crypto.MarshalPublicKey(&pkey.PublicKey))
keys.Private = string(crypto.MarshalPrivateKey(pkey))
err = store.UpdateKey(c, keys)
if err != nil {
c.String(500, "Error updating repository key")
return
}
c.String(201, keys.Public)
}
func DeleteRepo(c *gin.Context) { func DeleteRepo(c *gin.Context) {
remote := remote.FromContext(c) remote := remote.FromContext(c)
repo := session.Repo(c) repo := session.Repo(c)

View file

@ -2,7 +2,6 @@ package main
import ( import (
"fmt" "fmt"
"log"
"net/http" "net/http"
"os" "os"
"time" "time"
@ -159,6 +158,16 @@ var DaemonCmd = cli.Command{
Usage: "gogs server address", Usage: "gogs server address",
Value: "https://github.com", Value: "https://github.com",
}, },
cli.StringFlag{
EnvVar: "DRONE_GOGS_GIT_USERNAME",
Name: "gogs-git-username",
Usage: "gogs service account username",
},
cli.StringFlag{
EnvVar: "DRONE_GOGS_GIT_PASSWORD",
Name: "gogs-git-password",
Usage: "gogs service account password",
},
cli.BoolFlag{ cli.BoolFlag{
EnvVar: "DRONE_GOGS_PRIVATE_MODE", EnvVar: "DRONE_GOGS_PRIVATE_MODE",
Name: "gogs-private-mode", Name: "gogs-private-mode",
@ -205,6 +214,16 @@ var DaemonCmd = cli.Command{
Name: "gitlab-sercret", Name: "gitlab-sercret",
Usage: "gitlab oauth2 client secret", Usage: "gitlab oauth2 client secret",
}, },
cli.StringFlag{
EnvVar: "DRONE_GITLAB_GIT_USERNAME",
Name: "gitlab-git-username",
Usage: "gitlab service account username",
},
cli.StringFlag{
EnvVar: "DRONE_GITLAB_GIT_PASSWORD",
Name: "gitlab-git-password",
Usage: "gitlab service account password",
},
cli.BoolFlag{ cli.BoolFlag{
EnvVar: "DRONE_GITLAB_SKIP_VERIFY", EnvVar: "DRONE_GITLAB_SKIP_VERIFY",
Name: "gitlab-skip-verify", Name: "gitlab-skip-verify",
@ -245,7 +264,11 @@ var DaemonCmd = cli.Command{
Name: "stash-git-password", Name: "stash-git-password",
Usage: "stash service account password", Usage: "stash service account password",
}, },
cli.BoolFlag{
EnvVar: "DRONE_STASH_SKIP_VERIFY",
Name: "stash-skip-verify",
Usage: "stash skip ssl verification",
},
// //
// remove these eventually // remove these eventually
// //
@ -347,60 +370,70 @@ func setupStore(c *cli.Context) store.Store {
} }
func setupRemote(c *cli.Context) remote.Remote { func setupRemote(c *cli.Context) remote.Remote {
var remote remote.Remote
var err error
switch { switch {
case c.Bool("github"): case c.Bool("github"):
return setupGithub(c) remote, err = setupGithub(c)
case c.Bool("gitlab"): case c.Bool("gitlab"):
return setupGitlab(c) remote, err = setupGitlab(c)
case c.Bool("bitbucket"): case c.Bool("bitbucket"):
return setupBitbucket(c) remote, err = setupBitbucket(c)
case c.Bool("stash"): case c.Bool("stash"):
return setupStash(c) remote, err = setupStash(c)
case c.Bool("gogs"): case c.Bool("gogs"):
return setupGogs(c) remote, err = setupGogs(c)
default: default:
logrus.Fatalln("version control system not configured") err = fmt.Errorf("version control system not configured")
return nil
} }
if err != nil {
logrus.Fatalln(err)
}
return remote
} }
func setupBitbucket(c *cli.Context) remote.Remote { func setupBitbucket(c *cli.Context) (remote.Remote, error) {
return bitbucket.New( return bitbucket.New(
c.String("bitbucket-client"), c.String("bitbucket-client"),
c.String("bitbucket-server"), c.String("bitbucket-server"),
) ), nil
} }
func setupGogs(c *cli.Context) remote.Remote { func setupGogs(c *cli.Context) (remote.Remote, error) {
return gogs.New( return gogs.New(gogs.Opts{
c.String("gogs-server"), URL: c.String("gogs-server"),
c.Bool("gogs-private-mode"), Username: c.String("gogs-git-username"),
c.Bool("gogs-skip-verify"), Password: c.String("gogs-git-password"),
) PrivateMode: c.Bool("gogs-private-mode"),
SkipVerify: c.Bool("gogs-skip-verify"),
})
} }
func setupStash(c *cli.Context) remote.Remote { func setupStash(c *cli.Context) (remote.Remote, error) {
return bitbucketserver.New( return bitbucketserver.New(bitbucketserver.Opts{
c.String("stash-server"), URL: c.String("stash-server"),
c.String("stash-consumer-key"), Username: c.String("stash-git-username"),
c.String("stash-consumer-rsa"), Password: c.String("stash-git-password"),
c.String("stash-git-username"), ConsumerKey: c.String("stash-consumer-key"),
c.String("stash-git-password"), ConsumerRSA: c.String("stash-consumer-rsa"),
) SkipVerify: c.Bool("stash-skip-verify"),
})
} }
func setupGitlab(c *cli.Context) remote.Remote { func setupGitlab(c *cli.Context) (remote.Remote, error) {
return gitlab.New( return gitlab.New(gitlab.Opts{
c.String("gitlab-server"), URL: c.String("gitlab-server"),
c.String("gitlab-client"), Client: c.String("gitlab-client"),
c.String("gitlab-sercret"), Secret: c.String("gitlab-sercret"),
c.Bool("gitlab-private-mode"), Username: c.String("gitlab-git-username"),
c.Bool("gitlab-skip-verify"), Password: c.String("gitlab-git-password"),
) PrivateMode: c.Bool("gitlab-private-mode"),
SkipVerify: c.Bool("gitlab-skip-verify"),
})
} }
func setupGithub(c *cli.Context) remote.Remote { func setupGithub(c *cli.Context) (remote.Remote, error) {
g, err := github.New( return github.New(
c.String("github-server"), c.String("github-server"),
c.String("github-client"), c.String("github-client"),
c.String("github-sercret"), c.String("github-sercret"),
@ -409,10 +442,6 @@ func setupGithub(c *cli.Context) remote.Remote {
c.Bool("github-skip-verify"), c.Bool("github-skip-verify"),
c.BoolT("github-merge-ref"), c.BoolT("github-merge-ref"),
) )
if err != nil {
log.Fatalln(err)
}
return g
} }
func printSecret(c *cli.Context) error { func printSecret(c *cli.Context) error {

View file

@ -132,7 +132,6 @@ func (c *config) Repos(u *model.User) ([]*model.RepoLite, error) {
accounts = append(accounts, team.Login) accounts = append(accounts, team.Login)
} }
// for each account, get the list of repos
for _, account := range accounts { for _, account := range accounts {
repos, err := client.ListReposAll(account) repos, err := client.ListReposAll(account)
if err != nil { if err != nil {
@ -142,7 +141,6 @@ func (c *config) Repos(u *model.User) ([]*model.RepoLite, error) {
all = append(all, convertRepoLite(repo)) all = append(all, convertRepoLite(repo))
} }
} }
return all, nil return all, nil
} }
@ -168,8 +166,7 @@ func (c *config) Perm(u *model.User, owner, name string) (*model.Perm, error) {
return perms, nil return perms, nil
} }
// File fetches the file from the Bitbucket repository and returns its contents // File fetches the file from the Bitbucket repository and returns its contents.
// in string format.
func (c *config) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { func (c *config) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
config, err := c.newClient(u).FindSource(r.Owner, r.Name, b.Commit, f) config, err := c.newClient(u).FindSource(r.Owner, r.Name, b.Commit, f)
if err != nil { if err != nil {

View file

@ -1,23 +1,17 @@
package bitbucketserver package bitbucketserver
// Requires the following to be set // WARNING! This is an work-in-progress patch and does not yet conform to the coding,
// REMOTE_DRIVER=bitbucketserver // quality or security standards expected of this project. Please use with caution.
// REMOTE_CONFIG=https://{servername}?consumer_key={key added on the stash server for oath1}&git_username={username for clone}&git_password={password for clone}&consumer_rsa=/path/to/pem.file&open={not used yet}
// Configure application links in the bitbucket server --
// application url needs to be the base url to drone
// incoming auth needs to have the consumer key (same as the key in REMOTE_CONFIG)
// set the public key (public key from the private key added to /var/lib/bitbucketserver/private_key.pem name matters)
// consumer call back is the base url to drone plus /authorize/
// Needs a pem private key added to /var/lib/bitbucketserver/private_key.pem
// After that you should be good to go
import ( import (
"crypto/rsa"
"crypto/x509"
"encoding/json" "encoding/json"
"encoding/pem"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strconv"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/drone/drone/model" "github.com/drone/drone/model"
@ -25,132 +19,132 @@ import (
"github.com/mrjones/oauth" "github.com/mrjones/oauth"
) )
type BitbucketServer struct { // Opts defines configuration options.
type Opts struct {
URL string // Stash server url.
Username string // Git machine account username.
Password string // Git machine account password.
ConsumerKey string // Oauth1 consumer key.
ConsumerRSA string // Oauth1 consumer key file.
SkipVerify bool // Skip ssl verification.
}
// New returns a Remote implementation that integrates with Bitbucket Server,
// the on-premise edition of Bitbucket Cloud, formerly known as Stash.
func New(opts Opts) (remote.Remote, error) {
bb := &client{
URL: opts.URL,
ConsumerKey: opts.ConsumerKey,
ConsumerRSA: opts.ConsumerRSA,
GitUserName: opts.Username,
GitPassword: opts.Password,
}
switch {
case bb.GitUserName == "":
return nil, fmt.Errorf("Must have a git machine account username")
case bb.GitPassword == "":
return nil, fmt.Errorf("Must have a git machine account password")
case bb.ConsumerKey == "":
return nil, fmt.Errorf("Must have a oauth1 consumer key")
case bb.ConsumerRSA == "":
return nil, fmt.Errorf("Must have a oauth1 consumer key file")
}
keyfile, err := ioutil.ReadFile(bb.ConsumerRSA)
if err != nil {
return nil, err
}
block, _ := pem.Decode(keyfile)
bb.PrivateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
// TODO de-referencing is a bit weird and may not behave as expected, and could
// have race conditions. Instead store the parsed key (I already did this above)
// and then pass the parsed private key when creating the Bitbucket client.
bb.Consumer = *NewClient(bb.ConsumerRSA, bb.ConsumerKey, bb.URL)
return bb, nil
}
type client struct {
URL string URL string
ConsumerKey string ConsumerKey string
GitUserName string GitUserName string
GitPassword string GitPassword string
ConsumerRSA string ConsumerRSA string
Open bool PrivateKey *rsa.PrivateKey
Consumer oauth.Consumer Consumer oauth.Consumer
} }
func New(url, key, rsa, username, password string) remote.Remote { func (c *client) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) {
bb := &BitbucketServer{ requestToken, url, err := c.Consumer.GetRequestTokenAndUrl("oob")
URL: url,
ConsumerKey: key,
GitUserName: username,
GitPassword: password,
ConsumerRSA: rsa,
}
bb.Consumer = *NewClient(bb.ConsumerRSA, bb.ConsumerKey, bb.URL)
return bb
}
func Load(config string) *BitbucketServer {
url_, err := url.Parse(config)
if err != nil { if err != nil {
log.Fatalln("unable to parse remote dsn. %s", err) return nil, err
}
params := url_.Query()
url_.Path = ""
url_.RawQuery = ""
bitbucketserver := BitbucketServer{}
bitbucketserver.URL = url_.String()
bitbucketserver.GitUserName = params.Get("git_username")
if bitbucketserver.GitUserName == "" {
log.Fatalln("Must have a git_username")
}
bitbucketserver.GitPassword = params.Get("git_password")
if bitbucketserver.GitPassword == "" {
log.Fatalln("Must have a git_password")
}
bitbucketserver.ConsumerKey = params.Get("consumer_key")
if bitbucketserver.ConsumerKey == "" {
log.Fatalln("Must have a consumer_key")
}
bitbucketserver.ConsumerRSA = params.Get("consumer_rsa")
if bitbucketserver.ConsumerRSA == "" {
log.Fatalln("Must have a consumer_rsa")
}
bitbucketserver.Open, _ = strconv.ParseBool(params.Get("open"))
bitbucketserver.Consumer = *NewClient(bitbucketserver.ConsumerRSA, bitbucketserver.ConsumerKey, bitbucketserver.URL)
return &bitbucketserver
}
func (bs *BitbucketServer) Login(res http.ResponseWriter, req *http.Request) (*model.User, bool, error) {
log.Info("Starting to login for bitbucketServer")
log.Info("getting the requestToken")
requestToken, url, err := bs.Consumer.GetRequestTokenAndUrl("oob")
if err != nil {
log.Error(err)
} }
var code = req.FormValue("oauth_verifier") var code = req.FormValue("oauth_verifier")
if len(code) == 0 { if len(code) == 0 {
log.Info("redirecting to %s", url)
http.Redirect(res, req, url, http.StatusSeeOther) http.Redirect(res, req, url, http.StatusSeeOther)
return nil, false, nil return nil, nil
} }
var request_oauth_token = req.FormValue("oauth_token") requestToken.Token = req.FormValue("oauth_token")
requestToken.Token = request_oauth_token accessToken, err := c.Consumer.AuthorizeToken(requestToken, code)
accessToken, err := bs.Consumer.AuthorizeToken(requestToken, code)
if err != nil { if err != nil {
log.Error(err) return nil, err
} }
client, err := bs.Consumer.MakeHttpClient(accessToken) client, err := c.Consumer.MakeHttpClient(accessToken)
if err != nil { if err != nil {
log.Error(err) return nil, err
} }
response, err := client.Get(fmt.Sprintf("%s/plugins/servlet/applinks/whoami", bs.URL)) response, err := client.Get(fmt.Sprintf("%s/plugins/servlet/applinks/whoami", c.URL))
if err != nil { if err != nil {
log.Error(err) return nil, err
} }
defer response.Body.Close() defer response.Body.Close()
bits, err := ioutil.ReadAll(response.Body) bits, err := ioutil.ReadAll(response.Body)
userName := string(bits) if err != nil {
return nil, err
}
login := string(bits)
response1, err := client.Get(fmt.Sprintf("%s/rest/api/1.0/users/%s", bs.URL, userName)) // TODO errors should never be ignored like this
response1, err := client.Get(fmt.Sprintf("%s/rest/api/1.0/users/%s", c.URL, login))
contents, err := ioutil.ReadAll(response1.Body) contents, err := ioutil.ReadAll(response1.Body)
defer response1.Body.Close() defer response1.Body.Close()
var mUser User var mUser User // TODO prefixing with m* is not a common convention in Go
json.Unmarshal(contents, &mUser) json.Unmarshal(contents, &mUser) // TODO should not ignore error
user := model.User{} return &model.User{
user.Login = userName Login: login,
user.Email = mUser.EmailAddress Email: mUser.EmailAddress,
user.Token = accessToken.Token Token: accessToken.Token,
Avatar: avatarLink(mUser.EmailAddress),
user.Avatar = avatarLink(mUser.EmailAddress) }, nil
return &user, bs.Open, nil
} }
func (bs *BitbucketServer) Auth(token, secret string) (string, error) { // Auth is not supported by the Stash driver.
log.Info("Staring to auth for bitbucketServer. %s", token) func (*client) Auth(token, secret string) (string, error) {
if len(token) == 0 { return "", fmt.Errorf("Not Implemented")
return "", fmt.Errorf("Hasn't logged in yet")
}
return token, nil
} }
func (bs *BitbucketServer) Repo(u *model.User, owner, name string) (*model.Repo, error) { // Teams is not supported by the Stash driver.
log.Info("Staring repo for bitbucketServer with user " + u.Login + " " + owner + " " + name) func (*client) Teams(u *model.User) ([]*model.Team, error) {
var teams []*model.Team
return teams, nil
}
client := NewClientWithToken(&bs.Consumer, u.Token) func (c *client) Repo(u *model.User, owner, name string) (*model.Repo, error) {
client := NewClientWithToken(&c.Consumer, u.Token)
url := fmt.Sprintf("%s/rest/api/1.0/projects/%s/repos/%s", c.URL, owner, name)
url := fmt.Sprintf("%s/rest/api/1.0/projects/%s/repos/%s", bs.URL, owner, name)
log.Info("Trying to get " + url)
response, err := client.Get(url) response, err := client.Get(url)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
@ -160,40 +154,36 @@ func (bs *BitbucketServer) Repo(u *model.User, owner, name string) (*model.Repo,
bsRepo := BSRepo{} bsRepo := BSRepo{}
json.Unmarshal(contents, &bsRepo) json.Unmarshal(contents, &bsRepo)
cloneLink := "" repo := &model.Repo{
repoLink := "" Name: bsRepo.Slug,
Owner: bsRepo.Project.Key,
Branch: "master",
Kind: model.RepoGit,
IsPrivate: !bsRepo.Project.Public, // TODO(josmo) verify this is corrrect
FullName: fmt.Sprintf("%s/%s", bsRepo.Project.Key, bsRepo.Slug),
}
for _, item := range bsRepo.Links.Clone { for _, item := range bsRepo.Links.Clone {
if item.Name == "http" { if item.Name == "http" {
cloneLink = item.Href repo.Clone = item.Href
} }
} }
for _, item := range bsRepo.Links.Self { for _, item := range bsRepo.Links.Self {
if item.Href != "" { if item.Href != "" {
repoLink = item.Href repo.Link = item.Href
} }
} }
//TODO: get the real allow tag+ infomration
repo := &model.Repo{}
repo.Clone = cloneLink
repo.Link = repoLink
repo.Name = bsRepo.Slug
repo.Owner = bsRepo.Project.Key
repo.AllowPush = true
repo.FullName = fmt.Sprintf("%s/%s", bsRepo.Project.Key, bsRepo.Slug)
repo.Branch = "master"
repo.Kind = model.RepoGit
return repo, nil return repo, nil
} }
func (bs *BitbucketServer) Repos(u *model.User) ([]*model.RepoLite, error) { func (c *client) Repos(u *model.User) ([]*model.RepoLite, error) {
log.Info("Staring repos for bitbucketServer " + u.Login)
var repos = []*model.RepoLite{} var repos = []*model.RepoLite{}
client := NewClientWithToken(&bs.Consumer, u.Token) client := NewClientWithToken(&c.Consumer, u.Token)
response, err := client.Get(fmt.Sprintf("%s/rest/api/1.0/repos?limit=10000", bs.URL)) response, err := client.Get(fmt.Sprintf("%s/rest/api/1.0/repos?limit=10000", c.URL))
if err != nil { if err != nil {
log.Error(err) log.Error(err)
} }
@ -213,10 +203,8 @@ func (bs *BitbucketServer) Repos(u *model.User) ([]*model.RepoLite, error) {
return repos, nil return repos, nil
} }
func (bs *BitbucketServer) Perm(u *model.User, owner, repo string) (*model.Perm, error) { func (c *client) Perm(u *model.User, owner, repo string) (*model.Perm, error) {
// TODO need to fetch real permissions here
//TODO: find the real permissions
log.Info("Staring perm for bitbucketServer")
perms := new(model.Perm) perms := new(model.Perm)
perms.Pull = true perms.Pull = true
perms.Admin = true perms.Admin = true
@ -224,11 +212,11 @@ func (bs *BitbucketServer) Perm(u *model.User, owner, repo string) (*model.Perm,
return perms, nil return perms, nil
} }
func (bs *BitbucketServer) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { func (c *client) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
log.Info(fmt.Sprintf("Staring file for bitbucketServer login: %s repo: %s buildevent: %s string: %s", u.Login, r.Name, b.Event, f)) log.Info(fmt.Sprintf("Staring file for bitbucketServer login: %s repo: %s buildevent: %s string: %s", u.Login, r.Name, b.Event, f))
client := NewClientWithToken(&bs.Consumer, u.Token) client := NewClientWithToken(&c.Consumer, u.Token)
fileURL := fmt.Sprintf("%s/projects/%s/repos/%s/browse/%s?raw", bs.URL, r.Owner, r.Name, f) fileURL := fmt.Sprintf("%s/projects/%s/repos/%s/browse/%s?raw", c.URL, r.Owner, r.Name, f)
log.Info(fileURL) log.Info(fileURL)
response, err := client.Get(fileURL) response, err := client.Get(fileURL)
if err != nil { if err != nil {
@ -246,28 +234,26 @@ func (bs *BitbucketServer) File(u *model.User, r *model.Repo, b *model.Build, f
return responseBytes, nil return responseBytes, nil
} }
func (bs *BitbucketServer) Status(u *model.User, r *model.Repo, b *model.Build, link string) error { // Status is not supported by the Gogs driver.
log.Info("Staring status for bitbucketServer") func (*client) Status(*model.User, *model.Repo, *model.Build, string) error {
return nil return nil
} }
func (bs *BitbucketServer) Netrc(user *model.User, r *model.Repo) (*model.Netrc, error) { func (c *client) Netrc(user *model.User, r *model.Repo) (*model.Netrc, error) {
log.Info("Starting the Netrc lookup") u, err := url.Parse(c.URL) // TODO strip port from url
u, err := url.Parse(bs.URL)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &model.Netrc{ return &model.Netrc{
Machine: u.Host, Machine: u.Host,
Login: bs.GitUserName, Login: c.GitUserName,
Password: bs.GitPassword, Password: c.GitPassword,
}, nil }, nil
} }
func (bs *BitbucketServer) Activate(u *model.User, r *model.Repo, k *model.Key, link string) error { func (c *client) Activate(u *model.User, r *model.Repo, link string) error {
log.Info(fmt.Sprintf("Staring activate for bitbucketServer user: %s repo: %s key: %s link: %s", u.Login, r.Name, k, link)) client := NewClientWithToken(&c.Consumer, u.Token)
client := NewClientWithToken(&bs.Consumer, u.Token) hook, err := c.CreateHook(client, r.Owner, r.Name, "com.atlassian.stash.plugin.stash-web-post-receive-hooks-plugin:postReceiveHook", link)
hook, err := bs.CreateHook(client, r.Owner, r.Name, "com.atlassian.stash.plugin.stash-web-post-receive-hooks-plugin:postReceiveHook", link)
if err != nil { if err != nil {
return err return err
} }
@ -275,50 +261,38 @@ func (bs *BitbucketServer) Activate(u *model.User, r *model.Repo, k *model.Key,
return nil return nil
} }
func (bs *BitbucketServer) Deactivate(u *model.User, r *model.Repo, link string) error { func (c *client) Deactivate(u *model.User, r *model.Repo, link string) error {
log.Info(fmt.Sprintf("Staring deactivating for bitbucketServer user: %s repo: %s link: %s", u.Login, r.Name, link)) client := NewClientWithToken(&c.Consumer, u.Token)
client := NewClientWithToken(&bs.Consumer, u.Token) err := c.DeleteHook(client, r.Owner, r.Name, "com.atlassian.stash.plugin.stash-web-post-receive-hooks-plugin:postReceiveHook", link)
err := bs.DeleteHook(client, r.Owner, r.Name, "com.atlassian.stash.plugin.stash-web-post-receive-hooks-plugin:postReceiveHook", link)
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
func (bs *BitbucketServer) Hook(r *http.Request) (*model.Repo, *model.Build, error) { func (c *client) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
log.Info("Staring hook for bitbucketServer") hook := new(postHook)
defer r.Body.Close() if err := json.NewDecoder(r.Body).Decode(hook); err != nil {
contents, err := ioutil.ReadAll(r.Body) return nil, nil, err
if err != nil {
log.Info(err)
} }
var hookPost postHook build := &model.Build{
json.Unmarshal(contents, &hookPost) Event: model.EventPush,
Ref: hook.RefChanges[0].RefID, // TODO check for index Values
Author: hook.Changesets.Values[0].ToCommit.Author.EmailAddress, // TODO check for index Values
Commit: hook.RefChanges[0].ToHash, // TODO check for index value
Avatar: avatarLink(hook.Changesets.Values[0].ToCommit.Author.EmailAddress),
}
buildModel := &model.Build{} repo := &model.Repo{
buildModel.Event = model.EventPush Name: hook.Repository.Slug,
buildModel.Ref = hookPost.RefChanges[0].RefID Owner: hook.Repository.Project.Key,
buildModel.Author = hookPost.Changesets.Values[0].ToCommit.Author.EmailAddress FullName: fmt.Sprintf("%s/%s", hook.Repository.Project.Key, hook.Repository.Slug),
buildModel.Commit = hookPost.RefChanges[0].ToHash Branch: "master",
buildModel.Avatar = avatarLink(hookPost.Changesets.Values[0].ToCommit.Author.EmailAddress) Kind: model.RepoGit,
}
//All you really need is the name and owner. That's what creates the lookup key, so it needs to match the repo info. Just an FYI return repo, build, nil
repo := &model.Repo{}
repo.Name = hookPost.Repository.Slug
repo.Owner = hookPost.Repository.Project.Key
repo.AllowTag = false
repo.AllowDeploy = false
repo.AllowPull = false
repo.AllowPush = true
repo.FullName = fmt.Sprintf("%s/%s", hookPost.Repository.Project.Key, hookPost.Repository.Slug)
repo.Branch = "master"
repo.Kind = model.RepoGit
return repo, buildModel, nil
}
func (bs *BitbucketServer) String() string {
return "bitbucketserver"
} }
type HookDetail struct { type HookDetail struct {
@ -336,7 +310,7 @@ type Hook struct {
} }
// Enable hook for named repository // Enable hook for named repository
func (bs *BitbucketServer) CreateHook(client *http.Client, project, slug, hook_key, link string) (*Hook, error) { func (bs *client) CreateHook(client *http.Client, project, slug, hook_key, link string) (*Hook, error) {
// Set hook // Set hook
hookBytes := []byte(fmt.Sprintf(`{"hook-url-0":"%s"}`, link)) hookBytes := []byte(fmt.Sprintf(`{"hook-url-0":"%s"}`, link))
@ -351,7 +325,7 @@ func (bs *BitbucketServer) CreateHook(client *http.Client, project, slug, hook_k
} }
// Disable hook for named repository // Disable hook for named repository
func (bs *BitbucketServer) DeleteHook(client *http.Client, project, slug, hook_key, link string) error { func (bs *client) DeleteHook(client *http.Client, project, slug, hook_key, link string) error {
enablePath := fmt.Sprintf("/rest/api/1.0/projects/%s/repos/%s/settings/hooks/%s/enabled", enablePath := fmt.Sprintf("/rest/api/1.0/projects/%s/repos/%s/settings/hooks/%s/enabled",
project, slug, hook_key) project, slug, hook_key)
doDelete(client, bs.URL+enablePath) doDelete(client, bs.URL+enablePath)

View file

@ -272,7 +272,7 @@ func (g *Github) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
// Activate activates a repository by creating the post-commit hook and // Activate activates a repository by creating the post-commit hook and
// adding the SSH deploy key, if applicable. // adding the SSH deploy key, if applicable.
func (g *Github) Activate(u *model.User, r *model.Repo, k *model.Key, link string) error { func (g *Github) Activate(u *model.User, r *model.Repo, link string) error {
client := NewClient(g.API, u.Token, g.SkipVerify) client := NewClient(g.API, u.Token, g.SkipVerify)
_, err := CreateUpdateHook(client, r.Owner, r.Name, link) _, err := CreateUpdateHook(client, r.Owner, r.Name, link)
return err return err

View file

@ -4,6 +4,7 @@ import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net"
"net/http" "net/http"
"net/url" "net/url"
"strconv" "strconv"
@ -13,38 +14,59 @@ import (
"github.com/drone/drone/remote" "github.com/drone/drone/remote"
"github.com/drone/drone/shared/httputil" "github.com/drone/drone/shared/httputil"
"github.com/drone/drone/shared/oauth2" "github.com/drone/drone/shared/oauth2"
"github.com/drone/drone/shared/token"
"github.com/drone/drone/remote/gitlab/client" "github.com/drone/drone/remote/gitlab/client"
) )
const ( const DefaultScope = "api"
DefaultScope = "api"
) // Opts defines configuration options.
type Opts struct {
URL string // Gogs server url.
Client string // Oauth2 client id.
Secret string // Oauth2 client secret.
Username string // Optional machine account username.
Password string // Optional machine account password.
PrivateMode bool // Gogs is running in private mode.
SkipVerify bool // Skip ssl verification.
}
// New returns a Remote implementation that integrates with Gitlab, an open
// source Git service. See https://gitlab.com
func New(opts Opts) (remote.Remote, error) {
url, err := url.Parse(opts.URL)
if err != nil {
return nil, err
}
host, _, err := net.SplitHostPort(url.Host)
if err == nil {
url.Host = host
}
return &Gitlab{
URL: opts.URL,
Client: opts.Client,
Secret: opts.Secret,
Machine: url.Host,
Username: opts.Username,
Password: opts.Password,
PrivateMode: opts.PrivateMode,
SkipVerify: opts.SkipVerify,
}, nil
}
type Gitlab struct { type Gitlab struct {
URL string URL string
Client string Client string
Secret string Secret string
AllowedOrgs []string Machine string
CloneMode string Username string
Open bool Password string
PrivateMode bool PrivateMode bool
SkipVerify bool SkipVerify bool
HideArchives bool HideArchives bool
Search bool Search bool
} }
func New(url, client, secret string, private, skipverify bool) remote.Remote {
return &Gitlab{
URL: url,
Client: client,
Secret: secret,
PrivateMode: private,
SkipVerify: skipverify,
}
}
func Load(config string) *Gitlab { func Load(config string) *Gitlab {
url_, err := url.Parse(config) url_, err := url.Parse(config)
if err != nil { if err != nil {
@ -57,17 +79,17 @@ func Load(config string) *Gitlab {
gitlab.URL = url_.String() gitlab.URL = url_.String()
gitlab.Client = params.Get("client_id") gitlab.Client = params.Get("client_id")
gitlab.Secret = params.Get("client_secret") gitlab.Secret = params.Get("client_secret")
gitlab.AllowedOrgs = params["orgs"] // gitlab.AllowedOrgs = params["orgs"]
gitlab.SkipVerify, _ = strconv.ParseBool(params.Get("skip_verify")) gitlab.SkipVerify, _ = strconv.ParseBool(params.Get("skip_verify"))
gitlab.HideArchives, _ = strconv.ParseBool(params.Get("hide_archives")) gitlab.HideArchives, _ = strconv.ParseBool(params.Get("hide_archives"))
gitlab.Open, _ = strconv.ParseBool(params.Get("open")) // gitlab.Open, _ = strconv.ParseBool(params.Get("open"))
switch params.Get("clone_mode") { // switch params.Get("clone_mode") {
case "oauth": // case "oauth":
gitlab.CloneMode = "oauth" // gitlab.CloneMode = "oauth"
default: // default:
gitlab.CloneMode = "token" // gitlab.CloneMode = "token"
} // }
// this is a temp workaround // this is a temp workaround
gitlab.Search, _ = strconv.ParseBool(params.Get("search")) gitlab.Search, _ = strconv.ParseBool(params.Get("search"))
@ -77,7 +99,7 @@ func Load(config string) *Gitlab {
// Login authenticates the session and returns the // Login authenticates the session and returns the
// remote user details. // remote user details.
func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User, bool, error) { func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) {
var config = &oauth2.Config{ var config = &oauth2.Config{
ClientId: g.Client, ClientId: g.Client,
@ -97,41 +119,41 @@ func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User,
var code = req.FormValue("code") var code = req.FormValue("code")
if len(code) == 0 { if len(code) == 0 {
http.Redirect(res, req, config.AuthCodeURL("drone"), http.StatusSeeOther) http.Redirect(res, req, config.AuthCodeURL("drone"), http.StatusSeeOther)
return nil, false, nil return nil, nil
} }
var trans = &oauth2.Transport{Config: config, Transport: trans_} var trans = &oauth2.Transport{Config: config, Transport: trans_}
var token_, err = trans.Exchange(code) var token_, err = trans.Exchange(code)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("Error exchanging token. %s", err) return nil, fmt.Errorf("Error exchanging token. %s", err)
} }
client := NewClient(g.URL, token_.AccessToken, g.SkipVerify) client := NewClient(g.URL, token_.AccessToken, g.SkipVerify)
login, err := client.CurrentUser() login, err := client.CurrentUser()
if err != nil { if err != nil {
return nil, false, err return nil, err
} }
if len(g.AllowedOrgs) != 0 { // if len(g.AllowedOrgs) != 0 {
groups, err := client.AllGroups() // groups, err := client.AllGroups()
if err != nil { // if err != nil {
return nil, false, fmt.Errorf("Could not check org membership. %s", err) // return nil, fmt.Errorf("Could not check org membership. %s", err)
} // }
//
var member bool // var member bool
for _, group := range groups { // for _, group := range groups {
for _, allowedOrg := range g.AllowedOrgs { // for _, allowedOrg := range g.AllowedOrgs {
if group.Path == allowedOrg { // if group.Path == allowedOrg {
member = true // member = true
break // break
} // }
} // }
} // }
//
if !member { // if !member {
return nil, false, fmt.Errorf("User does not belong to correct group. Must belong to %v", g.AllowedOrgs) // return nil, false, fmt.Errorf("User does not belong to correct group. Must belong to %v", g.AllowedOrgs)
} // }
} // }
user := &model.User{} user := &model.User{}
user.Login = login.Username user.Login = login.Username
@ -145,7 +167,7 @@ func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User,
user.Avatar = g.URL + "/" + login.AvatarUrl user.Avatar = g.URL + "/" + login.AvatarUrl
} }
return user, g.Open, nil return user, nil
} }
func (g *Gitlab) Auth(token, secret string) (string, error) { func (g *Gitlab) Auth(token, secret string) (string, error) {
@ -157,6 +179,21 @@ func (g *Gitlab) Auth(token, secret string) (string, error) {
return login.Username, nil return login.Username, nil
} }
func (g *Gitlab) Teams(u *model.User) ([]*model.Team, error) {
client := NewClient(g.URL, u.Token, g.SkipVerify)
groups, err := client.AllGroups()
if err != nil {
return nil, err
}
var teams []*model.Team
for _, group := range groups {
teams = append(teams, &model.Team{
Login: group.Name,
})
}
return teams, nil
}
// Repo fetches the named repository from the remote system. // Repo fetches the named repository from the remote system.
func (g *Gitlab) Repo(u *model.User, owner, name string) (*model.Repo, error) { func (g *Gitlab) Repo(u *model.User, owner, name string) (*model.Repo, error) {
client := NewClient(g.URL, u.Token, g.SkipVerify) client := NewClient(g.URL, u.Token, g.SkipVerify)
@ -295,29 +332,47 @@ func (g *Gitlab) Status(u *model.User, repo *model.Repo, b *model.Build, link st
// Netrc returns a .netrc file that can be used to clone // Netrc returns a .netrc file that can be used to clone
// private repositories from a remote system. // private repositories from a remote system.
func (g *Gitlab) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { // func (g *Gitlab) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
url_, err := url.Parse(g.URL) // url_, err := url.Parse(g.URL)
if err != nil { // if err != nil {
return nil, err // return nil, err
} // }
netrc := &model.Netrc{} // netrc := &model.Netrc{}
netrc.Machine = url_.Host // netrc.Machine = url_.Host
//
// switch g.CloneMode {
// case "oauth":
// netrc.Login = "oauth2"
// netrc.Password = u.Token
// case "token":
// t := token.New(token.HookToken, r.FullName)
// netrc.Login = "drone-ci-token"
// netrc.Password, err = t.Sign(r.Hash)
// }
// return netrc, err
// }
switch g.CloneMode { // Netrc returns a netrc file capable of authenticating Gitlab requests and
case "oauth": // cloning Gitlab repositories. The netrc will use the global machine account
netrc.Login = "oauth2" // when configured.
netrc.Password = u.Token func (g *Gitlab) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
case "token": if g.Password != "" {
t := token.New(token.HookToken, r.FullName) return &model.Netrc{
netrc.Login = "drone-ci-token" Login: g.Username,
netrc.Password, err = t.Sign(r.Hash) Password: g.Password,
Machine: g.Machine,
}, nil
} }
return netrc, err return &model.Netrc{
Login: "oauth2",
Password: u.Token,
Machine: g.Machine,
}, nil
} }
// Activate activates a repository by adding a Post-commit hook and // Activate activates a repository by adding a Post-commit hook and
// a Public Deploy key, if applicable. // a Public Deploy key, if applicable.
func (g *Gitlab) Activate(user *model.User, repo *model.Repo, k *model.Key, link string) error { func (g *Gitlab) Activate(user *model.User, repo *model.Repo, link string) error {
var client = NewClient(g.URL, user.Token, g.SkipVerify) var client = NewClient(g.URL, user.Token, g.SkipVerify)
id, err := GetProjectId(g, client, repo.Owner, repo.Name) id, err := GetProjectId(g, client, repo.Owner, repo.Name)
if err != nil { if err != nil {

View file

@ -94,13 +94,13 @@ func Test_Gitlab(t *testing.T) {
// Test activate method // Test activate method
g.Describe("Activate", func() { g.Describe("Activate", func() {
g.It("Should be success", func() { g.It("Should be success", func() {
err := gitlab.Activate(&user, &repo, &model.Key{}, "http://example.com/api/hook/test/test?access_token=token") err := gitlab.Activate(&user, &repo, "http://example.com/api/hook/test/test?access_token=token")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
}) })
g.It("Should be failed, when token not given", func() { g.It("Should be failed, when token not given", func() {
err := gitlab.Activate(&user, &repo, &model.Key{}, "http://example.com/api/hook/test/test") err := gitlab.Activate(&user, &repo, "http://example.com/api/hook/test/test")
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })

View file

@ -0,0 +1,109 @@
package fixtures
import (
"net/http"
"github.com/gin-gonic/gin"
)
// Handler returns an http.Handler that is capable of handling a variety of mock
// Bitbucket requests and returning mock responses.
func Handler() http.Handler {
gin.SetMode(gin.TestMode)
e := gin.New()
e.GET("/api/v1/repos/:owner/:name", getRepo)
e.GET("/api/v1/repos/:owner/:name/raw/:commit/:file", getRepoFile)
e.POST("/api/v1/repos/:owner/:name/hooks", createRepoHook)
e.GET("/api/v1/user/repos", getUserRepos)
return e
}
func getRepo(c *gin.Context) {
switch c.Param("name") {
case "repo_not_found":
c.String(404, "")
default:
c.String(200, repoPayload)
}
}
func getRepoFile(c *gin.Context) {
switch c.Param("file") {
case "file_not_found":
c.String(404, "")
default:
c.String(200, repoFilePayload)
}
}
func createRepoHook(c *gin.Context) {
in := struct {
Type string `json:"type"`
Conf struct {
Type string `json:"content_type"`
URL string `json:"url"`
} `json:"config"`
}{}
c.BindJSON(&in)
if in.Type != "gogs" ||
in.Conf.Type != "json" ||
in.Conf.URL != "http://localhost" {
c.String(500, "")
return
}
c.String(200, "{}")
}
func getUserRepos(c *gin.Context) {
switch c.Request.Header.Get("Authorization") {
case "token repos_not_found":
c.String(404, "")
default:
c.String(200, userRepoPayload)
}
}
const repoPayload = `
{
"owner": {
"username": "test_name",
"email": "octocat@github.com",
"avatar_url": "https:\/\/secure.gravatar.com\/avatar\/8c58a0be77ee441bb8f8595b7f1b4e87"
},
"full_name": "test_name\/repo_name",
"private": true,
"html_url": "http:\/\/localhost\/test_name\/repo_name",
"clone_url": "http:\/\/localhost\/test_name\/repo_name.git",
"permissions": {
"admin": true,
"push": true,
"pull": true
}
}
`
const repoFilePayload = `{ platform: linux/amd64 }`
const userRepoPayload = `
[
{
"owner": {
"username": "test_name",
"email": "octocat@github.com",
"avatar_url": "https:\/\/secure.gravatar.com\/avatar\/8c58a0be77ee441bb8f8595b7f1b4e87"
},
"full_name": "test_name\/repo_name",
"private": true,
"html_url": "http:\/\/localhost\/test_name\/repo_name",
"clone_url": "http:\/\/localhost\/test_name\/repo_name.git",
"permissions": {
"admin": true,
"push": true,
"pull": true
}
}
]
`

View file

@ -1,6 +1,6 @@
package testdata package fixtures
var PushHook = ` var HookPush = `
{ {
"ref": "refs/heads/master", "ref": "refs/heads/master",
"before": "4b2626259b5a97b6b4eab5e6cca66adb986b672b", "before": "4b2626259b5a97b6b4eab5e6cca66adb986b672b",

View file

@ -12,172 +12,181 @@ import (
"github.com/gogits/go-gogs-client" "github.com/gogits/go-gogs-client"
) )
// Remote defines a remote implementation that integrates with Gogs, an open // Opts defines configuration options.
// source Git service written in Go. See https://gogs.io/ type Opts struct {
type Remote struct { URL string // Gogs server url.
Username string // Optional machine account username.
Password string // Optional machine account password.
PrivateMode bool // Gogs is running in private mode.
SkipVerify bool // Skip ssl verification.
}
type client struct {
URL string URL string
Open bool Machine string
Username string
Password string
PrivateMode bool PrivateMode bool
SkipVerify bool SkipVerify bool
} }
// New returns a Remote implementation that integrates with Gogs, an open // New returns a Remote implementation that integrates with Gogs, an open
// source Git service written in Go. See https://gogs.io/ // source Git service written in Go. See https://gogs.io/
func New(url string, private, skipverify bool) remote.Remote { func New(opts Opts) (remote.Remote, error) {
return &Remote{ url, err := url.Parse(opts.URL)
URL: url, if err != nil {
PrivateMode: private, return nil, err
SkipVerify: skipverify,
} }
host, _, err := net.SplitHostPort(url.Host)
if err == nil {
url.Host = host
}
return &client{
URL: opts.URL,
Machine: url.Host,
Username: opts.Username,
Password: opts.Password,
PrivateMode: opts.PrivateMode,
SkipVerify: opts.SkipVerify,
}, nil
} }
// Login authenticates the session and returns the authenticated user. // Login authenticates an account with Gogs using basic authenticaiton. The
func (g *Remote) Login(res http.ResponseWriter, req *http.Request) (*model.User, bool, error) { // Gogs account details are returned when the user is successfully authenticated.
func (c *client) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) {
var ( var (
username = req.FormValue("username") username = req.FormValue("username")
password = req.FormValue("password") password = req.FormValue("password")
) )
// if the username or password doesn't exist we re-direct // if the username or password is empty we re-direct to the login screen.
// the user to the login screen.
if len(username) == 0 || len(password) == 0 { if len(username) == 0 || len(password) == 0 {
http.Redirect(res, req, "/login/form", http.StatusSeeOther) http.Redirect(res, req, "/login/form", http.StatusSeeOther)
return nil, false, nil return nil, nil
} }
client := NewGogsClient(g.URL, "", g.SkipVerify) client := c.newClient()
// try to fetch drone token if it exists // try to fetch drone token if it exists
var accessToken string var accessToken string
tokens, err := client.ListAccessTokens(username, password) tokens, err := client.ListAccessTokens(username, password)
if err != nil { if err == nil {
return nil, false, err for _, token := range tokens {
} if token.Name == "drone" {
for _, token := range tokens { accessToken = token.Sha1
if token.Name == "drone" { break
accessToken = token.Sha1 }
break
} }
} }
// if drone token not found, create it // if drone token not found, create it
if accessToken == "" { if accessToken == "" {
token, err := client.CreateAccessToken(username, password, gogs.CreateAccessTokenOption{Name: "drone"}) token, terr := client.CreateAccessToken(
if err != nil { username,
return nil, false, err password,
gogs.CreateAccessTokenOption{Name: "drone"},
)
if terr != nil {
return nil, terr
} }
accessToken = token.Sha1 accessToken = token.Sha1
} }
client = NewGogsClient(g.URL, accessToken, g.SkipVerify) client = c.newClientToken(accessToken)
userInfo, err := client.GetUserInfo(username) account, err := client.GetUserInfo(username)
if err != nil {
return nil, false, err
}
user := model.User{}
user.Token = accessToken
user.Login = userInfo.UserName
user.Email = userInfo.Email
user.Avatar = expandAvatar(g.URL, userInfo.AvatarUrl)
return &user, false, nil
}
// Auth authenticates the session and returns the remote user
// login for the given token and secret
func (g *Remote) Auth(token, secret string) (string, error) {
return "", fmt.Errorf("Method not supported")
}
// Repo fetches the named repository from the remote system.
func (g *Remote) Repo(u *model.User, owner, name string) (*model.Repo, error) {
client := NewGogsClient(g.URL, u.Token, g.SkipVerify)
repos_, err := client.ListMyRepos()
if err != nil { if err != nil {
return nil, err return nil, err
} }
fullName := owner + "/" + name return &model.User{
for _, repo := range repos_ { Token: accessToken,
if repo.FullName == fullName { Login: account.UserName,
return toRepo(repo), nil Email: account.Email,
} Avatar: expandAvatar(c.URL, account.AvatarUrl),
} }, nil
return nil, fmt.Errorf("Not Found")
} }
// Repos fetches a list of repos from the remote system. // Auth is not supported by the Gogs driver.
func (g *Remote) Repos(u *model.User) ([]*model.RepoLite, error) { func (c *client) Auth(token, secret string) (string, error) {
return "", fmt.Errorf("Not Implemented")
}
// Teams is not supported by the Gogs driver.
func (c *client) Teams(u *model.User) ([]*model.Team, error) {
var empty []*model.Team
return empty, nil
}
// Repo returns the named Gogs repository.
func (c *client) Repo(u *model.User, owner, name string) (*model.Repo, error) {
client := c.newClientToken(u.Token)
repo, err := client.GetRepo(owner, name)
if err != nil {
return nil, err
}
return toRepo(repo), nil
}
// Repos returns a list of all repositories for the Gogs account, including
// organization repositories.
func (c *client) Repos(u *model.User) ([]*model.RepoLite, error) {
repos := []*model.RepoLite{} repos := []*model.RepoLite{}
client := NewGogsClient(g.URL, u.Token, g.SkipVerify) client := c.newClientToken(u.Token)
repos_, err := client.ListMyRepos() all, err := client.ListMyRepos()
if err != nil { if err != nil {
return repos, err return repos, err
} }
for _, repo := range repos_ { for _, repo := range all {
repos = append(repos, toRepoLite(repo)) repos = append(repos, toRepoLite(repo))
} }
return repos, err return repos, err
} }
// Perm fetches the named repository permissions from // Perm returns the user permissions for the named Gogs repository.
// the remote system for the specified user. func (c *client) Perm(u *model.User, owner, name string) (*model.Perm, error) {
func (g *Remote) Perm(u *model.User, owner, name string) (*model.Perm, error) { client := c.newClientToken(u.Token)
client := NewGogsClient(g.URL, u.Token, g.SkipVerify) repo, err := client.GetRepo(owner, name)
repos_, err := client.ListMyRepos()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return toPerm(repo.Permissions), nil
fullName := owner + "/" + name
for _, repo := range repos_ {
if repo.FullName == fullName {
return toPerm(repo.Permissions), nil
}
}
return nil, fmt.Errorf("Not Found")
} }
// File fetches a file from the remote repository and returns in string format. // File fetches the file from the Gogs repository and returns its contents.
func (g *Remote) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { func (c *client) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
client := NewGogsClient(g.URL, u.Token, g.SkipVerify) client := c.newClientToken(u.Token)
cfg, err := client.GetFile(r.Owner, r.Name, b.Commit, f) cfg, err := client.GetFile(r.Owner, r.Name, b.Commit, f)
return cfg, err return cfg, err
} }
// Status sends the commit status to the remote system. // Status is not supported by the Gogs driver.
// An example would be the GitHub pull request status. func (c *client) Status(u *model.User, r *model.Repo, b *model.Build, link string) error {
func (g *Remote) Status(u *model.User, r *model.Repo, b *model.Build, link string) error { return nil
return fmt.Errorf("Not Implemented")
} }
// Netrc returns a .netrc file that can be used to clone // Netrc returns a netrc file capable of authenticating Gogs requests and
// private repositories from a remote system. // cloning Gogs repositories. The netrc will use the global machine account
func (g *Remote) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { // when configured.
url_, err := url.Parse(g.URL) func (c *client) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
if err != nil { if c.Password != "" {
return nil, err return &model.Netrc{
} Login: c.Username,
host, _, err := net.SplitHostPort(url_.Host) Password: c.Password,
if err == nil { Machine: c.Machine,
url_.Host = host }, nil
} }
return &model.Netrc{ return &model.Netrc{
Login: u.Token, Login: u.Token,
Password: "x-oauth-basic", Password: "x-oauth-basic",
Machine: url_.Host, Machine: c.Machine,
}, nil }, nil
} }
// Activate activates a repository by creating the post-commit hook and // Activate activates the repository by registering post-commit hooks with
// adding the SSH deploy key, if applicable. // the Gogs repository.
func (g *Remote) Activate(u *model.User, r *model.Repo, k *model.Key, link string) error { func (c *client) Activate(u *model.User, r *model.Repo, link string) error {
config := map[string]string{ config := map[string]string{
"url": link, "url": link,
"secret": r.Hash, "secret": r.Hash,
@ -189,20 +198,19 @@ func (g *Remote) Activate(u *model.User, r *model.Repo, k *model.Key, link strin
Active: true, Active: true,
} }
client := NewGogsClient(g.URL, u.Token, g.SkipVerify) client := c.newClientToken(u.Token)
_, err := client.CreateRepoHook(r.Owner, r.Name, hook) _, err := client.CreateRepoHook(r.Owner, r.Name, hook)
return err return err
} }
// Deactivate removes a repository by removing all the post-commit hooks // Deactivate is not supported by the Gogs driver.
// which are equal to link and removing the SSH deploy key. func (c *client) Deactivate(u *model.User, r *model.Repo, link string) error {
func (g *Remote) Deactivate(u *model.User, r *model.Repo, link string) error { return nil
return fmt.Errorf("Not Implemented")
} }
// Hook parses the post-commit hook from the Request body // Hook parses the incoming Gogs hook and returns the Repository and Build
// and returns the required data in a standard format. // details. If the hook is unsupported nil values are returned.
func (g *Remote) Hook(r *http.Request) (*model.Repo, *model.Build, error) { func (c *client) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
var ( var (
err error err error
repo *model.Repo repo *model.Repo
@ -211,7 +219,7 @@ func (g *Remote) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
switch r.Header.Get("X-Gogs-Event") { switch r.Header.Get("X-Gogs-Event") {
case "push": case "push":
var push *PushHook var push *pushHook
push, err = parsePush(r.Body) push, err = parsePush(r.Body)
if err == nil { if err == nil {
repo = repoFromPush(push) repo = repoFromPush(push)
@ -221,20 +229,20 @@ func (g *Remote) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
return repo, build, err return repo, build, err
} }
// NewClient initializes and returns a API client. // helper function to return the Gogs client
func NewGogsClient(url, token string, skipVerify bool) *gogs.Client { func (c *client) newClient() *gogs.Client {
sslClient := &http.Client{} return c.newClientToken("")
c := gogs.NewClient(url, token) }
if skipVerify { // helper function to return the Gogs client
sslClient.Transport = &http.Transport{ func (c *client) newClientToken(token string) *gogs.Client {
client := gogs.NewClient(c.URL, token)
if c.SkipVerify {
httpClient := &http.Client{}
httpClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
} }
c.SetHTTPClient(sslClient) client.SetHTTPClient(httpClient)
} }
return c return client
}
func (g *Remote) String() string {
return "gogs"
} }

183
remote/gogs/gogs_test.go Normal file
View file

@ -0,0 +1,183 @@
package gogs
import (
"net/http/httptest"
"testing"
"github.com/drone/drone/model"
"github.com/drone/drone/remote/gogs/fixtures"
"github.com/franela/goblin"
"github.com/gin-gonic/gin"
)
func Test_gogs(t *testing.T) {
gin.SetMode(gin.TestMode)
s := httptest.NewServer(fixtures.Handler())
c, _ := New(Opts{
URL: s.URL,
SkipVerify: true,
})
g := goblin.Goblin(t)
g.Describe("Gogs", func() {
g.After(func() {
s.Close()
})
g.Describe("Creating a remote", func() {
g.It("Should return client with specified options", func() {
remote, _ := New(Opts{
URL: "http://localhost:8080",
Username: "someuser",
Password: "password",
SkipVerify: true,
PrivateMode: true,
})
g.Assert(remote.(*client).URL).Equal("http://localhost:8080")
g.Assert(remote.(*client).Machine).Equal("localhost")
g.Assert(remote.(*client).Username).Equal("someuser")
g.Assert(remote.(*client).Password).Equal("password")
g.Assert(remote.(*client).SkipVerify).Equal(true)
g.Assert(remote.(*client).PrivateMode).Equal(true)
})
g.It("Should handle malformed url", func() {
_, err := New(Opts{URL: "%gh&%ij"})
g.Assert(err != nil).IsTrue()
})
})
g.Describe("Generating a netrc file", func() {
g.It("Should return a netrc with the user token", func() {
remote, _ := New(Opts{
URL: "http://gogs.com",
})
netrc, _ := remote.Netrc(fakeUser, nil)
g.Assert(netrc.Machine).Equal("gogs.com")
g.Assert(netrc.Login).Equal(fakeUser.Token)
g.Assert(netrc.Password).Equal("x-oauth-basic")
})
g.It("Should return a netrc with the machine account", func() {
remote, _ := New(Opts{
URL: "http://gogs.com",
Username: "someuser",
Password: "password",
})
netrc, _ := remote.Netrc(nil, nil)
g.Assert(netrc.Machine).Equal("gogs.com")
g.Assert(netrc.Login).Equal("someuser")
g.Assert(netrc.Password).Equal("password")
})
})
g.Describe("Requesting a repository", func() {
g.It("Should return the repository details", func() {
repo, err := c.Repo(fakeUser, fakeRepo.Owner, fakeRepo.Name)
g.Assert(err == nil).IsTrue()
g.Assert(repo.Owner).Equal(fakeRepo.Owner)
g.Assert(repo.Name).Equal(fakeRepo.Name)
g.Assert(repo.FullName).Equal(fakeRepo.Owner + "/" + fakeRepo.Name)
g.Assert(repo.IsPrivate).IsTrue()
g.Assert(repo.Clone).Equal("http://localhost/test_name/repo_name.git")
g.Assert(repo.Link).Equal("http://localhost/test_name/repo_name")
})
g.It("Should handle a not found error", func() {
_, err := c.Repo(fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name)
g.Assert(err != nil).IsTrue()
})
})
g.Describe("Requesting repository permissions", func() {
g.It("Should return the permission details", func() {
perm, err := c.Perm(fakeUser, fakeRepo.Owner, fakeRepo.Name)
g.Assert(err == nil).IsTrue()
g.Assert(perm.Admin).IsTrue()
g.Assert(perm.Push).IsTrue()
g.Assert(perm.Pull).IsTrue()
})
g.It("Should handle a not found error", func() {
_, err := c.Perm(fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name)
g.Assert(err != nil).IsTrue()
})
})
g.Describe("Requesting a repository list", func() {
g.It("Should return the repository list", func() {
repos, err := c.Repos(fakeUser)
g.Assert(err == nil).IsTrue()
g.Assert(repos[0].Owner).Equal(fakeRepo.Owner)
g.Assert(repos[0].Name).Equal(fakeRepo.Name)
g.Assert(repos[0].FullName).Equal(fakeRepo.Owner + "/" + fakeRepo.Name)
})
g.It("Should handle a not found error", func() {
_, err := c.Repos(fakeUserNoRepos)
g.Assert(err != nil).IsTrue()
})
})
g.It("Should register repositroy hooks", func() {
err := c.Activate(fakeUser, fakeRepo, "http://localhost")
g.Assert(err == nil).IsTrue()
})
g.It("Should return a repository file", func() {
raw, err := c.File(fakeUser, fakeRepo, fakeBuild, ".drone.yml")
g.Assert(err == nil).IsTrue()
g.Assert(string(raw)).Equal("{ platform: linux/amd64 }")
})
g.Describe("Given an authentication request", func() {
g.It("Should redirect to login form")
g.It("Should create an access token")
g.It("Should handle an access token error")
g.It("Should return the authenticated user")
})
g.Describe("Given a repository hook", func() {
g.It("Should skip non-push events")
g.It("Should return push details")
g.It("Should handle a parsing error")
})
g.It("Should return no-op for usupporeted features", func() {
_, err1 := c.Auth("octocat", "4vyW6b49Z")
_, err2 := c.Teams(nil)
err3 := c.Status(nil, nil, nil, "")
err4 := c.Deactivate(nil, nil, "")
g.Assert(err1 != nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
g.Assert(err3 == nil).IsTrue()
g.Assert(err4 == nil).IsTrue()
})
})
}
var (
fakeUser = &model.User{
Login: "someuser",
Token: "cfcd2084",
}
fakeUserNoRepos = &model.User{
Login: "someuser",
Token: "repos_not_found",
}
fakeRepo = &model.Repo{
Owner: "test_name",
Name: "repo_name",
FullName: "test_name/repo_name",
}
fakeRepoNotFound = &model.Repo{
Owner: "test_name",
Name: "repo_not_found",
FullName: "test_name/repo_not_found",
}
fakeBuild = &model.Build{
Commit: "9ecad50",
}
)

View file

@ -12,8 +12,7 @@ import (
"github.com/gogits/go-gogs-client" "github.com/gogits/go-gogs-client"
) )
// helper function that converts a Gogs repository // helper function that converts a Gogs repository to a Drone repository.
// to a Drone repository.
func toRepoLite(from *gogs.Repository) *model.RepoLite { func toRepoLite(from *gogs.Repository) *model.RepoLite {
name := strings.Split(from.FullName, "/")[1] name := strings.Split(from.FullName, "/")[1]
avatar := expandAvatar( avatar := expandAvatar(
@ -28,8 +27,7 @@ func toRepoLite(from *gogs.Repository) *model.RepoLite {
} }
} }
// helper function that converts a Gogs repository // helper function that converts a Gogs repository to a Drone repository.
// to a Drone repository.
func toRepo(from *gogs.Repository) *model.Repo { func toRepo(from *gogs.Repository) *model.Repo {
name := strings.Split(from.FullName, "/")[1] name := strings.Split(from.FullName, "/")[1]
avatar := expandAvatar( avatar := expandAvatar(
@ -49,8 +47,7 @@ func toRepo(from *gogs.Repository) *model.Repo {
} }
} }
// helper function that converts a Gogs permission // helper function that converts a Gogs permission to a Drone permission.
// to a Drone permission.
func toPerm(from gogs.Permission) *model.Perm { func toPerm(from gogs.Permission) *model.Perm {
return &model.Perm{ return &model.Perm{
Pull: from.Pull, Pull: from.Pull,
@ -59,11 +56,10 @@ func toPerm(from gogs.Permission) *model.Perm {
} }
} }
// helper function that extracts the Build data // helper function that extracts the Build data from a Gogs push hook
// from a Gogs push hook func buildFromPush(hook *pushHook) *model.Build {
func buildFromPush(hook *PushHook) *model.Build {
avatar := expandAvatar( avatar := expandAvatar(
hook.Repo.Url, hook.Repo.URL,
fixMalformedAvatar(hook.Sender.Avatar), fixMalformedAvatar(hook.Sender.Avatar),
) )
return &model.Build{ return &model.Build{
@ -79,9 +75,8 @@ func buildFromPush(hook *PushHook) *model.Build {
} }
} }
// helper function that extracts the Repository data // helper function that extracts the Repository data from a Gogs push hook
// from a Gogs push hook func repoFromPush(hook *pushHook) *model.Repo {
func repoFromPush(hook *PushHook) *model.Repo {
fullName := fmt.Sprintf( fullName := fmt.Sprintf(
"%s/%s", "%s/%s",
hook.Repo.Owner.Username, hook.Repo.Owner.Username,
@ -91,20 +86,19 @@ func repoFromPush(hook *PushHook) *model.Repo {
Name: hook.Repo.Name, Name: hook.Repo.Name,
Owner: hook.Repo.Owner.Username, Owner: hook.Repo.Owner.Username,
FullName: fullName, FullName: fullName,
Link: hook.Repo.Url, Link: hook.Repo.URL,
} }
} }
// helper function that parses a push hook from // helper function that parses a push hook from a read closer.
// a read closer. func parsePush(r io.Reader) (*pushHook, error) {
func parsePush(r io.Reader) (*PushHook, error) { push := new(pushHook)
push := new(PushHook)
err := json.NewDecoder(r).Decode(push) err := json.NewDecoder(r).Decode(push)
return push, err return push, err
} }
// fixMalformedAvatar is a helper function that fixes // fixMalformedAvatar is a helper function that fixes an avatar url if malformed
// an avatar url if malformed (known bug with gogs) // (currently a known bug with gogs)
func fixMalformedAvatar(url string) string { func fixMalformedAvatar(url string) string {
index := strings.Index(url, "///") index := strings.Index(url, "///")
if index != -1 { if index != -1 {
@ -117,16 +111,16 @@ func fixMalformedAvatar(url string) string {
return url return url
} }
// expandAvatar is a helper function that converts // expandAvatar is a helper function that converts a relative avatar URL to the
// a relative avatar URL to the abosolute url. // abosolute url.
func expandAvatar(repo, rawurl string) string { func expandAvatar(repo, rawurl string) string {
if !strings.HasPrefix(rawurl, "/avatars/") { if !strings.HasPrefix(rawurl, "/avatars/") {
return rawurl return rawurl
} }
url_, err := url.Parse(repo) url, err := url.Parse(repo)
if err != nil { if err != nil {
return rawurl return rawurl
} }
url_.Path = rawurl url.Path = rawurl
return url_.String() return url.String()
} }

View file

@ -5,7 +5,7 @@ import (
"testing" "testing"
"github.com/drone/drone/model" "github.com/drone/drone/model"
"github.com/drone/drone/remote/gogs/testdata" "github.com/drone/drone/remote/gogs/fixtures"
"github.com/franela/goblin" "github.com/franela/goblin"
"github.com/gogits/go-gogs-client" "github.com/gogits/go-gogs-client"
@ -17,7 +17,7 @@ func Test_parse(t *testing.T) {
g.Describe("Gogs", func() { g.Describe("Gogs", func() {
g.It("Should parse push hook payload", func() { g.It("Should parse push hook payload", func() {
buf := bytes.NewBufferString(testdata.PushHook) buf := bytes.NewBufferString(fixtures.HookPush)
hook, err := parsePush(buf) hook, err := parsePush(buf)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(hook.Ref).Equal("refs/heads/master") g.Assert(hook.Ref).Equal("refs/heads/master")
@ -25,7 +25,7 @@ func Test_parse(t *testing.T) {
g.Assert(hook.Before).Equal("4b2626259b5a97b6b4eab5e6cca66adb986b672b") g.Assert(hook.Before).Equal("4b2626259b5a97b6b4eab5e6cca66adb986b672b")
g.Assert(hook.Compare).Equal("http://gogs.golang.org/gordon/hello-world/compare/4b2626259b5a97b6b4eab5e6cca66adb986b672b...ef98532add3b2feb7a137426bba1248724367df5") g.Assert(hook.Compare).Equal("http://gogs.golang.org/gordon/hello-world/compare/4b2626259b5a97b6b4eab5e6cca66adb986b672b...ef98532add3b2feb7a137426bba1248724367df5")
g.Assert(hook.Repo.Name).Equal("hello-world") g.Assert(hook.Repo.Name).Equal("hello-world")
g.Assert(hook.Repo.Url).Equal("http://gogs.golang.org/gordon/hello-world") g.Assert(hook.Repo.URL).Equal("http://gogs.golang.org/gordon/hello-world")
g.Assert(hook.Repo.Owner.Name).Equal("gordon") g.Assert(hook.Repo.Owner.Name).Equal("gordon")
g.Assert(hook.Repo.Owner.Email).Equal("gordon@golang.org") g.Assert(hook.Repo.Owner.Email).Equal("gordon@golang.org")
g.Assert(hook.Repo.Owner.Username).Equal("gordon") g.Assert(hook.Repo.Owner.Username).Equal("gordon")
@ -38,7 +38,7 @@ func Test_parse(t *testing.T) {
}) })
g.It("Should return a Build struct from a push hook", func() { g.It("Should return a Build struct from a push hook", func() {
buf := bytes.NewBufferString(testdata.PushHook) buf := bytes.NewBufferString(fixtures.HookPush)
hook, _ := parsePush(buf) hook, _ := parsePush(buf)
build := buildFromPush(hook) build := buildFromPush(hook)
g.Assert(build.Event).Equal(model.EventPush) g.Assert(build.Event).Equal(model.EventPush)
@ -53,13 +53,13 @@ func Test_parse(t *testing.T) {
}) })
g.It("Should return a Repo struct from a push hook", func() { g.It("Should return a Repo struct from a push hook", func() {
buf := bytes.NewBufferString(testdata.PushHook) buf := bytes.NewBufferString(fixtures.HookPush)
hook, _ := parsePush(buf) hook, _ := parsePush(buf)
repo := repoFromPush(hook) repo := repoFromPush(hook)
g.Assert(repo.Name).Equal(hook.Repo.Name) g.Assert(repo.Name).Equal(hook.Repo.Name)
g.Assert(repo.Owner).Equal(hook.Repo.Owner.Username) g.Assert(repo.Owner).Equal(hook.Repo.Owner.Username)
g.Assert(repo.FullName).Equal("gordon/hello-world") g.Assert(repo.FullName).Equal("gordon/hello-world")
g.Assert(repo.Link).Equal(hook.Repo.Url) g.Assert(repo.Link).Equal(hook.Repo.URL)
}) })
g.It("Should return a Perm struct from a Gogs Perm", func() { g.It("Should return a Perm struct from a Gogs Perm", func() {

View file

@ -1,6 +1,6 @@
package gogs package gogs
type PushHook struct { type pushHook struct {
Ref string `json:"ref"` Ref string `json:"ref"`
Before string `json:"before"` Before string `json:"before"`
After string `json:"after"` After string `json:"after"`
@ -15,7 +15,7 @@ type PushHook struct {
Repo struct { Repo struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Url string `json:"url"` URL string `json:"url"`
Private bool `json:"private"` Private bool `json:"private"`
Owner struct { Owner struct {
Name string `json:"name"` Name string `json:"name"`
@ -27,7 +27,7 @@ type PushHook struct {
Commits []struct { Commits []struct {
ID string `json:"id"` ID string `json:"id"`
Message string `json:"message"` Message string `json:"message"`
Url string `json:"url"` URL string `json:"url"`
} `json:"commits"` } `json:"commits"`
Sender struct { Sender struct {

View file

@ -134,8 +134,6 @@ func (s *Server) Handler() http.Handler {
repo.Use(session.MustPull) repo.Use(session.MustPull)
repo.GET("", api.GetRepo) repo.GET("", api.GetRepo)
repo.GET("/key", api.GetRepoKey)
repo.POST("/key", api.PostRepoKey)
repo.GET("/builds", api.GetBuilds) repo.GET("/builds", api.GetBuilds)
repo.GET("/builds/:number", api.GetBuild) repo.GET("/builds/:number", api.GetBuild)
repo.GET("/logs/:number/:job", api.GetBuildLogs) repo.GET("/logs/:number/:job", api.GetBuildLogs)

View file

@ -1,31 +0,0 @@
package datastore
import (
"github.com/drone/drone/model"
"github.com/russross/meddler"
)
func (db *datastore) GetKey(repo *model.Repo) (*model.Key, error) {
var key = new(model.Key)
var err = meddler.QueryRow(db, key, rebind(keyQuery), repo.ID)
return key, err
}
func (db *datastore) CreateKey(key *model.Key) error {
return meddler.Save(db, keyTable, key)
}
func (db *datastore) UpdateKey(key *model.Key) error {
return meddler.Save(db, keyTable, key)
}
func (db *datastore) DeleteKey(key *model.Key) error {
var _, err = db.Exec(rebind(keyDeleteStmt), key.ID)
return err
}
const keyTable = "keys"
const keyQuery = "SELECT * FROM `keys` WHERE key_repo_id=? LIMIT 1"
const keyDeleteStmt = "DELETE FROM `keys` WHERE key_id=?"

View file

@ -1,114 +0,0 @@
package datastore
import (
"testing"
"github.com/drone/drone/model"
"github.com/franela/goblin"
)
func TestKeys(t *testing.T) {
db := openTest()
defer db.Close()
s := From(db)
g := goblin.Goblin(t)
g.Describe("Keys", func() {
// before each test be sure to purge the package
// table data from the database.
g.BeforeEach(func() {
db.Exec(rebind("DELETE FROM `keys`"))
})
g.It("Should create a key", func() {
key := model.Key{
RepoID: 1,
Public: fakePublicKey,
Private: fakePrivateKey,
}
err := s.CreateKey(&key)
g.Assert(err == nil).IsTrue()
g.Assert(key.ID != 0).IsTrue()
})
g.It("Should update a key", func() {
key := model.Key{
RepoID: 1,
Public: fakePublicKey,
Private: fakePrivateKey,
}
err := s.CreateKey(&key)
g.Assert(err == nil).IsTrue()
g.Assert(key.ID != 0).IsTrue()
key.Private = ""
key.Public = ""
err1 := s.UpdateKey(&key)
getkey, err2 := s.GetKey(&model.Repo{ID: 1})
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
g.Assert(key.ID).Equal(getkey.ID)
g.Assert(key.Public).Equal(getkey.Public)
g.Assert(key.Private).Equal(getkey.Private)
})
g.It("Should get a key", func() {
key := model.Key{
RepoID: 1,
Public: fakePublicKey,
Private: fakePrivateKey,
}
err := s.CreateKey(&key)
g.Assert(err == nil).IsTrue()
g.Assert(key.ID != 0).IsTrue()
getkey, err := s.GetKey(&model.Repo{ID: 1})
g.Assert(err == nil).IsTrue()
g.Assert(key.ID).Equal(getkey.ID)
g.Assert(key.Public).Equal(getkey.Public)
g.Assert(key.Private).Equal(getkey.Private)
})
g.It("Should delete a key", func() {
key := model.Key{
RepoID: 1,
Public: fakePublicKey,
Private: fakePrivateKey,
}
err1 := s.CreateKey(&key)
err2 := s.DeleteKey(&key)
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
_, err := s.GetKey(&model.Repo{ID: 1})
g.Assert(err == nil).IsFalse()
})
})
}
var fakePublicKey = `
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0
FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/
3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQAB
-----END PUBLIC KEY-----
`
var fakePrivateKey = `
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh
3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2
pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX
GukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il
AkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF
L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k
X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----
`

View file

@ -54,18 +54,6 @@ type Store interface {
// DeleteRepo deletes a user repository. // DeleteRepo deletes a user repository.
DeleteRepo(*model.Repo) error DeleteRepo(*model.Repo) error
// GetKey gets a key by unique repository ID.
GetKey(*model.Repo) (*model.Key, error)
// CreateKey creates a new key.
CreateKey(*model.Key) error
// UpdateKey updates a user key.
UpdateKey(*model.Key) error
// DeleteKey deletes a user key.
DeleteKey(*model.Key) error
// GetSecretList gets a list of repository secrets // GetSecretList gets a list of repository secrets
GetSecretList(*model.Repo) ([]*model.Secret, error) GetSecretList(*model.Repo) ([]*model.Secret, error)
@ -192,22 +180,6 @@ func DeleteRepo(c context.Context, repo *model.Repo) error {
return FromContext(c).DeleteRepo(repo) return FromContext(c).DeleteRepo(repo)
} }
func GetKey(c context.Context, repo *model.Repo) (*model.Key, error) {
return FromContext(c).GetKey(repo)
}
func CreateKey(c context.Context, key *model.Key) error {
return FromContext(c).CreateKey(key)
}
func UpdateKey(c context.Context, key *model.Key) error {
return FromContext(c).UpdateKey(key)
}
func DeleteKey(c context.Context, key *model.Key) error {
return FromContext(c).DeleteKey(key)
}
func GetSecretList(c context.Context, r *model.Repo) ([]*model.Secret, error) { func GetSecretList(c context.Context, r *model.Repo) ([]*model.Secret, error) {
return FromContext(c).GetSecretList(r) return FromContext(c).GetSecretList(r)
} }

View file

@ -65,10 +65,6 @@ block content
else else
input#trusted[type="checkbox"][hidden="hidden"] input#trusted[type="checkbox"][hidden="hidden"]
label.switch[for="trusted"] label.switch[for="trusted"]
div.row
div.col-md-3 Public Key
div.col-md-9
pre #{Key.Public} #{Repo.Owner}-#{Repo.Name}@drone
div.row div.row
div.col-md-12 div.col-md-12
div.alert.alert-danger div.alert.alert-danger
@ -78,4 +74,4 @@ block content
block append scripts block append scripts
script script
var view = new RepoConfigViewModel(#{Repo.FullName}); var view = new RepoConfigViewModel(#{Repo.FullName});

View file

@ -23,7 +23,7 @@ func GetLogin(c *gin.Context) {
// remember why, so need to revisit this line. // remember why, so need to revisit this line.
c.Writer.Header().Del("Content-Type") c.Writer.Header().Del("Content-Type")
tmpuser, open, err := remote.Login(c.Writer, c.Request) tmpuser, err := remote.Login(c.Writer, c.Request)
if err != nil { if err != nil {
log.Errorf("cannot authenticate user. %s", err) log.Errorf("cannot authenticate user. %s", err)
c.Redirect(303, "/login?error=oauth_error") c.Redirect(303, "/login?error=oauth_error")
@ -35,20 +35,16 @@ func GetLogin(c *gin.Context) {
return return
} }
var open = false // TODO get this from context
// get the user from the database // get the user from the database
u, err := store.GetUserLogin(c, tmpuser.Login) u, err := store.GetUserLogin(c, tmpuser.Login)
if err != nil { if err != nil {
count, err := store.GetUserCount(c)
if err != nil {
log.Errorf("cannot register %s. %s", tmpuser.Login, err)
c.Redirect(303, "/login?error=internal_error")
return
}
// if self-registration is disabled we should // if self-registration is disabled we should
// return a notAuthorized error. the only exception // return a notAuthorized error. the only exception
// is if no users exist yet in the system we'll proceed. // is if no users exist yet in the system we'll proceed.
if !open && count != 0 { if !open {
log.Errorf("cannot register %s. registration closed", tmpuser.Login) log.Errorf("cannot register %s. registration closed", tmpuser.Login)
c.Redirect(303, "/login?error=access_denied") c.Redirect(303, "/login?error=access_denied")
return return
@ -69,12 +65,6 @@ func GetLogin(c *gin.Context) {
c.Redirect(303, "/login?error=internal_error") c.Redirect(303, "/login?error=internal_error")
return return
} }
// if this is the first user, they
// should be an admin.
if count == 0 {
u.Admin = true
}
} }
// update the user meta data and authorization // update the user meta data and authorization

View file

@ -116,7 +116,6 @@ func ShowRepoConf(c *gin.Context) {
user := session.User(c) user := session.User(c)
repo := session.Repo(c) repo := session.Repo(c)
key, _ := store.GetKey(c, repo)
token, _ := token.New( token, _ := token.New(
token.CsrfToken, token.CsrfToken,
@ -126,7 +125,6 @@ func ShowRepoConf(c *gin.Context) {
c.HTML(200, "repo_config.html", gin.H{ c.HTML(200, "repo_config.html", gin.H{
"User": user, "User": user,
"Repo": repo, "Repo": repo,
"Key": key,
"Csrf": token, "Csrf": token,
"Link": httputil.GetURL(c.Request), "Link": httputil.GetURL(c.Request),
}) })