From ebd547deacce9de774d609261d4049c99883945c Mon Sep 17 00:00:00 2001 From: Brad Rydzewski Date: Sun, 1 May 2016 16:30:00 -0700 Subject: [PATCH] refactoring remotes to remove deprecated variables, adding tests --- api/repo.go | 59 +--- drone/daemon.go | 107 +++--- remote/bitbucket/bitbucket.go | 5 +- remote/bitbucketserver/bitbucketserver.go | 320 ++++++++---------- remote/github/github.go | 2 +- remote/gitlab/gitlab.go | 189 +++++++---- remote/gitlab/gitlab_test.go | 4 +- remote/gogs/fixtures/handler.go | 109 ++++++ .../hook_push.go => fixtures/hooks.go} | 4 +- remote/gogs/gogs.go | 248 +++++++------- remote/gogs/gogs_test.go | 183 ++++++++++ remote/gogs/helper.go | 44 ++- remote/gogs/helper_test.go | 12 +- remote/gogs/types.go | 6 +- server/server.go | 2 - store/datastore/keys.go | 31 -- store/datastore/keys_test.go | 114 ------- store/store.go | 28 -- template/amber/repo_config.amber | 6 +- web/login.go | 18 +- web/pages.go | 2 - 21 files changed, 797 insertions(+), 696 deletions(-) create mode 100644 remote/gogs/fixtures/handler.go rename remote/gogs/{testdata/hook_push.go => fixtures/hooks.go} (97%) create mode 100644 remote/gogs/gogs_test.go delete mode 100644 store/datastore/keys.go delete mode 100644 store/datastore/keys_test.go diff --git a/api/repo.go b/api/repo.go index c7ed5e2fd..5b631973b 100644 --- a/api/repo.go +++ b/api/repo.go @@ -2,13 +2,11 @@ package api import ( "fmt" - "io/ioutil" "net/http" "github.com/gin-gonic/gin" "github.com/drone/drone/cache" - "github.com/drone/drone/model" "github.com/drone/drone/remote" "github.com/drone/drone/router/middleware/session" "github.com/drone/drone/shared/crypto" @@ -72,19 +70,9 @@ func PostRepo(c *gin.Context) { 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 // local changes to the database. - err = remote.Activate(user, r, keys, link) + err = remote.Activate(user, r, link) if err != nil { c.String(500, err.Error()) return @@ -96,12 +84,6 @@ func PostRepo(c *gin.Context) { c.String(500, err.Error()) return } - keys.RepoID = r.ID - err = store.CreateKey(c, keys) - if err != nil { - c.String(500, err.Error()) - return - } c.JSON(200, r) } @@ -155,45 +137,6 @@ func GetRepo(c *gin.Context) { 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) { remote := remote.FromContext(c) repo := session.Repo(c) diff --git a/drone/daemon.go b/drone/daemon.go index cc85e6d77..f2cdf3dd3 100644 --- a/drone/daemon.go +++ b/drone/daemon.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "log" "net/http" "os" "time" @@ -159,6 +158,16 @@ var DaemonCmd = cli.Command{ Usage: "gogs server address", 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{ EnvVar: "DRONE_GOGS_PRIVATE_MODE", Name: "gogs-private-mode", @@ -205,6 +214,16 @@ var DaemonCmd = cli.Command{ Name: "gitlab-sercret", 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{ EnvVar: "DRONE_GITLAB_SKIP_VERIFY", Name: "gitlab-skip-verify", @@ -245,7 +264,11 @@ var DaemonCmd = cli.Command{ Name: "stash-git-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 // @@ -347,60 +370,70 @@ func setupStore(c *cli.Context) store.Store { } func setupRemote(c *cli.Context) remote.Remote { + var remote remote.Remote + var err error switch { case c.Bool("github"): - return setupGithub(c) + remote, err = setupGithub(c) case c.Bool("gitlab"): - return setupGitlab(c) + remote, err = setupGitlab(c) case c.Bool("bitbucket"): - return setupBitbucket(c) + remote, err = setupBitbucket(c) case c.Bool("stash"): - return setupStash(c) + remote, err = setupStash(c) case c.Bool("gogs"): - return setupGogs(c) + remote, err = setupGogs(c) default: - logrus.Fatalln("version control system not configured") - return nil + err = fmt.Errorf("version control system not configured") } + 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( c.String("bitbucket-client"), c.String("bitbucket-server"), - ) + ), nil } -func setupGogs(c *cli.Context) remote.Remote { - return gogs.New( - c.String("gogs-server"), - c.Bool("gogs-private-mode"), - c.Bool("gogs-skip-verify"), - ) +func setupGogs(c *cli.Context) (remote.Remote, error) { + return gogs.New(gogs.Opts{ + URL: c.String("gogs-server"), + Username: c.String("gogs-git-username"), + 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 { - return bitbucketserver.New( - c.String("stash-server"), - c.String("stash-consumer-key"), - c.String("stash-consumer-rsa"), - c.String("stash-git-username"), - c.String("stash-git-password"), - ) +func setupStash(c *cli.Context) (remote.Remote, error) { + return bitbucketserver.New(bitbucketserver.Opts{ + URL: c.String("stash-server"), + Username: c.String("stash-git-username"), + Password: c.String("stash-git-password"), + ConsumerKey: c.String("stash-consumer-key"), + ConsumerRSA: c.String("stash-consumer-rsa"), + SkipVerify: c.Bool("stash-skip-verify"), + }) } -func setupGitlab(c *cli.Context) remote.Remote { - return gitlab.New( - c.String("gitlab-server"), - c.String("gitlab-client"), - c.String("gitlab-sercret"), - c.Bool("gitlab-private-mode"), - c.Bool("gitlab-skip-verify"), - ) +func setupGitlab(c *cli.Context) (remote.Remote, error) { + return gitlab.New(gitlab.Opts{ + URL: c.String("gitlab-server"), + Client: c.String("gitlab-client"), + Secret: c.String("gitlab-sercret"), + Username: c.String("gitlab-git-username"), + 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 { - g, err := github.New( +func setupGithub(c *cli.Context) (remote.Remote, error) { + return github.New( c.String("github-server"), c.String("github-client"), c.String("github-sercret"), @@ -409,10 +442,6 @@ func setupGithub(c *cli.Context) remote.Remote { c.Bool("github-skip-verify"), c.BoolT("github-merge-ref"), ) - if err != nil { - log.Fatalln(err) - } - return g } func printSecret(c *cli.Context) error { diff --git a/remote/bitbucket/bitbucket.go b/remote/bitbucket/bitbucket.go index c7acc38ed..b3e51a97f 100644 --- a/remote/bitbucket/bitbucket.go +++ b/remote/bitbucket/bitbucket.go @@ -132,7 +132,6 @@ func (c *config) Repos(u *model.User) ([]*model.RepoLite, error) { accounts = append(accounts, team.Login) } - // for each account, get the list of repos for _, account := range accounts { repos, err := client.ListReposAll(account) if err != nil { @@ -142,7 +141,6 @@ func (c *config) Repos(u *model.User) ([]*model.RepoLite, error) { all = append(all, convertRepoLite(repo)) } } - return all, nil } @@ -168,8 +166,7 @@ func (c *config) Perm(u *model.User, owner, name string) (*model.Perm, error) { return perms, nil } -// File fetches the file from the Bitbucket repository and returns its contents -// in string format. +// File fetches the file from the Bitbucket repository and returns its contents. 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) if err != nil { diff --git a/remote/bitbucketserver/bitbucketserver.go b/remote/bitbucketserver/bitbucketserver.go index c40a4c7bf..6b73ad136 100644 --- a/remote/bitbucketserver/bitbucketserver.go +++ b/remote/bitbucketserver/bitbucketserver.go @@ -1,23 +1,17 @@ package bitbucketserver -// Requires the following to be set -// REMOTE_DRIVER=bitbucketserver -// 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 +// WARNING! This is an work-in-progress patch and does not yet conform to the coding, +// quality or security standards expected of this project. Please use with caution. import ( + "crypto/rsa" + "crypto/x509" "encoding/json" + "encoding/pem" "fmt" "io/ioutil" "net/http" "net/url" - "strconv" log "github.com/Sirupsen/logrus" "github.com/drone/drone/model" @@ -25,132 +19,132 @@ import ( "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 ConsumerKey string GitUserName string GitPassword string ConsumerRSA string - Open bool + PrivateKey *rsa.PrivateKey Consumer oauth.Consumer } -func New(url, key, rsa, username, password string) remote.Remote { - bb := &BitbucketServer{ - 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) +func (c *client) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) { + requestToken, url, err := c.Consumer.GetRequestTokenAndUrl("oob") if err != nil { - log.Fatalln("unable to parse remote dsn. %s", 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) + return nil, err } var code = req.FormValue("oauth_verifier") if len(code) == 0 { - log.Info("redirecting to %s", url) http.Redirect(res, req, url, http.StatusSeeOther) - return nil, false, nil + return nil, nil } - var request_oauth_token = req.FormValue("oauth_token") - requestToken.Token = request_oauth_token - accessToken, err := bs.Consumer.AuthorizeToken(requestToken, code) + requestToken.Token = req.FormValue("oauth_token") + accessToken, err := c.Consumer.AuthorizeToken(requestToken, code) if err != nil { - log.Error(err) + return nil, err } - client, err := bs.Consumer.MakeHttpClient(accessToken) + client, err := c.Consumer.MakeHttpClient(accessToken) 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 { - log.Error(err) + return nil, err } defer response.Body.Close() 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) defer response1.Body.Close() - var mUser User - json.Unmarshal(contents, &mUser) + var mUser User // TODO prefixing with m* is not a common convention in Go + json.Unmarshal(contents, &mUser) // TODO should not ignore error - user := model.User{} - user.Login = userName - user.Email = mUser.EmailAddress - user.Token = accessToken.Token - - user.Avatar = avatarLink(mUser.EmailAddress) - - return &user, bs.Open, nil + return &model.User{ + Login: login, + Email: mUser.EmailAddress, + Token: accessToken.Token, + Avatar: avatarLink(mUser.EmailAddress), + }, nil } -func (bs *BitbucketServer) Auth(token, secret string) (string, error) { - log.Info("Staring to auth for bitbucketServer. %s", token) - if len(token) == 0 { - return "", fmt.Errorf("Hasn't logged in yet") - } - return token, nil +// Auth is not supported by the Stash driver. +func (*client) Auth(token, secret string) (string, error) { + return "", fmt.Errorf("Not Implemented") } -func (bs *BitbucketServer) Repo(u *model.User, owner, name string) (*model.Repo, error) { - log.Info("Staring repo for bitbucketServer with user " + u.Login + " " + owner + " " + name) +// Teams is not supported by the Stash driver. +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) if err != nil { log.Error(err) @@ -160,40 +154,36 @@ func (bs *BitbucketServer) Repo(u *model.User, owner, name string) (*model.Repo, bsRepo := BSRepo{} json.Unmarshal(contents, &bsRepo) - cloneLink := "" - repoLink := "" + repo := &model.Repo{ + 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 { if item.Name == "http" { - cloneLink = item.Href + repo.Clone = item.Href } } for _, item := range bsRepo.Links.Self { 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 } -func (bs *BitbucketServer) Repos(u *model.User) ([]*model.RepoLite, error) { - log.Info("Staring repos for bitbucketServer " + u.Login) +func (c *client) Repos(u *model.User) ([]*model.RepoLite, error) { + 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 { log.Error(err) } @@ -213,10 +203,8 @@ func (bs *BitbucketServer) Repos(u *model.User) ([]*model.RepoLite, error) { return repos, nil } -func (bs *BitbucketServer) Perm(u *model.User, owner, repo string) (*model.Perm, error) { - - //TODO: find the real permissions - log.Info("Staring perm for bitbucketServer") +func (c *client) Perm(u *model.User, owner, repo string) (*model.Perm, error) { + // TODO need to fetch real permissions here perms := new(model.Perm) perms.Pull = true perms.Admin = true @@ -224,11 +212,11 @@ func (bs *BitbucketServer) Perm(u *model.User, owner, repo string) (*model.Perm, 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)) - client := NewClientWithToken(&bs.Consumer, u.Token) - fileURL := fmt.Sprintf("%s/projects/%s/repos/%s/browse/%s?raw", bs.URL, r.Owner, r.Name, f) + client := NewClientWithToken(&c.Consumer, u.Token) + fileURL := fmt.Sprintf("%s/projects/%s/repos/%s/browse/%s?raw", c.URL, r.Owner, r.Name, f) log.Info(fileURL) response, err := client.Get(fileURL) if err != nil { @@ -246,28 +234,26 @@ func (bs *BitbucketServer) File(u *model.User, r *model.Repo, b *model.Build, f return responseBytes, nil } -func (bs *BitbucketServer) Status(u *model.User, r *model.Repo, b *model.Build, link string) error { - log.Info("Staring status for bitbucketServer") +// Status is not supported by the Gogs driver. +func (*client) Status(*model.User, *model.Repo, *model.Build, string) error { return nil } -func (bs *BitbucketServer) Netrc(user *model.User, r *model.Repo) (*model.Netrc, error) { - log.Info("Starting the Netrc lookup") - u, err := url.Parse(bs.URL) +func (c *client) Netrc(user *model.User, r *model.Repo) (*model.Netrc, error) { + u, err := url.Parse(c.URL) // TODO strip port from url if err != nil { return nil, err } return &model.Netrc{ Machine: u.Host, - Login: bs.GitUserName, - Password: bs.GitPassword, + Login: c.GitUserName, + Password: c.GitPassword, }, nil } -func (bs *BitbucketServer) Activate(u *model.User, r *model.Repo, k *model.Key, 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(&bs.Consumer, u.Token) - hook, err := bs.CreateHook(client, r.Owner, r.Name, "com.atlassian.stash.plugin.stash-web-post-receive-hooks-plugin:postReceiveHook", link) +func (c *client) Activate(u *model.User, r *model.Repo, link string) error { + client := NewClientWithToken(&c.Consumer, u.Token) + hook, err := c.CreateHook(client, r.Owner, r.Name, "com.atlassian.stash.plugin.stash-web-post-receive-hooks-plugin:postReceiveHook", link) if err != nil { return err } @@ -275,50 +261,38 @@ func (bs *BitbucketServer) Activate(u *model.User, r *model.Repo, k *model.Key, return nil } -func (bs *BitbucketServer) 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(&bs.Consumer, u.Token) - err := bs.DeleteHook(client, r.Owner, r.Name, "com.atlassian.stash.plugin.stash-web-post-receive-hooks-plugin:postReceiveHook", link) +func (c *client) Deactivate(u *model.User, r *model.Repo, link string) error { + client := NewClientWithToken(&c.Consumer, u.Token) + err := c.DeleteHook(client, r.Owner, r.Name, "com.atlassian.stash.plugin.stash-web-post-receive-hooks-plugin:postReceiveHook", link) if err != nil { return err } return nil } -func (bs *BitbucketServer) Hook(r *http.Request) (*model.Repo, *model.Build, error) { - log.Info("Staring hook for bitbucketServer") - defer r.Body.Close() - contents, err := ioutil.ReadAll(r.Body) - if err != nil { - log.Info(err) +func (c *client) Hook(r *http.Request) (*model.Repo, *model.Build, error) { + hook := new(postHook) + if err := json.NewDecoder(r.Body).Decode(hook); err != nil { + return nil, nil, err } - var hookPost postHook - json.Unmarshal(contents, &hookPost) + build := &model.Build{ + 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{} - buildModel.Event = model.EventPush - buildModel.Ref = hookPost.RefChanges[0].RefID - buildModel.Author = hookPost.Changesets.Values[0].ToCommit.Author.EmailAddress - buildModel.Commit = hookPost.RefChanges[0].ToHash - buildModel.Avatar = avatarLink(hookPost.Changesets.Values[0].ToCommit.Author.EmailAddress) + repo := &model.Repo{ + Name: hook.Repository.Slug, + Owner: hook.Repository.Project.Key, + FullName: fmt.Sprintf("%s/%s", hook.Repository.Project.Key, hook.Repository.Slug), + Branch: "master", + 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 - 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" + return repo, build, nil } type HookDetail struct { @@ -336,7 +310,7 @@ type Hook struct { } // 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 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 -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", project, slug, hook_key) doDelete(client, bs.URL+enablePath) diff --git a/remote/github/github.go b/remote/github/github.go index 258aa9cb6..ac9b1a6aa 100644 --- a/remote/github/github.go +++ b/remote/github/github.go @@ -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 // 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) _, err := CreateUpdateHook(client, r.Owner, r.Name, link) return err diff --git a/remote/gitlab/gitlab.go b/remote/gitlab/gitlab.go index b5beca6d0..488dbd9d7 100644 --- a/remote/gitlab/gitlab.go +++ b/remote/gitlab/gitlab.go @@ -4,6 +4,7 @@ import ( "crypto/tls" "fmt" "io/ioutil" + "net" "net/http" "net/url" "strconv" @@ -13,38 +14,59 @@ import ( "github.com/drone/drone/remote" "github.com/drone/drone/shared/httputil" "github.com/drone/drone/shared/oauth2" - "github.com/drone/drone/shared/token" "github.com/drone/drone/remote/gitlab/client" ) -const ( - DefaultScope = "api" -) +const 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 { URL string Client string Secret string - AllowedOrgs []string - CloneMode string - Open bool + Machine string + Username string + Password string PrivateMode bool SkipVerify bool HideArchives 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 { url_, err := url.Parse(config) if err != nil { @@ -57,17 +79,17 @@ func Load(config string) *Gitlab { gitlab.URL = url_.String() gitlab.Client = params.Get("client_id") gitlab.Secret = params.Get("client_secret") - gitlab.AllowedOrgs = params["orgs"] + // gitlab.AllowedOrgs = params["orgs"] gitlab.SkipVerify, _ = strconv.ParseBool(params.Get("skip_verify")) 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") { - case "oauth": - gitlab.CloneMode = "oauth" - default: - gitlab.CloneMode = "token" - } + // switch params.Get("clone_mode") { + // case "oauth": + // gitlab.CloneMode = "oauth" + // default: + // gitlab.CloneMode = "token" + // } // this is a temp workaround gitlab.Search, _ = strconv.ParseBool(params.Get("search")) @@ -77,7 +99,7 @@ func Load(config string) *Gitlab { // Login authenticates the session and returns the // 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{ ClientId: g.Client, @@ -97,41 +119,41 @@ func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User, var code = req.FormValue("code") if len(code) == 0 { 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 token_, err = trans.Exchange(code) 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) login, err := client.CurrentUser() if err != nil { - return nil, false, err + return nil, err } - if len(g.AllowedOrgs) != 0 { - groups, err := client.AllGroups() - if err != nil { - return nil, false, fmt.Errorf("Could not check org membership. %s", err) - } - - var member bool - for _, group := range groups { - for _, allowedOrg := range g.AllowedOrgs { - if group.Path == allowedOrg { - member = true - break - } - } - } - - if !member { - return nil, false, fmt.Errorf("User does not belong to correct group. Must belong to %v", g.AllowedOrgs) - } - } + // if len(g.AllowedOrgs) != 0 { + // groups, err := client.AllGroups() + // if err != nil { + // return nil, fmt.Errorf("Could not check org membership. %s", err) + // } + // + // var member bool + // for _, group := range groups { + // for _, allowedOrg := range g.AllowedOrgs { + // if group.Path == allowedOrg { + // member = true + // break + // } + // } + // } + // + // if !member { + // return nil, false, fmt.Errorf("User does not belong to correct group. Must belong to %v", g.AllowedOrgs) + // } + // } user := &model.User{} 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 } - return user, g.Open, nil + return user, nil } 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 } +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. func (g *Gitlab) Repo(u *model.User, owner, name string) (*model.Repo, error) { 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 // private repositories from a remote system. -func (g *Gitlab) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { - url_, err := url.Parse(g.URL) - if err != nil { - return nil, err - } - netrc := &model.Netrc{} - netrc.Machine = url_.Host +// func (g *Gitlab) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { +// url_, err := url.Parse(g.URL) +// if err != nil { +// return nil, err +// } +// netrc := &model.Netrc{} +// 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 { - 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) +// Netrc returns a netrc file capable of authenticating Gitlab requests and +// cloning Gitlab repositories. The netrc will use the global machine account +// when configured. +func (g *Gitlab) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { + if g.Password != "" { + return &model.Netrc{ + Login: g.Username, + 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 // 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) id, err := GetProjectId(g, client, repo.Owner, repo.Name) if err != nil { diff --git a/remote/gitlab/gitlab_test.go b/remote/gitlab/gitlab_test.go index 944afbb70..6e4f7003f 100644 --- a/remote/gitlab/gitlab_test.go +++ b/remote/gitlab/gitlab_test.go @@ -94,13 +94,13 @@ func Test_Gitlab(t *testing.T) { // Test activate method g.Describe("Activate", 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.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() }) diff --git a/remote/gogs/fixtures/handler.go b/remote/gogs/fixtures/handler.go new file mode 100644 index 000000000..26300644c --- /dev/null +++ b/remote/gogs/fixtures/handler.go @@ -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 + } + } +] +` diff --git a/remote/gogs/testdata/hook_push.go b/remote/gogs/fixtures/hooks.go similarity index 97% rename from remote/gogs/testdata/hook_push.go rename to remote/gogs/fixtures/hooks.go index 868f1db6f..f73ced4be 100644 --- a/remote/gogs/testdata/hook_push.go +++ b/remote/gogs/fixtures/hooks.go @@ -1,6 +1,6 @@ -package testdata +package fixtures -var PushHook = ` +var HookPush = ` { "ref": "refs/heads/master", "before": "4b2626259b5a97b6b4eab5e6cca66adb986b672b", diff --git a/remote/gogs/gogs.go b/remote/gogs/gogs.go index 31b4b1604..b4306a827 100644 --- a/remote/gogs/gogs.go +++ b/remote/gogs/gogs.go @@ -12,172 +12,181 @@ import ( "github.com/gogits/go-gogs-client" ) -// Remote defines a remote implementation that integrates with Gogs, an open -// source Git service written in Go. See https://gogs.io/ -type Remote struct { +// Opts defines configuration options. +type Opts 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 - Open bool + Machine string + Username string + Password string PrivateMode bool SkipVerify bool } // New returns a Remote implementation that integrates with Gogs, an open // source Git service written in Go. See https://gogs.io/ -func New(url string, private, skipverify bool) remote.Remote { - return &Remote{ - URL: url, - PrivateMode: private, - SkipVerify: skipverify, +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 &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. -func (g *Remote) Login(res http.ResponseWriter, req *http.Request) (*model.User, bool, error) { +// Login authenticates an account with Gogs using basic authenticaiton. The +// 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 ( username = req.FormValue("username") password = req.FormValue("password") ) - // if the username or password doesn't exist we re-direct - // the user to the login screen. + // if the username or password is empty we re-direct to the login screen. if len(username) == 0 || len(password) == 0 { 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 var accessToken string tokens, err := client.ListAccessTokens(username, password) - if err != nil { - return nil, false, err - } - for _, token := range tokens { - if token.Name == "drone" { - accessToken = token.Sha1 - break + if err == nil { + for _, token := range tokens { + if token.Name == "drone" { + accessToken = token.Sha1 + break + } } } // if drone token not found, create it if accessToken == "" { - token, err := client.CreateAccessToken(username, password, gogs.CreateAccessTokenOption{Name: "drone"}) - if err != nil { - return nil, false, err + token, terr := client.CreateAccessToken( + username, + password, + gogs.CreateAccessTokenOption{Name: "drone"}, + ) + if terr != nil { + return nil, terr } accessToken = token.Sha1 } - client = NewGogsClient(g.URL, accessToken, g.SkipVerify) - userInfo, 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() + client = c.newClientToken(accessToken) + account, err := client.GetUserInfo(username) if err != nil { return nil, err } - fullName := owner + "/" + name - for _, repo := range repos_ { - if repo.FullName == fullName { - return toRepo(repo), nil - } - } - - return nil, fmt.Errorf("Not Found") + return &model.User{ + Token: accessToken, + Login: account.UserName, + Email: account.Email, + Avatar: expandAvatar(c.URL, account.AvatarUrl), + }, nil } -// Repos fetches a list of repos from the remote system. -func (g *Remote) Repos(u *model.User) ([]*model.RepoLite, error) { +// Auth is not supported by the Gogs driver. +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{} - client := NewGogsClient(g.URL, u.Token, g.SkipVerify) - repos_, err := client.ListMyRepos() + client := c.newClientToken(u.Token) + all, err := client.ListMyRepos() if err != nil { return repos, err } - for _, repo := range repos_ { + for _, repo := range all { repos = append(repos, toRepoLite(repo)) } - return repos, err } -// Perm fetches the named repository permissions from -// the remote system for the specified user. -func (g *Remote) Perm(u *model.User, owner, name string) (*model.Perm, error) { - client := NewGogsClient(g.URL, u.Token, g.SkipVerify) - repos_, err := client.ListMyRepos() +// Perm returns the user permissions for the named Gogs repository. +func (c *client) Perm(u *model.User, owner, name string) (*model.Perm, error) { + client := c.newClientToken(u.Token) + repo, err := client.GetRepo(owner, name) if err != nil { return nil, err } - - fullName := owner + "/" + name - for _, repo := range repos_ { - if repo.FullName == fullName { - return toPerm(repo.Permissions), nil - } - } - - return nil, fmt.Errorf("Not Found") - + return toPerm(repo.Permissions), nil } -// File fetches a file from the remote repository and returns in string format. -func (g *Remote) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { - client := NewGogsClient(g.URL, u.Token, g.SkipVerify) +// File fetches the file from the Gogs repository and returns its contents. +func (c *client) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { + client := c.newClientToken(u.Token) cfg, err := client.GetFile(r.Owner, r.Name, b.Commit, f) return cfg, err } -// Status sends the commit status to the remote system. -// An example would be the GitHub pull request status. -func (g *Remote) Status(u *model.User, r *model.Repo, b *model.Build, link string) error { - return fmt.Errorf("Not Implemented") +// Status is not supported by the Gogs driver. +func (c *client) Status(u *model.User, r *model.Repo, b *model.Build, link string) error { + return nil } -// Netrc returns a .netrc file that can be used to clone -// private repositories from a remote system. -func (g *Remote) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { - url_, err := url.Parse(g.URL) - if err != nil { - return nil, err - } - host, _, err := net.SplitHostPort(url_.Host) - if err == nil { - url_.Host = host +// Netrc returns a netrc file capable of authenticating Gogs requests and +// cloning Gogs repositories. The netrc will use the global machine account +// when configured. +func (c *client) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { + if c.Password != "" { + return &model.Netrc{ + Login: c.Username, + Password: c.Password, + Machine: c.Machine, + }, nil } return &model.Netrc{ Login: u.Token, Password: "x-oauth-basic", - Machine: url_.Host, + Machine: c.Machine, }, nil } -// Activate activates a repository by creating the post-commit hook and -// adding the SSH deploy key, if applicable. -func (g *Remote) Activate(u *model.User, r *model.Repo, k *model.Key, link string) error { +// Activate activates the repository by registering post-commit hooks with +// the Gogs repository. +func (c *client) Activate(u *model.User, r *model.Repo, link string) error { config := map[string]string{ "url": link, "secret": r.Hash, @@ -189,20 +198,19 @@ func (g *Remote) Activate(u *model.User, r *model.Repo, k *model.Key, link strin Active: true, } - client := NewGogsClient(g.URL, u.Token, g.SkipVerify) + client := c.newClientToken(u.Token) _, err := client.CreateRepoHook(r.Owner, r.Name, hook) return err } -// Deactivate removes a repository by removing all the post-commit hooks -// which are equal to link and removing the SSH deploy key. -func (g *Remote) Deactivate(u *model.User, r *model.Repo, link string) error { - return fmt.Errorf("Not Implemented") +// Deactivate is not supported by the Gogs driver. +func (c *client) Deactivate(u *model.User, r *model.Repo, link string) error { + return nil } -// Hook parses the post-commit hook from the Request body -// and returns the required data in a standard format. -func (g *Remote) Hook(r *http.Request) (*model.Repo, *model.Build, error) { +// Hook parses the incoming Gogs hook and returns the Repository and Build +// details. If the hook is unsupported nil values are returned. +func (c *client) Hook(r *http.Request) (*model.Repo, *model.Build, error) { var ( err error 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") { case "push": - var push *PushHook + var push *pushHook push, err = parsePush(r.Body) if err == nil { repo = repoFromPush(push) @@ -221,20 +229,20 @@ func (g *Remote) Hook(r *http.Request) (*model.Repo, *model.Build, error) { return repo, build, err } -// NewClient initializes and returns a API client. -func NewGogsClient(url, token string, skipVerify bool) *gogs.Client { - sslClient := &http.Client{} - c := gogs.NewClient(url, token) +// helper function to return the Gogs client +func (c *client) newClient() *gogs.Client { + return c.newClientToken("") +} - if skipVerify { - sslClient.Transport = &http.Transport{ +// helper function to return the Gogs client +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}, } - c.SetHTTPClient(sslClient) + client.SetHTTPClient(httpClient) } - return c -} - -func (g *Remote) String() string { - return "gogs" + return client } diff --git a/remote/gogs/gogs_test.go b/remote/gogs/gogs_test.go new file mode 100644 index 000000000..792935956 --- /dev/null +++ b/remote/gogs/gogs_test.go @@ -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", + } +) diff --git a/remote/gogs/helper.go b/remote/gogs/helper.go index bc698c04d..c6941faa3 100644 --- a/remote/gogs/helper.go +++ b/remote/gogs/helper.go @@ -12,8 +12,7 @@ import ( "github.com/gogits/go-gogs-client" ) -// helper function that converts a Gogs repository -// to a Drone repository. +// helper function that converts a Gogs repository to a Drone repository. func toRepoLite(from *gogs.Repository) *model.RepoLite { name := strings.Split(from.FullName, "/")[1] avatar := expandAvatar( @@ -28,8 +27,7 @@ func toRepoLite(from *gogs.Repository) *model.RepoLite { } } -// helper function that converts a Gogs repository -// to a Drone repository. +// helper function that converts a Gogs repository to a Drone repository. func toRepo(from *gogs.Repository) *model.Repo { name := strings.Split(from.FullName, "/")[1] avatar := expandAvatar( @@ -49,8 +47,7 @@ func toRepo(from *gogs.Repository) *model.Repo { } } -// helper function that converts a Gogs permission -// to a Drone permission. +// helper function that converts a Gogs permission to a Drone permission. func toPerm(from gogs.Permission) *model.Perm { return &model.Perm{ Pull: from.Pull, @@ -59,11 +56,10 @@ func toPerm(from gogs.Permission) *model.Perm { } } -// helper function that extracts the Build data -// from a Gogs push hook -func buildFromPush(hook *PushHook) *model.Build { +// helper function that extracts the Build data from a Gogs push hook +func buildFromPush(hook *pushHook) *model.Build { avatar := expandAvatar( - hook.Repo.Url, + hook.Repo.URL, fixMalformedAvatar(hook.Sender.Avatar), ) return &model.Build{ @@ -79,9 +75,8 @@ func buildFromPush(hook *PushHook) *model.Build { } } -// helper function that extracts the Repository data -// from a Gogs push hook -func repoFromPush(hook *PushHook) *model.Repo { +// helper function that extracts the Repository data from a Gogs push hook +func repoFromPush(hook *pushHook) *model.Repo { fullName := fmt.Sprintf( "%s/%s", hook.Repo.Owner.Username, @@ -91,20 +86,19 @@ func repoFromPush(hook *PushHook) *model.Repo { Name: hook.Repo.Name, Owner: hook.Repo.Owner.Username, FullName: fullName, - Link: hook.Repo.Url, + Link: hook.Repo.URL, } } -// helper function that parses a push hook from -// a read closer. -func parsePush(r io.Reader) (*PushHook, error) { - push := new(PushHook) +// helper function that parses a push hook from a read closer. +func parsePush(r io.Reader) (*pushHook, error) { + push := new(pushHook) err := json.NewDecoder(r).Decode(push) return push, err } -// fixMalformedAvatar is a helper function that fixes -// an avatar url if malformed (known bug with gogs) +// fixMalformedAvatar is a helper function that fixes an avatar url if malformed +// (currently a known bug with gogs) func fixMalformedAvatar(url string) string { index := strings.Index(url, "///") if index != -1 { @@ -117,16 +111,16 @@ func fixMalformedAvatar(url string) string { return url } -// expandAvatar is a helper function that converts -// a relative avatar URL to the abosolute url. +// expandAvatar is a helper function that converts a relative avatar URL to the +// abosolute url. func expandAvatar(repo, rawurl string) string { if !strings.HasPrefix(rawurl, "/avatars/") { return rawurl } - url_, err := url.Parse(repo) + url, err := url.Parse(repo) if err != nil { return rawurl } - url_.Path = rawurl - return url_.String() + url.Path = rawurl + return url.String() } diff --git a/remote/gogs/helper_test.go b/remote/gogs/helper_test.go index 444596e54..9ec3d635b 100644 --- a/remote/gogs/helper_test.go +++ b/remote/gogs/helper_test.go @@ -5,7 +5,7 @@ import ( "testing" "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/gogits/go-gogs-client" @@ -17,7 +17,7 @@ func Test_parse(t *testing.T) { g.Describe("Gogs", func() { g.It("Should parse push hook payload", func() { - buf := bytes.NewBufferString(testdata.PushHook) + buf := bytes.NewBufferString(fixtures.HookPush) hook, err := parsePush(buf) g.Assert(err == nil).IsTrue() 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.Compare).Equal("http://gogs.golang.org/gordon/hello-world/compare/4b2626259b5a97b6b4eab5e6cca66adb986b672b...ef98532add3b2feb7a137426bba1248724367df5") 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.Email).Equal("gordon@golang.org") 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() { - buf := bytes.NewBufferString(testdata.PushHook) + buf := bytes.NewBufferString(fixtures.HookPush) hook, _ := parsePush(buf) build := buildFromPush(hook) 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() { - buf := bytes.NewBufferString(testdata.PushHook) + buf := bytes.NewBufferString(fixtures.HookPush) hook, _ := parsePush(buf) repo := repoFromPush(hook) g.Assert(repo.Name).Equal(hook.Repo.Name) g.Assert(repo.Owner).Equal(hook.Repo.Owner.Username) 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() { diff --git a/remote/gogs/types.go b/remote/gogs/types.go index ac1947357..2b9f38ed1 100644 --- a/remote/gogs/types.go +++ b/remote/gogs/types.go @@ -1,6 +1,6 @@ package gogs -type PushHook struct { +type pushHook struct { Ref string `json:"ref"` Before string `json:"before"` After string `json:"after"` @@ -15,7 +15,7 @@ type PushHook struct { Repo struct { ID int64 `json:"id"` Name string `json:"name"` - Url string `json:"url"` + URL string `json:"url"` Private bool `json:"private"` Owner struct { Name string `json:"name"` @@ -27,7 +27,7 @@ type PushHook struct { Commits []struct { ID string `json:"id"` Message string `json:"message"` - Url string `json:"url"` + URL string `json:"url"` } `json:"commits"` Sender struct { diff --git a/server/server.go b/server/server.go index 477722adb..557b4b756 100644 --- a/server/server.go +++ b/server/server.go @@ -134,8 +134,6 @@ func (s *Server) Handler() http.Handler { repo.Use(session.MustPull) repo.GET("", api.GetRepo) - repo.GET("/key", api.GetRepoKey) - repo.POST("/key", api.PostRepoKey) repo.GET("/builds", api.GetBuilds) repo.GET("/builds/:number", api.GetBuild) repo.GET("/logs/:number/:job", api.GetBuildLogs) diff --git a/store/datastore/keys.go b/store/datastore/keys.go deleted file mode 100644 index ca68b728a..000000000 --- a/store/datastore/keys.go +++ /dev/null @@ -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=?" diff --git a/store/datastore/keys_test.go b/store/datastore/keys_test.go deleted file mode 100644 index 2883ee7f7..000000000 --- a/store/datastore/keys_test.go +++ /dev/null @@ -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----- -` diff --git a/store/store.go b/store/store.go index a359a3edd..2d20380aa 100644 --- a/store/store.go +++ b/store/store.go @@ -54,18 +54,6 @@ type Store interface { // DeleteRepo deletes a user repository. 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(*model.Repo) ([]*model.Secret, error) @@ -192,22 +180,6 @@ func DeleteRepo(c context.Context, repo *model.Repo) error { 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) { return FromContext(c).GetSecretList(r) } diff --git a/template/amber/repo_config.amber b/template/amber/repo_config.amber index be0f3516e..f33c3201a 100644 --- a/template/amber/repo_config.amber +++ b/template/amber/repo_config.amber @@ -65,10 +65,6 @@ block content else input#trusted[type="checkbox"][hidden="hidden"] 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.col-md-12 div.alert.alert-danger @@ -78,4 +74,4 @@ block content block append scripts script - var view = new RepoConfigViewModel(#{Repo.FullName}); \ No newline at end of file + var view = new RepoConfigViewModel(#{Repo.FullName}); diff --git a/web/login.go b/web/login.go index dbeb54b5c..4bc03c130 100644 --- a/web/login.go +++ b/web/login.go @@ -23,7 +23,7 @@ func GetLogin(c *gin.Context) { // remember why, so need to revisit this line. 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 { log.Errorf("cannot authenticate user. %s", err) c.Redirect(303, "/login?error=oauth_error") @@ -35,20 +35,16 @@ func GetLogin(c *gin.Context) { return } + var open = false // TODO get this from context + // get the user from the database u, err := store.GetUserLogin(c, tmpuser.Login) 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 // return a notAuthorized error. the only exception // 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) c.Redirect(303, "/login?error=access_denied") return @@ -69,12 +65,6 @@ func GetLogin(c *gin.Context) { c.Redirect(303, "/login?error=internal_error") 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 diff --git a/web/pages.go b/web/pages.go index 8480c1e85..7163063ff 100644 --- a/web/pages.go +++ b/web/pages.go @@ -116,7 +116,6 @@ func ShowRepoConf(c *gin.Context) { user := session.User(c) repo := session.Repo(c) - key, _ := store.GetKey(c, repo) token, _ := token.New( token.CsrfToken, @@ -126,7 +125,6 @@ func ShowRepoConf(c *gin.Context) { c.HTML(200, "repo_config.html", gin.H{ "User": user, "Repo": repo, - "Key": key, "Csrf": token, "Link": httputil.GetURL(c.Request), })