Skip to content

Commit

Permalink
REST API for policies
Browse files Browse the repository at this point in the history
  • Loading branch information
joshblakeley authored and vaske committed Sep 13, 2021
1 parent 84289db commit d7a6ac9
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 4 deletions.
3 changes: 2 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ type PoliciesConfig struct {
// If you set this value to `true`, then the id parameter in a stored policy (or imported policy using the Dashboard API), will be used instead of the internal ID.
//
// This option should only be used when moving an installation to a new database.
AllowExplicitPolicyID bool `json:"allow_explicit_policy_id"`
AllowExplicitPolicyID bool `json:"allow_explicit_policy_id"`
PolicyPath string `json:"policy_path"`
}

type DBAppConfOptionsConfig struct {
Expand Down
134 changes: 134 additions & 0 deletions gateway/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,103 @@ func (gw *Gateway) handleRemoveSortedSetRange(keyName, scoreFrom, scoreTo string
return gw.GlobalSessionManager.Store().RemoveSortedSetRange(keyName, scoreFrom, scoreTo)
}

func handleGetPolicy(polID string) (interface{}, int) {
if pol := getPolicy(polID); pol.ID != "" {
return user.Policy{}, http.StatusOK
}

log.WithFields(logrus.Fields{
"prefix": "policy",
"polID": polID,
}).Error("Policy doesn't exist.")
return apiError("Policy not found"), http.StatusNotFound
}

func handleGetPolicyList() (interface{}, int) {
apisMu.RLock()
defer apisMu.RUnlock()
polIDList := make([]user.Policy, len(policiesByID))
c := 0
for _, pol := range policiesByID {
polIDList[c] = pol
c++
}
return polIDList, http.StatusOK
}

func handleAddOrUpdatePolicy(polID string, r *http.Request) (interface{}, int) {
if config.Global().Policies.PolicySource == "service" {
log.Error("Rejected new policy due to PolicySource = service")
return apiError("Due to enabled service policy source, please use the Dashboard API"), http.StatusInternalServerError
}

newPol := &user.Policy{}
if err := json.NewDecoder(r.Body).Decode(newPol); err != nil {
log.Error("Couldn't decode new policy object: ", err)
return apiError("Request malformed"), http.StatusBadRequest
}

if polID != "" && newPol.ID != polID {
log.Error("PUT operation on different IDs")
return apiError("Request ID does not match that in policy! For Update operations these must match."), http.StatusBadRequest
}

// Create a filename
polFilePath := filepath.Join(config.Global().Policies.PolicyRecordName, newPol.ID+".json")

// If it exists, delete it
if _, err := os.Stat(polFilePath); err == nil {
log.Warning("Policy with this ID already exists, deleting file...")
os.Remove(polFilePath)
}

// unmarshal the object into the file
asByte, err := json.MarshalIndent(newPol, "", " ")
if err != nil {
log.Error("Marshalling of policy failed: ", err)
return apiError("Marshalling failed"), http.StatusInternalServerError
}

if err := ioutil.WriteFile(polFilePath, asByte, 0644); err != nil {
log.Error("Failed to create file! - ", err)
return apiError("File object creation failed, write error"), http.StatusInternalServerError
}

action := "modified"
if r.Method == "POST" {
action = "added"
}

response := apiModifyKeySuccess{
Key: newPol.ID,
Status: "ok",
Action: action,
}

return response, http.StatusOK
}

func handleDeletePolicy(polID string) (interface{}, int) {
// Generate a filename
defFilePath := filepath.Join(config.Global().Policies.PolicyRecordName, polID+".json")

// If it exists, delete it
if _, err := os.Stat(defFilePath); err != nil {
log.Warning("File does not exist! ", err)
return apiError("Delete failed"), http.StatusInternalServerError
}

os.Remove(defFilePath)

response := apiModifyKeySuccess{
Key: polID,
Status: "ok",
Action: "deleted",
}

return response, http.StatusOK
}

func (gw *Gateway) handleGetAPIList() (interface{}, int) {
gw.apisMu.RLock()
defer gw.apisMu.RUnlock()
Expand Down Expand Up @@ -943,6 +1040,43 @@ func (gw *Gateway) handleDeleteAPI(apiID string) (interface{}, int) {
return response, http.StatusOK
}

func polHandler(w http.ResponseWriter, r *http.Request) {
polID := mux.Vars(r)["polID"]

var obj interface{}
var code int

switch r.Method {
case "GET":
if polID != "" {
log.Debug("Requesting policy for", polID)
obj, code = handleGetPolicy(polID)
} else {
log.Debug("Requesting Policy list")
obj, code = handleGetPolicyList()
}
case "POST":
log.Debug("Creating new definition file")
obj, code = handleAddOrUpdatePolicy(polID, r)
case "PUT":
if polID != "" {
log.Debug("Updating existing Policy: ", polID)
obj, code = handleAddOrUpdatePolicy(polID, r)
} else {
obj, code = apiError("Must specify an apiID to update"), http.StatusBadRequest
}
case "DELETE":
if polID != "" {
log.Debug("Deleting policy for: ", polID)
obj, code = handleDeletePolicy(polID)
} else {
obj, code = apiError("Must specify an apiID to delete"), http.StatusBadRequest
}
}

doJSONWrite(w, code, obj)
}

func (gw *Gateway) apiHandler(w http.ResponseWriter, r *http.Request) {
apiID := mux.Vars(r)["apiID"]
oasTyped := r.FormValue("type") == "oas"
Expand Down
22 changes: 22 additions & 0 deletions gateway/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io/ioutil"
"net/http"
"os"
"path/filepath"

"github.com/jensneuse/graphql-go-tools/pkg/graphql"

Expand Down Expand Up @@ -76,6 +77,27 @@ func LoadPoliciesFromFile(filePath string) map[string]user.Policy {
return policies
}

func LoadPoliciesFromDir(dir string) map[string]user.Policy {
policies := make(map[string]user.Policy)
// Grab json files from directory
paths, _ := filepath.Glob(filepath.Join(dir, "*.json"))
for _, path := range paths {
log.Info("Loading policy from ", path)
f, err := os.Open(path)
if err != nil {
log.Error("Couldn't open policy file from dir: ", err)
continue
}
pol := &user.Policy{}
if err := json.NewDecoder(f).Decode(pol); err != nil {
log.Error("[RPC] --> Couldn't unmarshal api configuration: ", err)
}
f.Close()
policies[pol.ID] = *pol
}
return policies
}

// LoadPoliciesFromDashboard will connect and download Policies from a Tyk Dashboard instance.
func (gw *Gateway) LoadPoliciesFromDashboard(endpoint, secret string, allowExplicit bool) map[string]user.Policy {

Expand Down
20 changes: 17 additions & 3 deletions gateway/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,13 @@ func (gw *Gateway) getApiSpec(apiID string) *APISpec {
return spec
}

func getPolicy(polID string) user.Policy {
apisMu.RLock()
pol := policiesByID[polID]
apisMu.RUnlock()
return pol
}

func (gw *Gateway) apisByIDLen() int {
gw.apisMu.RLock()
defer gw.apisMu.RUnlock()
Expand Down Expand Up @@ -469,12 +476,18 @@ func (gw *Gateway) syncPolicies() (count int, err error) {
mainLog.Debug("Using Policies from RPC")
pols, err = gw.LoadPoliciesFromRPC(gw.GetConfig().SlaveOptions.RPCKey)
default:
// this is the only case now where we need a policy record name
if gw.GetConfig().Policies.PolicyRecordName == "" {
//if policy path defined we want to allow use of the REST API
if gw.GetConfig().Policies.PolicyPath != "" {
pols = LoadPoliciesFromDir(gw.GetConfig().Policies.PolicyPath)

} else if gw.GetConfig().Policies.PolicyRecordName == "" {
// old way of doing things before REST Api added
// this is the only case now where we need a policy record name
mainLog.Debug("No policy record name defined, skipping...")
return 0, nil
} else {
pols = LoadPoliciesFromFile(gw.GetConfig().Policies.PolicyRecordName)
}
pols = LoadPoliciesFromFile(gw.GetConfig().Policies.PolicyRecordName)
}
mainLog.Infof("Policies found (%d total):", len(pols))
for id := range pols {
Expand Down Expand Up @@ -569,6 +582,7 @@ func (gw *Gateway) loadControlAPIEndpoints(muxer *mux.Router) {
r.HandleFunc("/apis", gw.apiHandler).Methods("GET", "POST", "PUT", "DELETE")
r.HandleFunc("/apis/{apiID}", gw.apiHandler).Methods("GET", "POST", "PUT", "DELETE")
r.HandleFunc("/health", gw.healthCheckhandler).Methods("GET")
r.HandleFunc("/policies", polHandler).Methods("GET", "POST", "PUT", "DELETE")
r.HandleFunc("/oauth/clients/create", gw.createOauthClient).Methods("POST")
r.HandleFunc("/oauth/clients/{apiID}/{keyName:[^/]*}", gw.oAuthClientHandler).Methods("PUT")
r.HandleFunc("/oauth/clients/{apiID}/{keyName:[^/]*}/rotate", gw.rotateOauthClientHandler).Methods("PUT")
Expand Down

0 comments on commit d7a6ac9

Please sign in to comment.