Skip to content

Commit

Permalink
Policy REST API (TykTechnologies#3302)
Browse files Browse the repository at this point in the history
* REST API for policies

* amend logs in policy loader

* handler for path param

* fix path load

* resolve review issues

* tests for handler and cleanup - actually add policy to map when creating

* update mutex

Co-authored-by: Geofrey Ernest <geofreyernest@live.com>

* Fixed retrieving a single Policy (TykTechnologies#3315)

Fixes retrieving single policy

* fixed issues after rebase

* fixed test lint issues

* ts.run checked

* code cleanup

* fixed schema model

* fixed failing tests for policies

* added comments for docs

Co-authored-by: Geofrey Ernest <geofreyernest@live.com>
Co-authored-by: Sedky Abou-Shamalah <sedkyaboushamalah@gmail.com>
Co-authored-by: Milan Vasic <milan.vasic.su@gmail.com>
  • Loading branch information
4 people authored Sep 15, 2021
1 parent b2e1b3e commit bc5243e
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 24 deletions.
3 changes: 3 additions & 0 deletions cli/linter/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,9 @@
"service",
"rpc"
]
},
"policy_path": {
"type": "string"
}
}
},
Expand Down
5 changes: 4 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ 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"`
// This option is used for storing a policies if `policies.policy_source` is set to `file`.
// it should be some existing file path on hard drive
PolicyPath string `json:"policy_path"`
}

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

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

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

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

func (gw *Gateway) handleAddOrUpdatePolicy(polID string, r *http.Request) (interface{}, int) {
if gw.GetConfig().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 && r.Method == http.MethodPut {
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(gw.GetConfig().Policies.PolicyPath, newPol.ID+".json")

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("Failed to create file!"), http.StatusInternalServerError
}

action := "modified"
if r.Method == http.MethodPost {
action = "added"
gw.policiesMu.Lock()
gw.policiesByID[polID] = *newPol
gw.policiesMu.Unlock()
}

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

return response, http.StatusOK
}

func (gw *Gateway) handleDeletePolicy(polID string) (interface{}, int) {
// Generate a filename
defFilePath := filepath.Join(gw.GetConfig().Policies.PolicyPath, polID+".json")

// If it exists, delete it
if _, err := os.Stat(defFilePath); err != nil {
log.Warningf("Error describing named file: %v ", err)
return apiError("Delete failed"), http.StatusInternalServerError
}

if err := os.Remove(defFilePath); err != nil {
log.Warningf("Delete failed: %v", err)
return apiError("Delete failed"), http.StatusInternalServerError
}

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 @@ -880,7 +976,7 @@ func (gw *Gateway) handleAddOrUpdateApi(apiID string, r *http.Request, fs afero.
}

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

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

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

var obj interface{}
var code int

switch r.Method {
case http.MethodGet:
if polID != "" {
log.Debug("Requesting policy for", polID)
obj, code = gw.handleGetPolicy(polID)
} else {
log.Debug("Requesting Policy list")
obj, code = gw.handleGetPolicyList()
}
case http.MethodPost:
log.Debug("Creating new definition file")
obj, code = gw.handleAddOrUpdatePolicy(polID, r)
case http.MethodPut:
if polID != "" {
log.Debug("Updating existing Policy: ", polID)
obj, code = gw.handleAddOrUpdatePolicy(polID, r)
} else {
obj, code = apiError("Must specify an apiID to update"), http.StatusBadRequest
}
case http.MethodDelete:
if polID != "" {
log.Debug("Deleting policy for: ", polID)
obj, code = gw.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 Expand Up @@ -1209,7 +1342,7 @@ func (gw *Gateway) handleOrgAddOrUpdate(orgID string, r *http.Request) (interfac
}).Info("New organization key added or updated.")

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

Expand Down
Loading

0 comments on commit bc5243e

Please sign in to comment.