Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add azure basic auth verification #191

Merged
merged 2 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}