Skip to content

Commit e70ec30

Browse files
committed
ORGANIC-467. Refactored authorization/authenication out of backends into auth.go. Got it compiled.
1 parent 914448d commit e70ec30

File tree

7 files changed

+78
-132
lines changed

7 files changed

+78
-132
lines changed

README.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ for GitLab:
3030
/repos/:owner/:name/tree/
3131
```
3232

33-
**Running git-gateway to test locally**
33+
**Running `git-gateway`**
3434
**(Do not merge this section back to the open source project)**
3535
**(Do not deploy it to production. It is a Proof of Concept has has not been secured. See @TODO items in code.**
3636
**(the instruction assume Okta, and github.com)**
@@ -58,3 +58,20 @@ for GitLab:
5858
change `backend.name` value to `git-gateway`
5959
change `backend.gateway_url` value to `http://localhost:8087`
6060
14. run `content-cms` following the README.md
61+
62+
**Develop, Build and Run git-gateway**
63+
64+
1. Follow instructions 1 - 10 in previous "Running `git-gateway`" section
65+
2. Run these commands once:
66+
```
67+
docker build -t netlify/git-gateway:latest .
68+
docker run --rm --env-file my.env --net localdev -p 127.0.0.1:8087:8087 --expose 8087 -ti -v $PWD:/go/src/github.com/netlify/git-gateway --entrypoint '/bin/sh' --user root netlify/git-gateway:latest
69+
cd /go/src/github.com/netlify/git-gateway
70+
make deps
71+
```
72+
3. Run these commands after edit:
73+
```
74+
make build && ./git-gateway
75+
```
76+
4. `<ctrl> + c` to stop
77+

api/api.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ var bearerRegexp = regexp.MustCompile(`^(?:B|b)earer (\S+$)`)
2727
type API struct {
2828
handler http.Handler
2929
db storage.Connection
30+
auth Auth
3031
config *conf.GlobalConfiguration
3132
version string
3233
}
@@ -58,7 +59,8 @@ func NewAPI(globalConfig *conf.GlobalConfiguration, db storage.Connection) *API
5859

5960
// NewAPIWithVersion creates a new REST API using the specified version
6061
func NewAPIWithVersion(ctx context.Context, globalConfig *conf.GlobalConfiguration, db storage.Connection, version string) *API {
61-
api := &API{config: globalConfig, db: db, version: version}
62+
auth := NewAuthWithVersion(ctx, globalConfig, version)
63+
api := &API{config: globalConfig, db: db, auth: *auth, version: version}
6264

6365
xffmw, _ := xff.Default()
6466

@@ -75,10 +77,10 @@ func NewAPIWithVersion(ctx context.Context, globalConfig *conf.GlobalConfigurati
7577
r.Use(api.loadJWSSignatureHeader)
7678
r.Use(api.loadInstanceConfig)
7779
}
78-
r.With(api.requireAuthentication).Mount("/github", NewGitHubGateway())
79-
r.With(api.requireAuthentication).Mount("/gitlab", NewGitLabGateway())
80-
r.With(api.requireAuthentication).Mount("/bitbucket", NewBitBucketGateway())
81-
r.With(api.requireAuthentication).Get("/settings", api.Settings)
80+
r.With(api.auth.authenticate).Mount("/github", NewGitHubGateway())
81+
r.With(api.auth.authenticate).Mount("/gitlab", NewGitLabGateway())
82+
r.With(api.auth.authenticate).Mount("/bitbucket", NewBitBucketGateway())
83+
r.With(api.auth.authenticate).Get("/settings", api.Settings)
8284
})
8385

