Skip to content

Commit

Permalink
add support for app_metadata 'readonly' enforcement
Browse files Browse the repository at this point in the history
  • Loading branch information
pickledish committed Jan 19, 2024
1 parent f78f64f commit 36023a3
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 0 deletions.
1 change: 1 addition & 0 deletions cmd/api/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ var ApiCmd = &cobra.Command{
r.Use(cors.Handler(corsOptions))
r.Use(jwtauth.Verifier(jwtSecret))
r.Use(util.CheckJwtMiddleware((config["PERMISSIVE_MODE"] == "true"), false))
r.Use(util.CheckReadOnlyMiddleware(config["PERMISSIVE_MODE"] == "true"))

r.Post("/site", s.CreateSite)
r.Post("/hostname", s.AddHostname)
Expand Down
1 change: 1 addition & 0 deletions cmd/git/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ var GitCmd = &cobra.Command{
r.Use(middleware.Timeout(60 * time.Second))
r.Use(util.BasicAuthJwtVerifier(jwtSecret, config["SUPABASE_URL"], config["SUPABASE_ANON_KEY"]))
r.Use(util.CheckJwtMiddleware((config["PERMISSIVE_MODE"] == "true"), true))
r.Use(util.CheckReadOnlyMiddleware(config["PERMISSIVE_MODE"] == "true"))
r.Use(mid.CreateGitInitMiddleware())
r.Use(mid.CreateGitHookMiddleware())

Expand Down
34 changes: 34 additions & 0 deletions util/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,37 @@ func CheckHostnameMiddleware(permissive bool) func(next http.Handler) http.Handl
})
}
}

// similar to the above, but enforces "readonly" field in app_metadata to disallow
// the creation or updates of sites
func CheckReadOnlyMiddleware(permissive bool) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(out http.ResponseWriter, req *http.Request) {

if permissive {

log.Printf("Permissive mode is enabled, not validating JWT tokens! SHOULD NOT SEE IN PROD")

} else {

// already checked for errors etc in the previous CheckJwtMiddleware
_, claims, _ := jwtauth.FromContext(req.Context())

readonly, err := GetReadonlyFromClaims(claims)
if err != nil {
http.Error(out, fmt.Sprintf("Unable to get readonly from JWT claims: %v", err), http.StatusBadRequest)
return
}

if (readonly) {
http.Error(out, fmt.Sprintf("User's account is readonly"), http.StatusUnauthorized)
return
}
}

// user is allowed to query this hostname, pass it through
next.ServeHTTP(out, req)
return
})
}
}
27 changes: 27 additions & 0 deletions util/claims.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,30 @@ func GetHostnamesFromClaims(claims map[string]interface{}) ([]string, error) {

return hostnamesStringArray, nil
}

// similar to the above, but gets the straightforward "readonly" field
func GetReadonlyFromClaims(claims map[string]interface{}) (bool, error) {

metadata, found1 := claims["app_metadata"]
if !found1 {
return false, fmt.Errorf("No 'app_metadata' found in JWT claims")
}

metadataMap, ok1 := metadata.(map[string]interface{})
if !ok1 {
return false, fmt.Errorf("Claims 'app_metadata' could not be parsed as map")
}

readonly, found2 := metadataMap["readonly"]
if !found2 {
// return early with no error -- only enforce readonly if field is present
return false, nil
}

readonlyString, ok2 := readonly.(bool)
if !ok2 {
return false, fmt.Errorf("Metadata 'readonly' field could not be parsed as bool")
}

return readonlyString, nil
}

0 comments on commit 36023a3

Please sign in to comment.