woodpecker/remote/bitbucketserver/bitbucketserver.go

361 lines
10 KiB
Go
Raw Normal View History

package bitbucketserver
2016-04-19 05:32:40 +00:00
// 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}
2016-04-19 05:32:40 +00:00
// 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)
2016-04-19 05:32:40 +00:00
// consumer call back is the base url to drone plus /authorize/
// Needs a pem private key added to /var/lib/bitbucketserver/private_key.pem
2016-04-19 05:32:40 +00:00
// After that you should be good to go
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
2016-04-29 19:39:56 +00:00
log "github.com/Sirupsen/logrus"
"github.com/drone/drone/model"
"github.com/drone/drone/remote"
"github.com/mrjones/oauth"
)
type BitbucketServer struct {
URL string
ConsumerKey string
GitUserName string
GitPassword string
ConsumerRSA string
Open bool
Consumer oauth.Consumer
}
2016-04-29 19:39:56 +00:00
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)
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 == "" {
2016-04-19 20:32:06 +00:00
log.Fatalln("Must have a git_username")
}
bitbucketserver.GitPassword = params.Get("git_password")
if bitbucketserver.GitPassword == "" {
2016-04-19 20:32:06 +00:00
log.Fatalln("Must have a git_password")
}
bitbucketserver.ConsumerKey = params.Get("consumer_key")
if bitbucketserver.ConsumerKey == "" {
2016-04-19 20:32:06 +00:00
log.Fatalln("Must have a consumer_key")
}
bitbucketserver.ConsumerRSA = params.Get("consumer_rsa")
if bitbucketserver.ConsumerRSA == "" {
2016-04-19 20:32:06 +00:00
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")
if len(code) == 0 {
log.Info("redirecting to %s", url)
http.Redirect(res, req, url, http.StatusSeeOther)
return nil, false, nil
}
var request_oauth_token = req.FormValue("oauth_token")
requestToken.Token = request_oauth_token
accessToken, err := bs.Consumer.AuthorizeToken(requestToken, code)
if err != nil {
log.Error(err)
}
client, err := bs.Consumer.MakeHttpClient(accessToken)
if err != nil {
log.Error(err)
}
response, err := client.Get(fmt.Sprintf("%s/plugins/servlet/applinks/whoami", bs.URL))
if err != nil {
log.Error(err)
}
defer response.Body.Close()
bits, err := ioutil.ReadAll(response.Body)
userName := string(bits)
2016-04-29 19:39:56 +00:00
response1, err := client.Get(fmt.Sprintf("%s/rest/api/1.0/users/%s", bs.URL, userName))
contents, err := ioutil.ReadAll(response1.Body)
defer response1.Body.Close()
var mUser User
json.Unmarshal(contents, &mUser)
user := model.User{}
user.Login = userName
user.Email = mUser.EmailAddress
user.Token = accessToken.Token
user.Avatar = avatarLink(mUser.EmailAddress)
return &user, bs.Open, 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
}
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)
client := NewClientWithToken(&bs.Consumer, u.Token)
2016-04-29 19:39:56 +00:00
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)
}
defer response.Body.Close()
contents, err := ioutil.ReadAll(response.Body)
bsRepo := BSRepo{}
json.Unmarshal(contents, &bsRepo)
cloneLink := ""
repoLink := ""
for _, item := range bsRepo.Links.Clone {
if item.Name == "http" {
cloneLink = item.Href
}
}
for _, item := range bsRepo.Links.Self {
if item.Href != "" {
repoLink = 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
2016-04-29 19:39:56 +00:00
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)
var repos = []*model.RepoLite{}
client := NewClientWithToken(&bs.Consumer, u.Token)
2016-04-29 19:39:56 +00:00
response, err := client.Get(fmt.Sprintf("%s/rest/api/1.0/repos?limit=10000", bs.URL))
if err != nil {
log.Error(err)
}
defer response.Body.Close()
contents, err := ioutil.ReadAll(response.Body)
var repoResponse Repos
json.Unmarshal(contents, &repoResponse)
for _, repo := range repoResponse.Values {
repos = append(repos, &model.RepoLite{
Name: repo.Slug,
FullName: repo.Project.Key + "/" + repo.Slug,
Owner: repo.Project.Key,
})
}
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")
perms := new(model.Perm)
perms.Pull = true
perms.Admin = true
perms.Push = true
return perms, nil
}
func (bs *BitbucketServer) 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)
log.Info(fileURL)
response, err := client.Get(fileURL)
if err != nil {
log.Error(err)
}
if response.StatusCode == 404 {
return nil, nil
}
defer response.Body.Close()
responseBytes, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Error(err)
}
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")
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)
if err != nil {
return nil, err
}
return &model.Netrc{
Machine: u.Host,
Login: bs.GitUserName,
Password: bs.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)
if err != nil {
return err
}
log.Info(hook)
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)
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)
}
var hookPost postHook
json.Unmarshal(contents, &hookPost)
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)
//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
2016-04-29 19:39:56 +00:00
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 {
2016-04-29 19:39:56 +00:00
Key string `json:"key"`
Name string `json:"name"`
Type string `json:"type"`
Description string `json:"description"`
Version string `json:"version"`
ConfigFormKey string `json:"configFormKey"`
}
type Hook struct {
2016-04-29 19:39:56 +00:00
Enabled bool `json:"enabled"`
Details *HookDetail `json:"details"`
}
// Enable hook for named repository
func (bs *BitbucketServer) CreateHook(client *http.Client, project, slug, hook_key, link string) (*Hook, error) {
// Set hook
hookBytes := []byte(fmt.Sprintf(`{"hook-url-0":"%s"}`, link))
// Enable hook
enablePath := fmt.Sprintf("/rest/api/1.0/projects/%s/repos/%s/settings/hooks/%s/enabled",
project, slug, hook_key)
doPut(client, bs.URL+enablePath, hookBytes)
return nil, nil
}
// Disable hook for named repository
func (bs *BitbucketServer) 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)
return nil
}