8486
if globalConfig.MultiInstanceMode {

api/auth.go

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,21 @@ package api
22

33
import (
44
"context"
5+
"errors"
56
"net/http"
67

8+
"github.com/netlify/git-gateway/conf"
79
"github.com/sirupsen/logrus"
810
"github.com/okta/okta-jwt-verifier-golang"
911
)
1012

11-
// requireAuthentication checks incoming requests for tokens presented using the Authorization header
12-
func (a *API) requireAuthentication(w http.ResponseWriter, r *http.Request) (context.Context, error) {
13+
type Auth struct {
14+
config *conf.GlobalConfiguration
15+
version string
16+
}
17+
18+
// authenicate checks incoming requests for tokens presented using the Authorization header
19+
func (a *Auth) authenticate(w http.ResponseWriter, r *http.Request) (context.Context, error) {
1320
logrus.Info("Getting auth token")
1421
token, err := a.extractBearerToken(w, r)
1522
if err != nil {
@@ -20,7 +27,48 @@ func (a *API) requireAuthentication(w http.ResponseWriter, r *http.Request) (con
2027
return a.parseJWTClaims(token, r)
2128
}
2229

23-
func (a *API) extractBearerToken(w http.ResponseWriter, r *http.Request) (string, error) {
30+
// authorize checks incoming requests for roles data in tokens that is parsed and verified by prior authentication step
31+
func (a *Auth) authorize(w http.ResponseWriter, r *http.Request) (context.Context, error) {
32+
ctx := r.Context()
33+
claims := getClaims(ctx)
34+
config := getConfig(ctx)
35+
36+
logrus.Infof("authenticate context: %v+", ctx)
37+
if claims == nil {
38+
return nil, errors.New("Access to endpoint not allowed: no claims found in Bearer token")
39+
}
40+
41+
if !allowedRegexp.MatchString(r.URL.Path) {
42+
return nil, errors.New("Access to endpoint not allowed: this part of GitHub's API has been restricted")
43+
}
44+
45+
if len(config.Roles) == 0 {
46+
return ctx, nil
47+
}
48+
49+
roles, ok := claims.AppMetaData["roles"]
50+
if ok {
51+
roleStrings, _ := roles.([]interface{})
52+
for _, data := range roleStrings {
53+
role, _ := data.(string)
54+
for _, adminRole := range config.Roles {
55+
if role == adminRole {
56+
return ctx, nil
57+
}
58+
}
59+
}
60+
}
61+
62+
return nil, errors.New("Access to endpoint not allowed: your role doesn't allow access")
63+
}
64+
65+
func NewAuthWithVersion(ctx context.Context, globalConfig *conf.GlobalConfiguration, version string) *Auth {
66+
auth := &Auth{config: globalConfig, version: version}
67+
68+
return auth
69+
}
70+
71+
func (a *Auth) extractBearerToken(w http.ResponseWriter, r *http.Request) (string, error) {
2472
authHeader := r.Header.Get("Authorization")
2573
if authHeader == "" {
2674
return "", unauthorizedError("This endpoint requires a Bearer token")
@@ -34,7 +82,7 @@ func (a *API) extractBearerToken(w http.ResponseWriter, r *http.Request) (string
3482
return matches[1], nil
3583
}
3684

37-
func (a *API) parseJWTClaims(bearer string, r *http.Request) (context.Context, error) {
85+
func (a *Auth) parseJWTClaims(bearer string, r *http.Request) (context.Context, error) {
3886
// Reimplemented to use Okta lib
3987
// Original validation only work for HS256 algo,
4088
// Okta supports RS256 only which requires public key downloading and caching (key rotation)

api/bitbucket.go

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"compress/gzip"
66
"context"
77
"encoding/json"
8-
"errors"
98
"io"
109
"io/ioutil"
1110
"net/http"
@@ -118,11 +117,6 @@ func (bb *BitBucketGateway) ServeHTTP(w http.ResponseWriter, r *http.Request) {
118117
return
119118
}
120119

121-
if err := bb.authenticate(w, r); err != nil {
122-
handleError(unauthorizedError(err.Error()), w, r)
123-
return
124-
}
125-
126120
endpoint := config.BitBucket.Endpoint
127121
apiURL := singleJoiningSlash(endpoint, "/repositories/"+config.BitBucket.Repo)
128122
target, err := url.Parse(apiURL)
@@ -142,39 +136,6 @@ func (bb *BitBucketGateway) ServeHTTP(w http.ResponseWriter, r *http.Request) {
142136
bb.proxy.ServeHTTP(w, r.WithContext(ctx))
143137
}
144138

145-
func (bb *BitBucketGateway) authenticate(w http.ResponseWriter, r *http.Request) error {
146-
ctx := r.Context()
147-
claims := getClaims(ctx)
148-
config := getConfig(ctx)
149-
150-
if claims == nil {
151-
return errors.New("Access to endpoint not allowed: no claims found in Bearer token")
152-
}
153-
154-
if !bitbucketAllowedRegexp.MatchString(r.URL.Path) {
155-
return errors.New("Access to endpoint not allowed: this part of BitBucket's API has been restricted")
156-
}
157-
158-
if len(config.Roles) == 0 {
159-
return nil
160-
}
161-
162-
roles, ok := claims.AppMetaData["roles"]
163-
if ok {
164-
roleStrings, _ := roles.([]interface{})
165-
for _, data := range roleStrings {
166-
role, _ := data.(string)
167-
for _, adminRole := range config.Roles {
168-
if role == adminRole {
169-
return nil
170-
}
171-
}
172-
}
173-
}
174-
175-
return errors.New("Access to endpoint not allowed: your role doesn't allow access")
176-
}
177-
178139
func rewriteBitBucketLink(link, endpointAPIURL, proxyAPIURL string) string {
179140
return proxyAPIURL + strings.TrimPrefix(link, endpointAPIURL)
180141
}

api/github.go

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package api
22

33
import (
4-
"errors"
54
"net/http"
65
"net/http/httputil"
76
"net/url"
@@ -60,11 +59,6 @@ func (gh *GitHubGateway) ServeHTTP(w http.ResponseWriter, r *http.Request) {
6059
return
6160
}
6261

63-
if err := gh.authenticate(w, r); err != nil {
64-
handleError(unauthorizedError(err.Error()), w, r)
65-
return
66-
}
67-
6862
endpoint := config.GitHub.Endpoint
6963
apiURL := singleJoiningSlash(endpoint, "/repos/"+config.GitHub.Repo)
7064
target, err := url.Parse(apiURL)
@@ -80,43 +74,6 @@ func (gh *GitHubGateway) ServeHTTP(w http.ResponseWriter, r *http.Request) {
8074
gh.proxy.ServeHTTP(w, r.WithContext(ctx))
8175
}
8276

83-
func (gh *GitHubGateway) authenticate(w http.ResponseWriter, r *http.Request) error {
84-
ctx := r.Context()
85-
claims := getClaims(ctx)
86-
config := getConfig(ctx)
87-
88-
log := getLogEntry(r)
89-
log.Infof("authenticate context: %v+", ctx)
90-
if claims == nil {
91-
// @TODO? WARNING: the check should be done in auth.go, imo.
92-
// Having the jwt in the context (and thus, sent to github.com) is not necessary
93-
// return errors.New("Access to endpoint not allowed: no claims found in Bearer token")
94-
}
95-
96-
if !allowedRegexp.MatchString(r.URL.Path) {
97-
return errors.New("Access to endpoint not allowed: this part of GitHub's API has been restricted")
98-
}
99-
100-
if len(config.Roles) == 0 {
101-
return nil
102-
}
103-
104-
roles, ok := claims.AppMetaData["roles"]
105-
if ok {
106-
roleStrings, _ := roles.([]interface{})
107-
for _, data := range roleStrings {
108-
role, _ := data.(string)
109-
for _, adminRole := range config.Roles {
110-
if role == adminRole {
111-
return nil
112-
}
113-
}
114-
}
115-
}
116-
117-
return errors.New("Access to endpoint not allowed: your role doesn't allow access")
118-
}
119-
12077
type GitHubTransport struct{}
12178

12279
func (t *GitHubTransport) RoundTrip(r *http.Request) (*http.Response, error) {

api/gitlab.go

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package api
22

33
import (
4-
"errors"
54
"net/http"
65
"net/http/httputil"
76
"net/url"
@@ -79,11 +78,6 @@ func (gl *GitLabGateway) ServeHTTP(w http.ResponseWriter, r *http.Request) {
7978
return
8079
}
8180

82-
if err := gl.authenticate(w, r); err != nil {
83-
handleError(unauthorizedError(err.Error()), w, r)
84-
return
85-
}
86-
8781
endpoint := config.GitLab.Endpoint
8882
// repos in the form of userName/repoName must be encoded as
8983
// userName%2FrepoName
@@ -99,39 +93,6 @@ func (gl *GitLabGateway) ServeHTTP(w http.ResponseWriter, r *http.Request) {
9993
gl.proxy.ServeHTTP(w, r.WithContext(ctx))
10094
}
10195

102-
func (gl *GitLabGateway) authenticate(w http.ResponseWriter, r *http.Request) error {
103-
ctx := r.Context()
104-
claims := getClaims(ctx)
105-
config := getConfig(ctx)
106-
107-
if claims == nil {
108-
return errors.New("Access to endpoint not allowed: no claims found in Bearer token")
109-
}
110-
111-
if !gitlabAllowedRegexp.MatchString(r.URL.Path) {
112-
return errors.New("Access to endpoint not allowed: this part of GitLab's API has been restricted")
113-
}
114-
115-
if len(config.Roles) == 0 {
116-
return nil
117-
}
118-
119-
roles, ok := claims.AppMetaData["roles"]
120-
if ok {
121-
roleStrings, _ := roles.([]interface{})
122-
for _, data := range roleStrings {
123-
role, _ := data.(string)
124-
for _, adminRole := range config.Roles {
125-
if role == adminRole {
126-
return nil
127-
}
128-
}
129-
}
130-
}
131-
132-
return errors.New("Access to endpoint not allowed: your role doesn't allow access")
133-
}
134-
13596
var gitlabLinkRegex = regexp.MustCompile("<(.*?)>")
13697
var gitlabLinkRelRegex = regexp.MustCompile("rel=\"(.*?)\"")
13798

api/middleware.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func (a *API) verifyOperatorRequest(w http.ResponseWriter, req *http.Request) (c
8080
}
8181

8282
func (a *API) extractOperatorRequest(w http.ResponseWriter, req *http.Request) (context.Context, string, error) {
83-
token, err := a.extractBearerToken(w, req)
83+
token, err := a.auth.extractBearerToken(w, req)
8484
if err != nil {
8585
return nil, token, err
8686
}

0 commit comments

Comments
 (0)