2018-02-20 00:24:10 +02:00
// Copyright 2018 Drone.IO Inc.
2018-03-21 15:02:17 +02:00
//
2018-02-20 00:24:10 +02:00
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
2018-03-21 15:02:17 +02:00
//
2018-02-20 00:24:10 +02:00
// http://www.apache.org/licenses/LICENSE-2.0
2018-03-21 15:02:17 +02:00
//
2018-02-20 00:24:10 +02:00
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2016-04-19 06:40:49 +02:00
package bitbucketserver
2016-05-02 01:30:00 +02:00
// 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.
2016-04-19 07:32:40 +02:00
2016-04-19 06:40:49 +02:00
import (
2016-05-02 01:30:00 +02:00
"crypto/rsa"
2016-06-26 07:27:09 +02:00
"crypto/tls"
2016-05-02 01:30:00 +02:00
"crypto/x509"
"encoding/pem"
2016-04-20 20:59:47 +02:00
"fmt"
2016-06-26 07:27:09 +02:00
"io/ioutil"
"net/http"
"net/url"
2016-06-12 03:42:55 +02:00
"strings"
2017-03-18 13:25:53 +02:00
"github.com/drone/drone/model"
"github.com/drone/drone/remote"
"github.com/drone/drone/remote/bitbucketserver/internal"
"github.com/mrjones/oauth"
2016-06-26 01:45:33 +02:00
)
const (
2016-06-26 07:27:09 +02:00
requestTokenURL = "%s/plugins/servlet/oauth/request-token"
2016-06-26 01:45:33 +02:00
authorizeTokenURL = "%s/plugins/servlet/oauth/authorize"
2016-06-26 07:27:09 +02:00
accessTokenURL = "%s/plugins/servlet/oauth/access-token"
2016-04-19 06:40:49 +02:00
)
2016-05-02 01:30:00 +02:00
// Opts defines configuration options.
type Opts struct {
2017-02-01 23:49:26 +02:00
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.
2017-02-01 23:41:45 +02:00
ConsumerRSAString string
2017-02-01 23:49:26 +02:00
SkipVerify bool // Skip ssl verification.
2016-04-19 06:40:49 +02:00
}
2016-06-26 01:45:33 +02:00
type Config struct {
2016-06-26 07:27:09 +02:00
URL string
Username string
Password string
2016-06-26 01:45:33 +02:00
SkipVerify bool
2016-06-26 07:27:09 +02:00
Consumer * oauth . Consumer
2016-06-26 01:45:33 +02:00
}
2016-05-02 01:30:00 +02:00
// 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 ) {
2016-06-26 01:45:33 +02:00
config := & Config {
2016-06-26 07:27:09 +02:00
URL : opts . URL ,
Username : opts . Username ,
Password : opts . Password ,
2016-06-26 01:45:33 +02:00
SkipVerify : opts . SkipVerify ,
2016-04-29 21:39:56 +02:00
}
2016-05-02 01:30:00 +02:00
switch {
2016-06-26 01:45:33 +02:00
case opts . Username == "" :
2016-05-02 01:30:00 +02:00
return nil , fmt . Errorf ( "Must have a git machine account username" )
2016-06-26 01:45:33 +02:00
case opts . Password == "" :
2016-05-02 01:30:00 +02:00
return nil , fmt . Errorf ( "Must have a git machine account password" )
2016-06-26 01:45:33 +02:00
case opts . ConsumerKey == "" :
2016-05-02 01:30:00 +02:00
return nil , fmt . Errorf ( "Must have a oauth1 consumer key" )
}
2016-04-19 06:40:49 +02:00
2017-02-01 23:41:45 +02:00
if opts . ConsumerRSA == "" && opts . ConsumerRSAString == "" {
return nil , fmt . Errorf ( "must have CONSUMER_RSA_KEY set to the path of a oauth1 consumer key file or CONSUMER_RSA_KEY_STRING set to the value of a oauth1 consumer key" )
2016-04-19 22:29:52 +02:00
}
2017-02-01 23:41:45 +02:00
2017-02-01 23:49:26 +02:00
var keyFileBytes [ ] byte
2017-02-01 23:41:45 +02:00
if opts . ConsumerRSA != "" {
2017-02-01 23:49:26 +02:00
var err error
2017-02-01 23:41:45 +02:00
keyFileBytes , err = ioutil . ReadFile ( opts . ConsumerRSA )
if err != nil {
return nil , err
}
} else {
keyFileBytes = [ ] byte ( opts . ConsumerRSAString )
2016-04-19 22:29:52 +02:00
}
2017-02-01 23:41:45 +02:00
2017-02-01 23:49:26 +02:00
block , _ := pem . Decode ( keyFileBytes )
PrivateKey , err := x509 . ParsePKCS1PrivateKey ( block . Bytes )
if err != nil {
return nil , err
}
2017-02-01 23:41:45 +02:00
2016-06-26 07:27:09 +02:00
config . Consumer = CreateConsumer ( opts . URL , opts . ConsumerKey , PrivateKey )
2016-06-26 01:45:33 +02:00
return config , nil
2016-04-19 06:40:49 +02:00
}
2016-06-26 01:45:33 +02:00
func ( c * Config ) Login ( res http . ResponseWriter , req * http . Request ) ( * model . User , error ) {
2016-06-26 07:27:09 +02:00
requestToken , url , err := c . Consumer . GetRequestTokenAndUrl ( "oob" )
2016-04-19 06:40:49 +02:00
if err != nil {
2016-05-02 01:30:00 +02:00
return nil , err
2016-04-19 06:40:49 +02:00
}
var code = req . FormValue ( "oauth_verifier" )
if len ( code ) == 0 {
http . Redirect ( res , req , url , http . StatusSeeOther )
2016-05-02 01:30:00 +02:00
return nil , nil
2016-04-19 06:40:49 +02:00
}
2016-05-02 01:30:00 +02:00
requestToken . Token = req . FormValue ( "oauth_token" )
2016-06-26 07:27:09 +02:00
accessToken , err := c . Consumer . AuthorizeToken ( requestToken , code )
2016-04-20 20:59:47 +02:00
if err != nil {
2016-05-02 01:30:00 +02:00
return nil , err
2016-04-19 06:40:49 +02:00
}
2016-06-26 07:27:09 +02:00
client := internal . NewClientWithToken ( c . URL , c . Consumer , accessToken . Token )
2016-04-19 06:40:49 +02:00
2016-07-24 23:07:44 +02:00
user , err := client . FindCurrentUser ( )
if err != nil {
return nil , err
}
return convertUser ( user , accessToken ) , nil
2016-06-13 07:18:31 +02:00
2016-05-02 01:30:00 +02:00
}
2016-04-19 06:40:49 +02:00
2016-05-02 01:30:00 +02:00
// Auth is not supported by the Stash driver.
2016-06-26 01:45:33 +02:00
func ( * Config ) Auth ( token , secret string ) ( string , error ) {
2016-05-02 01:30:00 +02:00
return "" , fmt . Errorf ( "Not Implemented" )
2016-04-19 06:40:49 +02:00
}
2016-05-02 01:30:00 +02:00
// Teams is not supported by the Stash driver.
2016-06-26 01:45:33 +02:00
func ( * Config ) Teams ( u * model . User ) ( [ ] * model . Team , error ) {
2016-05-02 01:30:00 +02:00
var teams [ ] * model . Team
return teams , nil
2016-04-19 06:40:49 +02:00
}
2016-11-11 20:56:48 +02:00
// TeamPerm is not supported by the Stash driver.
func ( * Config ) TeamPerm ( u * model . User , org string ) ( * model . Perm , error ) {
return nil , nil
}
2016-06-26 01:45:33 +02:00
func ( c * Config ) Repo ( u * model . User , owner , name string ) ( * model . Repo , error ) {
2016-07-24 23:07:44 +02:00
repo , err := internal . NewClientWithToken ( c . URL , c . Consumer , u . Token ) . FindRepo ( owner , name )
if err != nil {
return nil , err
}
return convertRepo ( repo ) , nil
2016-06-26 01:45:33 +02:00
}
2016-04-19 06:40:49 +02:00
2017-07-14 21:58:38 +02:00
func ( c * Config ) Repos ( u * model . User ) ( [ ] * model . Repo , error ) {
2016-07-24 23:07:44 +02:00
repos , err := internal . NewClientWithToken ( c . URL , c . Consumer , u . Token ) . FindRepos ( )
if err != nil {
return nil , err
}
2017-07-14 21:58:38 +02:00
var all [ ] * model . Repo
2016-07-24 23:07:44 +02:00
for _ , repo := range repos {
2017-07-14 21:58:38 +02:00
all = append ( all , convertRepo ( repo ) )
2016-07-24 23:07:44 +02:00
}
2016-04-19 06:40:49 +02:00
2016-07-24 23:07:44 +02:00
return all , nil
2016-04-19 06:40:49 +02:00
}
2016-06-26 01:45:33 +02:00
func ( c * Config ) Perm ( u * model . User , owner , repo string ) ( * model . Perm , error ) {
2016-06-26 07:27:09 +02:00
client := internal . NewClientWithToken ( c . URL , c . Consumer , u . Token )
2016-06-14 05:08:56 +02:00
2016-06-26 01:45:33 +02:00
return client . FindRepoPerms ( owner , repo )
2016-04-19 06:40:49 +02:00
}
2016-06-26 01:45:33 +02:00
func ( c * Config ) File ( u * model . User , r * model . Repo , b * model . Build , f string ) ( [ ] byte , error ) {
2016-06-26 07:27:09 +02:00
client := internal . NewClientWithToken ( c . URL , c . Consumer , u . Token )
2016-04-19 06:40:49 +02:00
2016-08-11 22:35:47 +02:00
return client . FindFileForRepo ( r . Owner , r . Name , f , b . Ref )
2016-04-19 06:40:49 +02:00
}
2017-03-18 13:25:53 +02:00
func ( c * Config ) FileRef ( u * model . User , r * model . Repo , ref , f string ) ( [ ] byte , error ) {
client := internal . NewClientWithToken ( c . URL , c . Consumer , u . Token )
return client . FindFileForRepo ( r . Owner , r . Name , f , ref )
}
2016-06-26 07:27:09 +02:00
// Status is not supported by the bitbucketserver driver.
2017-02-01 23:49:26 +02:00
func ( c * Config ) Status ( u * model . User , r * model . Repo , b * model . Build , link string ) error {
2016-08-14 04:06:15 +02:00
status := internal . BuildStatus {
State : convertStatus ( b . Status ) ,
Desc : convertDesc ( b . Status ) ,
2016-08-14 21:27:53 +02:00
Name : fmt . Sprintf ( "Drone #%d - %s" , b . Number , b . Branch ) ,
2016-08-14 04:06:15 +02:00
Key : "Drone" ,
Url : link ,
}
client := internal . NewClientWithToken ( c . URL , c . Consumer , u . Token )
return client . CreateStatus ( b . Commit , & status )
2016-04-19 06:40:49 +02:00
}
2016-06-26 01:45:33 +02:00
func ( c * Config ) Netrc ( user * model . User , r * model . Repo ) ( * model . Netrc , error ) {
2016-06-12 03:42:55 +02:00
u , err := url . Parse ( c . URL )
2016-06-13 07:18:31 +02:00
if err != nil {
return nil , err
}
2016-06-12 03:42:55 +02:00
//remove the port
tmp := strings . Split ( u . Host , ":" )
var host = tmp [ 0 ]
2016-04-19 06:40:49 +02:00
if err != nil {
2016-04-19 18:47:02 +02:00
return nil , err
2016-04-19 06:40:49 +02:00
}
return & model . Netrc {
2016-06-12 03:42:55 +02:00
Machine : host ,
2016-06-26 01:45:33 +02:00
Login : c . Username ,
Password : c . Password ,
2016-04-19 06:40:49 +02:00
} , nil
}
2016-06-26 01:45:33 +02:00
func ( c * Config ) Activate ( u * model . User , r * model . Repo , link string ) error {
2016-06-26 07:27:09 +02:00
client := internal . NewClientWithToken ( c . URL , c . Consumer , u . Token )
2016-06-26 01:45:33 +02:00
return client . CreateHook ( r . Owner , r . Name , link )
2016-04-19 06:40:49 +02:00
}
2016-06-26 01:45:33 +02:00
func ( c * Config ) Deactivate ( u * model . User , r * model . Repo , link string ) error {
2016-06-26 07:27:09 +02:00
client := internal . NewClientWithToken ( c . URL , c . Consumer , u . Token )
2016-06-26 01:45:33 +02:00
return client . DeleteHook ( r . Owner , r . Name , link )
2016-04-19 06:40:49 +02:00
}
2016-06-26 01:45:33 +02:00
func ( c * Config ) Hook ( r * http . Request ) ( * model . Repo , * model . Build , error ) {
2016-08-11 22:35:47 +02:00
return parseHook ( r , c . URL )
2016-04-19 06:40:49 +02:00
}
2016-06-26 07:27:09 +02:00
func CreateConsumer ( URL string , ConsumerKey string , PrivateKey * rsa . PrivateKey ) * oauth . Consumer {
2016-06-26 01:45:33 +02:00
consumer := oauth . NewRSAConsumer (
2016-06-26 07:27:09 +02:00
ConsumerKey ,
PrivateKey ,
2016-06-26 01:45:33 +02:00
oauth . ServiceProvider {
2016-06-26 07:27:09 +02:00
RequestTokenUrl : fmt . Sprintf ( requestTokenURL , URL ) ,
AuthorizeTokenUrl : fmt . Sprintf ( authorizeTokenURL , URL ) ,
AccessTokenUrl : fmt . Sprintf ( accessTokenURL , URL ) ,
2016-06-26 01:45:33 +02:00
HttpMethod : "POST" ,
} )
consumer . HttpClient = & http . Client {
Transport : & http . Transport {
TLSClientConfig : & tls . Config { InsecureSkipVerify : true } ,
2017-07-14 21:58:38 +02:00
Proxy : http . ProxyFromEnvironment ,
2016-06-26 01:45:33 +02:00
} ,
2016-06-14 05:08:56 +02:00
}
2016-06-26 01:45:33 +02:00
return consumer
}