2018-02-19 22:24:10 +00:00
// Copyright 2018 Drone.IO Inc.
2018-03-21 13:02:17 +00:00
//
2018-02-19 22:24:10 +00: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 13:02:17 +00:00
//
2018-02-19 22:24:10 +00:00
// http://www.apache.org/licenses/LICENSE-2.0
2018-03-21 13:02:17 +00:00
//
2018-02-19 22:24:10 +00: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 04:40:49 +00:00
package bitbucketserver
2016-05-01 23:30:00 +00: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 05:32:40 +00:00
2016-04-19 04:40:49 +00:00
import (
2021-09-28 10:56:59 +00:00
"context"
2016-05-01 23:30:00 +00:00
"crypto/rsa"
2016-06-26 05:27:09 +00:00
"crypto/tls"
2016-05-01 23:30:00 +00:00
"crypto/x509"
"encoding/pem"
2016-04-20 18:59:47 +00:00
"fmt"
2016-06-26 05:27:09 +00:00
"io/ioutil"
"net/http"
"net/url"
2016-06-12 01:42:55 +00:00
"strings"
2017-03-18 11:25:53 +00:00
2021-06-22 10:34:35 +00:00
"github.com/mrjones/oauth"
2021-09-27 17:51:55 +00:00
"github.com/woodpecker-ci/woodpecker/server/model"
2021-09-23 16:25:51 +00:00
"github.com/woodpecker-ci/woodpecker/server/remote"
"github.com/woodpecker-ci/woodpecker/server/remote/bitbucketserver/internal"
2016-06-25 23:45:33 +00:00
)
const (
2016-06-26 05:27:09 +00:00
requestTokenURL = "%s/plugins/servlet/oauth/request-token"
2016-06-25 23:45:33 +00:00
authorizeTokenURL = "%s/plugins/servlet/oauth/authorize"
2016-06-26 05:27:09 +00:00
accessTokenURL = "%s/plugins/servlet/oauth/access-token"
2016-04-19 04:40:49 +00:00
)
2016-05-01 23:30:00 +00:00
// Opts defines configuration options.
type Opts struct {
2017-02-01 21:49:26 +00: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 21:41:45 +00:00
ConsumerRSAString string
2017-02-01 21:49:26 +00:00
SkipVerify bool // Skip ssl verification.
2016-04-19 04:40:49 +00:00
}
2016-06-25 23:45:33 +00:00
type Config struct {
2016-06-26 05:27:09 +00:00
URL string
Username string
Password string
2016-06-25 23:45:33 +00:00
SkipVerify bool
2016-06-26 05:27:09 +00:00
Consumer * oauth . Consumer
2016-06-25 23:45:33 +00:00
}
2016-05-01 23:30:00 +00: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-25 23:45:33 +00:00
config := & Config {
2016-06-26 05:27:09 +00:00
URL : opts . URL ,
Username : opts . Username ,
Password : opts . Password ,
2016-06-25 23:45:33 +00:00
SkipVerify : opts . SkipVerify ,
2016-04-29 19:39:56 +00:00
}
2016-05-01 23:30:00 +00:00
switch {
2016-06-25 23:45:33 +00:00
case opts . Username == "" :
2016-05-01 23:30:00 +00:00
return nil , fmt . Errorf ( "Must have a git machine account username" )
2016-06-25 23:45:33 +00:00
case opts . Password == "" :
2016-05-01 23:30:00 +00:00
return nil , fmt . Errorf ( "Must have a git machine account password" )
2016-06-25 23:45:33 +00:00
case opts . ConsumerKey == "" :
2016-05-01 23:30:00 +00:00
return nil , fmt . Errorf ( "Must have a oauth1 consumer key" )
}
2016-04-19 04:40:49 +00:00
2017-02-01 21:41:45 +00: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 20:29:52 +00:00
}
2017-02-01 21:41:45 +00:00
2017-02-01 21:49:26 +00:00
var keyFileBytes [ ] byte
2017-02-01 21:41:45 +00:00
if opts . ConsumerRSA != "" {
2017-02-01 21:49:26 +00:00
var err error
2017-02-01 21:41:45 +00:00
keyFileBytes , err = ioutil . ReadFile ( opts . ConsumerRSA )
if err != nil {
return nil , err
}
} else {
keyFileBytes = [ ] byte ( opts . ConsumerRSAString )
2016-04-19 20:29:52 +00:00
}
2017-02-01 21:41:45 +00:00
2017-02-01 21:49:26 +00:00
block , _ := pem . Decode ( keyFileBytes )
PrivateKey , err := x509 . ParsePKCS1PrivateKey ( block . Bytes )
if err != nil {
return nil , err
}
2017-02-01 21:41:45 +00:00
2016-06-26 05:27:09 +00:00
config . Consumer = CreateConsumer ( opts . URL , opts . ConsumerKey , PrivateKey )
2016-06-25 23:45:33 +00:00
return config , nil
2016-04-19 04:40:49 +00:00
}
2021-09-28 10:56:59 +00:00
func ( c * Config ) Login ( ctx context . Context , res http . ResponseWriter , req * http . Request ) ( * model . User , error ) {
2021-09-24 14:29:26 +00:00
requestToken , u , err := c . Consumer . GetRequestTokenAndUrl ( "oob" )
2016-04-19 04:40:49 +00:00
if err != nil {
2016-05-01 23:30:00 +00:00
return nil , err
2016-04-19 04:40:49 +00:00
}
var code = req . FormValue ( "oauth_verifier" )
if len ( code ) == 0 {
2021-09-24 14:29:26 +00:00
http . Redirect ( res , req , u , http . StatusSeeOther )
2016-05-01 23:30:00 +00:00
return nil , nil
2016-04-19 04:40:49 +00:00
}
2016-05-01 23:30:00 +00:00
requestToken . Token = req . FormValue ( "oauth_token" )
2016-06-26 05:27:09 +00:00
accessToken , err := c . Consumer . AuthorizeToken ( requestToken , code )
2016-04-20 18:59:47 +00:00
if err != nil {
2016-05-01 23:30:00 +00:00
return nil , err
2016-04-19 04:40:49 +00:00
}
2021-09-28 10:56:59 +00:00
client := internal . NewClientWithToken ( ctx , c . URL , c . Consumer , accessToken . Token )
2016-04-19 04:40:49 +00:00
2016-07-24 21:07:44 +00:00
user , err := client . FindCurrentUser ( )
if err != nil {
return nil , err
}
return convertUser ( user , accessToken ) , nil
2016-06-13 05:18:31 +00:00
2016-05-01 23:30:00 +00:00
}
2016-04-19 04:40:49 +00:00
2016-05-01 23:30:00 +00:00
// Auth is not supported by the Stash driver.
2021-09-28 10:56:59 +00:00
func ( * Config ) Auth ( ctx context . Context , token , secret string ) ( string , error ) {
2016-05-01 23:30:00 +00:00
return "" , fmt . Errorf ( "Not Implemented" )
2016-04-19 04:40:49 +00:00
}
2016-05-01 23:30:00 +00:00
// Teams is not supported by the Stash driver.
2021-09-28 10:56:59 +00:00
func ( * Config ) Teams ( ctx context . Context , u * model . User ) ( [ ] * model . Team , error ) {
2016-05-01 23:30:00 +00:00
var teams [ ] * model . Team
return teams , nil
2016-04-19 04:40:49 +00:00
}
2016-11-11 18:56:48 +00:00
// TeamPerm is not supported by the Stash driver.
func ( * Config ) TeamPerm ( u * model . User , org string ) ( * model . Perm , error ) {
return nil , nil
}
2021-09-28 10:56:59 +00:00
func ( c * Config ) Repo ( ctx context . Context , u * model . User , owner , name string ) ( * model . Repo , error ) {
repo , err := internal . NewClientWithToken ( ctx , c . URL , c . Consumer , u . Token ) . FindRepo ( owner , name )
2016-07-24 21:07:44 +00:00
if err != nil {
return nil , err
}
return convertRepo ( repo ) , nil
2016-06-25 23:45:33 +00:00
}
2016-04-19 04:40:49 +00:00
2021-09-28 10:56:59 +00:00
func ( c * Config ) Repos ( ctx context . Context , u * model . User ) ( [ ] * model . Repo , error ) {
repos , err := internal . NewClientWithToken ( ctx , c . URL , c . Consumer , u . Token ) . FindRepos ( )
2016-07-24 21:07:44 +00:00
if err != nil {
return nil , err
}
2017-07-14 19:58:38 +00:00
var all [ ] * model . Repo
2016-07-24 21:07:44 +00:00
for _ , repo := range repos {
2017-07-14 19:58:38 +00:00
all = append ( all , convertRepo ( repo ) )
2016-07-24 21:07:44 +00:00
}
2016-04-19 04:40:49 +00:00
2016-07-24 21:07:44 +00:00
return all , nil
2016-04-19 04:40:49 +00:00
}
2021-09-28 10:56:59 +00:00
func ( c * Config ) Perm ( ctx context . Context , u * model . User , owner , repo string ) ( * model . Perm , error ) {
client := internal . NewClientWithToken ( ctx , c . URL , c . Consumer , u . Token )
2016-06-14 03:08:56 +00:00
2016-06-25 23:45:33 +00:00
return client . FindRepoPerms ( owner , repo )
2016-04-19 04:40:49 +00:00
}
2021-09-28 10:56:59 +00:00
func ( c * Config ) File ( ctx context . Context , u * model . User , r * model . Repo , b * model . Build , f string ) ( [ ] byte , error ) {
client := internal . NewClientWithToken ( ctx , c . URL , c . Consumer , u . Token )
2016-04-19 04:40:49 +00:00
2016-08-11 20:35:47 +00:00
return client . FindFileForRepo ( r . Owner , r . Name , f , b . Ref )
2016-04-19 04:40:49 +00:00
}
2021-09-28 10:56:59 +00:00
func ( c * Config ) Dir ( ctx context . Context , u * model . User , r * model . Repo , b * model . Build , f string ) ( [ ] * remote . FileMeta , error ) {
2019-06-03 07:16:15 +00:00
return nil , fmt . Errorf ( "Not implemented" )
}
2016-06-26 05:27:09 +00:00
// Status is not supported by the bitbucketserver driver.
2021-09-28 10:56:59 +00:00
func ( c * Config ) Status ( ctx context . Context , u * model . User , r * model . Repo , b * model . Build , link string , proc * model . Proc ) error {
2016-08-14 02:06:15 +00:00
status := internal . BuildStatus {
State : convertStatus ( b . Status ) ,
Desc : convertDesc ( b . Status ) ,
2021-10-02 08:59:34 +00:00
Name : fmt . Sprintf ( "Woodpecker #%d - %s" , b . Number , b . Branch ) ,
Key : "Woodpecker" ,
2016-08-14 02:06:15 +00:00
Url : link ,
}
2021-09-28 10:56:59 +00:00
client := internal . NewClientWithToken ( ctx , c . URL , c . Consumer , u . Token )
2016-08-14 02:06:15 +00:00
return client . CreateStatus ( b . Commit , & status )
2016-04-19 04:40:49 +00:00
}
2016-06-25 23:45:33 +00:00
func ( c * Config ) Netrc ( user * model . User , r * model . Repo ) ( * model . Netrc , error ) {
2016-06-12 01:42:55 +00:00
u , err := url . Parse ( c . URL )
2016-06-13 05:18:31 +00:00
if err != nil {
return nil , err
}
2016-06-12 01:42:55 +00:00
//remove the port
tmp := strings . Split ( u . Host , ":" )
var host = tmp [ 0 ]
2016-04-19 04:40:49 +00:00
if err != nil {
2016-04-19 16:47:02 +00:00
return nil , err
2016-04-19 04:40:49 +00:00
}
return & model . Netrc {
2016-06-12 01:42:55 +00:00
Machine : host ,
2016-06-25 23:45:33 +00:00
Login : c . Username ,
Password : c . Password ,
2016-04-19 04:40:49 +00:00
} , nil
}
2021-09-28 10:56:59 +00:00
func ( c * Config ) Activate ( ctx context . Context , u * model . User , r * model . Repo , link string ) error {
client := internal . NewClientWithToken ( ctx , c . URL , c . Consumer , u . Token )
2016-06-25 23:45:33 +00:00
return client . CreateHook ( r . Owner , r . Name , link )
2016-04-19 04:40:49 +00:00
}
2021-09-28 10:56:59 +00:00
func ( c * Config ) Deactivate ( ctx context . Context , u * model . User , r * model . Repo , link string ) error {
client := internal . NewClientWithToken ( ctx , c . URL , c . Consumer , u . Token )
2016-06-25 23:45:33 +00:00
return client . DeleteHook ( r . Owner , r . Name , link )
2016-04-19 04:40:49 +00:00
}
2016-06-25 23:45:33 +00:00
func ( c * Config ) Hook ( r * http . Request ) ( * model . Repo , * model . Build , error ) {
2016-08-11 20:35:47 +00:00
return parseHook ( r , c . URL )
2016-04-19 04:40:49 +00:00
}
2016-06-26 05:27:09 +00:00
func CreateConsumer ( URL string , ConsumerKey string , PrivateKey * rsa . PrivateKey ) * oauth . Consumer {
2016-06-25 23:45:33 +00:00
consumer := oauth . NewRSAConsumer (
2016-06-26 05:27:09 +00:00
ConsumerKey ,
PrivateKey ,
2016-06-25 23:45:33 +00:00
oauth . ServiceProvider {
2016-06-26 05:27:09 +00:00
RequestTokenUrl : fmt . Sprintf ( requestTokenURL , URL ) ,
AuthorizeTokenUrl : fmt . Sprintf ( authorizeTokenURL , URL ) ,
AccessTokenUrl : fmt . Sprintf ( accessTokenURL , URL ) ,
2016-06-25 23:45:33 +00:00
HttpMethod : "POST" ,
} )
consumer . HttpClient = & http . Client {
Transport : & http . Transport {
TLSClientConfig : & tls . Config { InsecureSkipVerify : true } ,
2017-07-14 19:58:38 +00:00
Proxy : http . ProxyFromEnvironment ,
2016-06-25 23:45:33 +00:00
} ,
2016-06-14 03:08:56 +00:00
}
2016-06-25 23:45:33 +00:00
return consumer
}