|
|
|
@ -12,6 +12,7 @@ import (
|
|
|
|
|
"net/http"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"github.com/drone/go-login/login"
|
|
|
|
|
"github.com/drone/go-login/login/internal/oauth1"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
@ -21,63 +22,38 @@ const (
|
|
|
|
|
accessTokenURL = "%s/plugins/servlet/oauth/access-token"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Options provides the Bitbucket Server (Stash)
|
|
|
|
|
// authentication options.
|
|
|
|
|
type Options struct {
|
|
|
|
|
callbackURL string
|
|
|
|
|
address string
|
|
|
|
|
consumerKey string
|
|
|
|
|
consumerSecret string
|
|
|
|
|
signer oauth1.Signer
|
|
|
|
|
client *http.Client
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func createOptions() *Options {
|
|
|
|
|
return &Options{
|
|
|
|
|
client: http.DefaultClient,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Option configures an authorization handler option.
|
|
|
|
|
type Option func(o *Options)
|
|
|
|
|
type Option func(a *Authorizer)
|
|
|
|
|
|
|
|
|
|
// WithClient configures the authorization handler with a
|
|
|
|
|
// custom http.Client.
|
|
|
|
|
func WithClient(client *http.Client) Option {
|
|
|
|
|
return func(o *Options) {
|
|
|
|
|
o.client = client
|
|
|
|
|
return func(a *Authorizer) {
|
|
|
|
|
a.client = client
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WithConsumerKey configures the authorization handler with
|
|
|
|
|
// the oauth_consumer_key.
|
|
|
|
|
func WithConsumerKey(consumerKey string) Option {
|
|
|
|
|
return func(o *Options) {
|
|
|
|
|
o.consumerKey = consumerKey
|
|
|
|
|
return func(a *Authorizer) {
|
|
|
|
|
a.consumerKey = consumerKey
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WithConsumerSecret configures the authorization handler
|
|
|
|
|
// with the oauth_consumer_secret.
|
|
|
|
|
func WithConsumerSecret(consumerSecret string) Option {
|
|
|
|
|
return func(o *Options) {
|
|
|
|
|
o.consumerSecret = consumerSecret
|
|
|
|
|
return func(a *Authorizer) {
|
|
|
|
|
a.consumerSecret = consumerSecret
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WithCallbackURL configures the authorization handler
|
|
|
|
|
// with the oauth_callback_url
|
|
|
|
|
func WithCallbackURL(callbackURL string) Option {
|
|
|
|
|
return func(o *Options) {
|
|
|
|
|
o.callbackURL = callbackURL
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WithAddress configures the authorization handler with
|
|
|
|
|
// the Bitbucket Server address.
|
|
|
|
|
func WithAddress(address string) Option {
|
|
|
|
|
return func(o *Options) {
|
|
|
|
|
o.address = strings.TrimPrefix(address, "/")
|
|
|
|
|
return func(a *Authorizer) {
|
|
|
|
|
a.callbackURL = callbackURL
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -94,32 +70,56 @@ func WithPrivateKeyFile(path string) Option {
|
|
|
|
|
// WithPrivateKey configures the authorization handler
|
|
|
|
|
// with the oauth private rsa key for signing requests.
|
|
|
|
|
func WithPrivateKey(data []byte) Option {
|
|
|
|
|
return func(o *Options) {
|
|
|
|
|
return func(a *Authorizer) {
|
|
|
|
|
p, _ := pem.Decode(data)
|
|
|
|
|
k, err := x509.ParsePKCS1PrivateKey(p.Bytes)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
o.signer = &oauth1.RSASigner{PrivateKey: k}
|
|
|
|
|
a.signer = &oauth1.RSASigner{PrivateKey: k}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// New returns a http.Handler that runs h at the completion
|
|
|
|
|
// of the Bitbucket authorization flow. The Bitbucket
|
|
|
|
|
// authorization is passed to h in the http.Request context.
|
|
|
|
|
func New(h http.Handler, opt ...Option) http.Handler {
|
|
|
|
|
opts := createOptions()
|
|
|
|
|
for _, fn := range opt {
|
|
|
|
|
fn(opts)
|
|
|
|
|
// Authorizer configures the Bitbucket Server (Stash)
|
|
|
|
|
// authorization provider.
|
|
|
|
|
type Authorizer struct {
|
|
|
|
|
callbackURL string
|
|
|
|
|
address string
|
|
|
|
|
consumerKey string
|
|
|
|
|
consumerSecret string
|
|
|
|
|
signer oauth1.Signer
|
|
|
|
|
client *http.Client
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func newDefault() *Authorizer {
|
|
|
|
|
return &Authorizer{
|
|
|
|
|
client: http.DefaultClient,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// New returns a Bitbucket Server authorization provider.
|
|
|
|
|
func New(address string, opts ...Option) login.Authorizer {
|
|
|
|
|
auther := newDefault()
|
|
|
|
|
auther.address = strings.TrimPrefix(address, "/")
|
|
|
|
|
for _, opt := range opts {
|
|
|
|
|
opt(auther)
|
|
|
|
|
}
|
|
|
|
|
return auther
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Authorize returns a http.Handler that runs h at the
|
|
|
|
|
// completion of the GitHub authorization flow. The GitHub
|
|
|
|
|
// authorization details are available to h in the
|
|
|
|
|
// http.Request context.
|
|
|
|
|
func (a *Authorizer) Authorize(h http.Handler) http.Handler {
|
|
|
|
|
return oauth1.Handler(h, &oauth1.Config{
|
|
|
|
|
Signer: opts.signer,
|
|
|
|
|
Client: opts.client,
|
|
|
|
|
ConsumerKey: opts.consumerKey,
|
|
|
|
|
ConsumerSecret: opts.consumerSecret,
|
|
|
|
|
CallbackURL: opts.callbackURL,
|
|
|
|
|
AccessTokenURL: fmt.Sprintf(accessTokenURL, opts.address),
|
|
|
|
|
AuthorizationURL: fmt.Sprintf(authorizeTokenURL, opts.address),
|
|
|
|
|
RequestTokenURL: fmt.Sprintf(requestTokenURL, opts.address),
|
|
|
|
|
Signer: a.signer,
|
|
|
|
|
Client: a.client,
|
|
|
|
|
ConsumerKey: a.consumerKey,
|
|
|
|
|
ConsumerSecret: a.consumerSecret,
|
|
|
|
|
CallbackURL: a.callbackURL,
|
|
|
|
|
AccessTokenURL: fmt.Sprintf(accessTokenURL, a.address),
|
|
|
|
|
AuthorizationURL: fmt.Sprintf(authorizeTokenURL, a.address),
|
|
|
|
|
RequestTokenURL: fmt.Sprintf(requestTokenURL, a.address),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|