Skip to content

Commit 9905388

Browse files
committed
Implement basic github API gateway
1 parent 7d2c2b1 commit 9905388

File tree

1 file changed

+110
-0
lines changed

1 file changed

+110
-0
lines changed

api/github.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package api
2+
3+
import (
4+
"net/http"
5+
"net/http/httputil"
6+
"net/url"
7+
"regexp"
8+
)
9+
10+
// GitHubGateway acts as a proxy to GitHub
11+
type GitHubGateway struct {
12+
proxy *httputil.ReverseProxy
13+
}
14+
15+
const defaultEndpoint = "https://api.github.com"
16+
17+
var pathRegexp = regexp.MustCompile("^/github/?")
18+
19+
func NewGitHubGateway() *GitHubGateway {
20+
return &GitHubGateway{
21+
proxy: &httputil.ReverseProxy{Director: director},
22+
}
23+
}
24+
25+
func director(r *http.Request) {
26+
ctx := r.Context()
27+
target := getProxyTarget(ctx)
28+
accessToken := getAccessToken(ctx)
29+
30+
targetQuery := target.RawQuery
31+
r.Host = target.Host
32+
r.URL.Scheme = target.Scheme
33+
r.URL.Host = target.Host
34+
r.URL.Path = singleJoiningSlash(target.Path, pathRegexp.ReplaceAllString(r.URL.Path, "/"))
35+
if targetQuery == "" || r.URL.RawQuery == "" {
36+
r.URL.RawQuery = targetQuery + r.URL.RawQuery
37+
} else {
38+
r.URL.RawQuery = targetQuery + "&" + r.URL.RawQuery
39+
}
40+
if _, ok := r.Header["User-Agent"]; !ok {
41+
// explicitly disable User-Agent so it's not set to default value
42+
r.Header.Set("User-Agent", "")
43+
}
44+
if r.Method != http.MethodOptions {
45+
r.Header.Set("Authorization", "Bearer "+accessToken)
46+
}
47+
}
48+
49+
func (gh *GitHubGateway) ServeHTTP(w http.ResponseWriter, r *http.Request) {
50+
ctx := r.Context()
51+
config := getConfig(ctx)
52+
if config == nil || config.GitHub.AccessToken == "" {
53+
handleError(notFoundError("No GitHub Settings Configured"), w, r)
54+
return
55+
}
56+
57+
if !gh.authenticate(w, r) {
58+
handleError(unauthorizedError("Not authorized to access this endpoint"), w, r)
59+
return
60+
}
61+
62+
var endpoint string
63+
if config.GitHub.Endpoint != "" {
64+
endpoint = config.GitHub.Endpoint
65+
} else {
66+
endpoint = defaultEndpoint
67+
}
68+
69+
target, err := url.Parse(endpoint)
70+
if err != nil {
71+
handleError(internalServerError("Unable to process GitHub endpoint"), w, r)
72+
return
73+
}
74+
ctx = withProxyTarget(ctx, target)
75+
ctx = withAccessToken(ctx, config.GitHub.AccessToken)
76+
gh.proxy.ServeHTTP(w, r.WithContext(ctx))
77+
}
78+
79+
func (gh *GitHubGateway) authenticate(w http.ResponseWriter, r *http.Request) bool {
80+
ctx := r.Context()
81+
claims := getClaims(ctx)
82+
adminRoles := getRoles(ctx)
83+
config := getConfig(ctx)
84+
85+
if claims == nil {
86+
return false
87+
}
88+
89+
if config.GitHub.Repo != "" {
90+
repoRegexp := regexp.MustCompile("^/github/repos/" + config.GitHub.Repo + "/?")
91+
if !repoRegexp.MatchString(r.URL.Path) {
92+
return false
93+
}
94+
}
95+
96+
roles, ok := claims.AppMetaData["roles"]
97+
if ok {
98+
roleStrings, _ := roles.([]interface{})
99+
for _, data := range roleStrings {
100+
role, _ := data.(string)
101+
for _, adminRole := range adminRoles {
102+
if role == adminRole.Name {
103+
return true
104+
}
105+
}
106+
}
107+
}
108+
109+
return false
110+
}

0 commit comments

Comments
 (0)