Skip to content

Commit

Permalink
feat: add azure basic auth verification (#191)
Browse files Browse the repository at this point in the history
  • Loading branch information
raulcabello authored Jul 30, 2024
1 parent e24c5f0 commit 2fe5d09
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 3 deletions.
46 changes: 43 additions & 3 deletions azuredevops/azuredevops.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import (

// parse errors
var (
ErrInvalidHTTPMethod = errors.New("invalid HTTP Method")
ErrParsingPayload = errors.New("error parsing payload")
ErrInvalidHTTPMethod = errors.New("invalid HTTP Method")
ErrParsingPayload = errors.New("error parsing payload")
ErrBasicAuthVerificationFailed = errors.New("basic auth verification failed")
)

// Event defines an Azure DevOps server hook event type
Expand All @@ -29,13 +30,38 @@ const (
GitPushEventType Event = "git.push"
)

// Option is a configuration option for the webhook
type Option func(*Webhook) error

// Options is a namespace var for configuration options
var Options = WebhookOptions{}

// WebhookOptions is a namespace for configuration option methods
type WebhookOptions struct{}

// BasicAuth verifies payload using basic auth
func (WebhookOptions) BasicAuth(username, password string) Option {
return func(hook *Webhook) error {
hook.username = username
hook.password = password
return nil
}
}

// Webhook instance contains all methods needed to process events
type Webhook struct {
username string
password string
}

// New creates and returns a WebHook instance
func New() (*Webhook, error) {
func New(options ...Option) (*Webhook, error) {
hook := new(Webhook)
for _, opt := range options {
if err := opt(hook); err != nil {
return nil, errors.New("Error applying Option")
}
}
return hook, nil
}

Expand All @@ -46,6 +72,10 @@ func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error)
_ = r.Body.Close()
}()

if !hook.verifyBasicAuth(r) {
return nil, ErrBasicAuthVerificationFailed
}

if r.Method != http.MethodPost {
return nil, ErrInvalidHTTPMethod
}
Expand Down Expand Up @@ -78,3 +108,13 @@ func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error)
return nil, fmt.Errorf("unknown event %s", pl.EventType)
}
}

func (hook Webhook) verifyBasicAuth(r *http.Request) bool {
// skip validation if username or password was not provided
if hook.username == "" && hook.password == "" {
return true
}
username, password, ok := r.BasicAuth()

return ok && username == hook.username && password == hook.password
}
66 changes: 66 additions & 0 deletions azuredevops/azuredevops_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package azuredevops

import (
"bytes"
"fmt"
"github.com/stretchr/testify/assert"
"log"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -117,3 +120,66 @@ func TestWebhooks(t *testing.T) {
})
}
}

func TestParseBasicAuth(t *testing.T) {
const validUser = "validUser"
const validPass = "pass123"
tests := []struct {
name string
webhookUser string
webhookPass string
reqUser string
reqPass string
expectedErr error
}{
{
name: "valid basic auth",
webhookUser: validUser,
webhookPass: validPass,
reqUser: validUser,
reqPass: validPass,
expectedErr: fmt.Errorf("unknown event "), // no event passed, so this is expected
},
{
name: "no basic auth provided",
expectedErr: fmt.Errorf("unknown event "), // no event passed, so this is expected
},
{
name: "invalid basic auth",
webhookUser: validUser,
webhookPass: validPass,
reqUser: "fakeUser",
reqPass: "fakePass",
expectedErr: ErrBasicAuthVerificationFailed,
},
}

for _, tt := range tests {
h := Webhook{
username: tt.webhookUser,
password: tt.webhookPass,
}
body := []byte(`{}`)
r, err := http.NewRequest(http.MethodPost, "", bytes.NewBuffer(body))
assert.NoError(t, err)
r.SetBasicAuth(tt.reqUser, tt.reqPass)

p, err := h.Parse(r)

assert.Equal(t, err, tt.expectedErr)
assert.Nil(t, p)
}
}

func TestBasicAuth(t *testing.T) {
const user = "user"
const pass = "pass123"

opt := Options.BasicAuth(user, pass)
h := &Webhook{}
err := opt(h)

assert.NoError(t, err)
assert.Equal(t, h.username, user)
assert.Equal(t, h.password, pass)
}

0 comments on commit 2fe5d09

Please sign in to comment.