Skip to content

Commit

Permalink
Merge pull request #4 from XenitAB/auth-regex
Browse files Browse the repository at this point in the history
Change authorization to use regex
  • Loading branch information
phillebaba authored Jul 10, 2020
2 parents 4a994b8 + 720de94 commit d5b886a
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 46 deletions.
16 changes: 11 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,18 @@ func main() {
log.Fatalf("Invalid remote url: %v\n", err)
}

// Generate authorization object
auth, err := auth.GenerateAuthorization(*config)
if err != nil {
log.Fatalf("Could not generate Authorization: %v", err)
}

// Configure revers proxy and http server
proxy := httputil.NewSingleHostReverseProxy(remote)
router := mux.NewRouter()
router.HandleFunc("/readyz", readinessHandler).Methods("GET")
router.HandleFunc("/healthz", livenessHandler).Methods("GET")
router.PathPrefix("/").HandlerFunc(proxyHandler(proxy, config))
router.PathPrefix("/").HandlerFunc(proxyHandler(proxy, auth, config.Domain, config.Pat))

srv := &http.Server{
Addr: ":" + strconv.Itoa(*port),
Expand Down Expand Up @@ -89,7 +95,7 @@ func livenessHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("{\"status\": \"ok\"}"))
}

func proxyHandler(p *httputil.ReverseProxy, c *config.Configuration) func(http.ResponseWriter, *http.Request) {
func proxyHandler(p *httputil.ReverseProxy, a *auth.Authorization, domain string, pat string) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
// If basic auth is missing return error to force client to retry
username, _, ok := r.BasicAuth()
Expand All @@ -100,7 +106,7 @@ func proxyHandler(p *httputil.ReverseProxy, c *config.Configuration) func(http.R
}

// Check basic auth with local auth configuration
err := auth.IsPermitted(c, r.URL.Path, username)
err := auth.IsPermitted(a, r.URL.Path, username)
if err != nil {
log.Printf("Received unauthorized request: %v\n", err)
http.Error(w, "User not permitted", http.StatusForbidden)
Expand All @@ -109,9 +115,9 @@ func proxyHandler(p *httputil.ReverseProxy, c *config.Configuration) func(http.R

// Forward request to destination server
log.Printf("Succesfully authenticated at path: %v\n", r.URL.Path)
r.Host = c.Domain
r.Host = domain
r.Header.Del("Authorization")
patB64 := base64.StdEncoding.EncodeToString([]byte("pat:" + c.Pat))
patB64 := base64.StdEncoding.EncodeToString([]byte("pat:" + pat))
r.Header.Add("Authorization", "Basic "+patB64)

p.ServeHTTP(w, r)
Expand Down
74 changes: 48 additions & 26 deletions pkg/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,64 @@ package auth

import (
"fmt"
"strings"
"regexp"

"github.com/xenitab/azdo-proxy/pkg/config"
)

// IsPermitted checks if a specific user is permitted to access a path
func IsPermitted(c *config.Configuration, p string, t string) error {
if p == "/"+c.Organization+"/"+"_apis" {
return nil
}
type Authorization struct {
Endpoints []Endpoint
}

comp := strings.Split(p, "/")
if len(comp) < 5 {
return fmt.Errorf("Path has to few components: %v", p)
}
type Endpoint struct {
Token string
Regexes []*regexp.Regexp
}

org := comp[1]
proj := comp[2]
action := comp[3]

var repo string
if action == "_git" {
repo = comp[4]
} else if action == "_apis" {
repo = comp[6]
} else {
return fmt.Errorf("Missing action path component: %v", p)
// GeneGenerateAuthorization creates regex resources to validate tokens and endpoints
func GenerateAuthorization(c config.Configuration) (*Authorization, error) {
baseApi, err := regexp.Compile(`/` + c.Organization + `/_apis\b`)
if err != nil {
return nil, err
}

if c.Organization != org {
return fmt.Errorf("Organization do not match: expected: %v, actual: %v, path: %v", c.Organization, org, p)
auth := &Authorization{Endpoints: []Endpoint{}}
for _, r := range c.Repositories {
git, err := regexp.Compile(`/` + c.Organization + `/` + r.Project + `/_git/` + r.Name + `(/.*)?\b`)
if err != nil {
return nil, err
}

api, err := regexp.Compile(`/` + c.Organization + `/` + r.Project + `/_apis/git/repositories/` + r.Name + `(/.*)?\b`)
if err != nil {
return nil, err
}

endpoint := Endpoint{
Token: r.Token,
Regexes: []*regexp.Regexp{baseApi, git, api},
}
auth.Endpoints = append(auth.Endpoints, endpoint)
}

for _, repository := range c.Repositories {
if repository.Project == proj && repository.Name == repo && repository.Token == t {
return nil
return auth, nil
}

// IsPermitted checks if a specific user is permitted to access a path
func IsPermitted(a *Authorization, p string, t string) error {
for _, e := range a.Endpoints {
// Only check regex for matching tokens
if e.Token != t {
continue
}

// Return of a regex matches the path
for _, r := range e.Regexes {
if r.MatchString(p) {
fmt.Println(p)
fmt.Printf("Path %v matches %v regex\n", p, r.String())
return nil
}
}
}

Expand Down
48 changes: 33 additions & 15 deletions pkg/auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
"github.com/xenitab/azdo-proxy/pkg/config"
)

func baseConf() config.Configuration {
return config.Configuration{
func auth() *Authorization {
config := config.Configuration{
Domain: "",
Pat: "",
Organization: "org",
Expand All @@ -24,69 +24,87 @@ func baseConf() config.Configuration {
},
},
}

auth, err := GenerateAuthorization(config)
if err != nil {
panic(err)
}

return auth
}

func TestPermitted(t *testing.T) {
c := baseConf()
auth := auth()
token := "token"
path := "/org/proj/_git/repo"

err := IsPermitted(&c, path, token)
err := IsPermitted(auth, path, token)
if err != nil {
t.Errorf("Token should be permitted: %v", err)
}
}

func TestPermittedExtraPath(t *testing.T) {
auth := auth()
token := "token"
path := "/org/proj/_git/repo/foobar/foobar"

err := IsPermitted(auth, path, token)
if err != nil {
t.Errorf("Token should be permitted: %v", err)
}
}

func TestWrongOrg(t *testing.T) {
auth := auth()
token := "token"
path := "/org1/proj/_git/repo"
c := baseConf()

err := IsPermitted(&c, path, token)
err := IsPermitted(auth, path, token)
if err == nil {
t.Error("Token should not be permitted")
}
}

func TestToShortPath(t *testing.T) {
auth := auth()
token := "token"
path := "/foobar/foobar/foobar"
c := baseConf()

err := IsPermitted(&c, path, token)
err := IsPermitted(auth, path, token)
if err == nil {
t.Error("Token should not be permitted")
}
}

func TestMisssingProject(t *testing.T) {
auth := auth()
token := "token"
path := "/org/proj1/_git/repo"
c := baseConf()

err := IsPermitted(&c, path, token)
err := IsPermitted(auth, path, token)
if err == nil {
t.Error("Token should not be permitted")
}
}

func TestMisssingRepo(t *testing.T) {
auth := auth()
token := "token"
path := "/org/proj/_git/repo1"
c := baseConf()
path := "/org/proj/_git/repo123"

err := IsPermitted(&c, path, token)
err := IsPermitted(auth, path, token)
if err == nil {
t.Error("Token should not be permitted")
}
}

func TestInvalidToken(t *testing.T) {
auth := auth()
token := "token1"
path := "/org/proj/_git/repo"
c := baseConf()

err := IsPermitted(&c, path, token)
err := IsPermitted(auth, path, token)
if err == nil {
t.Error("Token should not be permitted")
}
Expand Down

0 comments on commit d5b886a

Please sign in to comment.