Skip to content

Commit

Permalink
Merging to release-5-lts: [TT-10856/TT-11593]fix quota limits not wor…
Browse files Browse the repository at this point in the history
…king with url rewrite to self (#6133) (#6168)

[TT-10856/TT-11593]fix quota limits not working with url rewrite to self
(#6133)

## **User description**
fix an issue where quota limits were not applied when URL rewrite
middleware target is using `tyk://self/<target-endpoint>/`

## Description
Quota limits were not applied when URL rewrite middleware is enabled
with target of format `tyk://self/<target-endpoint>/`.
This was happening with keys created with `access_rights` specified for
per API basis.
Creating keys from policies also filled in `access_rights` in keys. 
Having `access_rights` filled in caused `quota_renews` to be set to 0
during URL rewrite. This caused quota limit resets everytime.
Updating session before URL rewrite would fix this as the `quota_renews`
is correctly applied.
calling `mw.Base().UpdateRequestSession(r)` after every middleware
execution is okay because it will update session only if a session
update is scheduled.


https://github.com/TykTechnologies/tyk/blob/53886d044c43930b332dbe4a73cf727069df0770/gateway/middleware.go#L329

The quota related values returned in API response headers
`X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` were
wrong since a session update was scheduled only during the first hit
after a quota reset. This PR also fixes this behaviour to report the
correct quota information in those headers.

## Related Issue
https://tyktech.atlassian.net/browse/TT-10856
https://tyktech.atlassian.net/browse/TT-11593
## Motivation and Context

<!-- Why is this change required? What problem does it solve? -->

## How This Has Been Tested

<!-- Please describe in detail how you tested your changes -->
<!-- Include details of your testing environment, and the tests -->
<!-- you ran to see how your change affects other areas of the code,
etc. -->
<!-- This information is helpful for reviewers and QA. -->

## Screenshots (if appropriate)

## Types of changes

<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->

- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Refactoring or add test (improvements in base code or adds test
coverage to functionality)

## Checklist

<!-- Go over all the following points, and put an `x` in all the boxes
that apply -->
<!-- If there are no documentation updates required, mark the item as
checked. -->
<!-- Raise up any additional concerns not covered by the checklist. -->

- [ ] I ensured that the documentation is up to date
- [ ] I explained why this PR updates go.mod in detail with reasoning
why it's required
- [ ] I would like a code coverage CI quality gate exception and have
explained why


___

## **Type**
bug_fix, tests


___

## **Description**
- Fixed an issue where quota limits were not applied when URL rewrite
middleware target is using `tyk://self/<endpoint>/`.
- Added a test case to ensure quota limits are correctly enforced when
using URL rewrite to self.


___



## **Changes walkthrough**
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Bug
fix</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>middleware.go</strong><dd><code>Ensure Session Updates
After Middleware Execution</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

gateway/middleware.go
<li>Added call to <code>UpdateRequestSession</code> after middleware
execution to <br>ensure session updates, particularly for quota limits,
are applied.<br>


</details>
    

  </td>
<td><a

href="https://github.com/TykTechnologies/tyk/pull/6133/files#diff-703054910891a4db633eca0f42ed779d6b4fa75cd9b3aa4c503e681364201c1b">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>
</tr>                    
</table></td></tr><tr><td><strong>Tests</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>middleware_test.go</strong><dd><code>Add Test for Quota
Limits with URL Rewrite to Self</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></summary>
<hr>

gateway/middleware_test.go
<li>Imported <code>time</code> package for session creation.<br> <li>
Added <code>createSession</code> helper function to simplify session
creation for <br>tests.<br> <li> Added
<code>TestQuotaNotAppliedWithURLRewrite</code> to validate quota limits
are <br>correctly applied when using URL rewrite to self.<br>


</details>
    

  </td>
<td><a

href="https://github.com/TykTechnologies/tyk/pull/6133/files#diff-6a09a08e3f82cc5e9d8c6b5c8426d75ea1e5d85e15ab008fca1f512e7c49c1e6">+66/-0</a>&nbsp;
&nbsp; </td>
</tr>                    
</table></td></tr></tr></tbody></table>

___

> ✨ **PR-Agent usage**:
>Comment `/help` on the PR to get a list of all available PR-Agent tools
and their descriptions

---------

Co-authored-by: Jeffy Mathew <jeffy.mathew100@gmail.com>
  • Loading branch information
buger and jeffy-mathew authored Mar 22, 2024
1 parent 0c089ac commit 78c891c
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 3 deletions.
3 changes: 1 addition & 2 deletions gateway/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,12 @@ func (gw *Gateway) createMiddleware(actualMW TykMiddleware) func(http.Handler) h

mw.Logger().WithField("code", errCode).WithField("ns", finishTime.Nanoseconds()).Debug("Finished")

mw.Base().UpdateRequestSession(r)
// Special code, bypasses all other execution
if errCode != mwStatusRespond {
// No error, carry on...
meta["bypass"] = "1"
h.ServeHTTP(w, r)
} else {
mw.Base().UpdateRequestSession(r)
}
})
}
Expand Down
66 changes: 66 additions & 0 deletions gateway/middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io/ioutil"
"net/http"
"testing"
"time"

"github.com/stretchr/testify/assert"

Expand Down Expand Up @@ -405,3 +406,68 @@ func TestCopyAllowedURLs(t *testing.T) {
})
}
}

func createSessionWithQuota(tb testing.TB, api *apidef.APIDefinition, quotaMax, quotaRenewalRate int64) *user.SessionState {
tb.Helper()
session := &user.SessionState{
DateCreated: time.Now().Add(time.Hour * -1),
Expires: time.Now().Add(time.Hour).Unix(),
AccessRights: map[string]user.AccessDefinition{
api.APIID: {
APIName: api.Name,
APIID: api.APIID,
Versions: []string{"default"},
Limit: user.APILimit{
QuotaMax: quotaMax,
QuotaRenewalRate: quotaRenewalRate,
},
AllowanceScope: api.APIID,
},
},
OrgID: api.OrgID,
}
return session
}

func TestQuotaNotAppliedWithURLRewrite(t *testing.T) {
ts := StartTest(nil)
defer ts.Close()

specs := ts.Gw.BuildAndLoadAPI(func(spec *APISpec) {
spec.Proxy.ListenPath = "/quota-test"
spec.UseKeylessAccess = false
UpdateAPIVersion(spec, "Default", func(v *apidef.VersionInfo) {
v.ExtendedPaths.URLRewrite = []apidef.URLRewriteMeta{{
Path: "/abc",
Method: http.MethodGet,
MatchPattern: "/abc",
RewriteTo: "tyk://self/anything",
}}
})
})

authKey := "auth-key"
session := createSessionWithQuota(t, specs[0].APIDefinition, 2, 3600)
assert.NoError(t, ts.Gw.GlobalSessionManager.UpdateSession(authKey, session, 60, false))

authorization := map[string]string{
"Authorization": authKey,
}
_, _ = ts.Run(t, []test.TestCase{
{
Headers: authorization,
Path: "/quota-test/abc",
Code: http.StatusOK,
},
{
Headers: authorization,
Path: "/quota-test/abc",
Code: http.StatusOK,
},
{
Headers: authorization,
Path: "/quota-test/abc",
Code: http.StatusForbidden,
},
}...)
}
5 changes: 4 additions & 1 deletion gateway/session_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,10 @@ func (l *SessionLimiter) RedisQuotaExceeded(r *http.Request, currentSession *use
return false
}

defer func() {
ctxScheduleSessionUpdate(r)
}()

quotaScope := ""
if scope != "" {
quotaScope = scope + "-"
Expand Down Expand Up @@ -321,7 +325,6 @@ func (l *SessionLimiter) RedisQuotaExceeded(r *http.Request, currentSession *use
// If this is a new Quota period, ensure we let the end user know
if qInt == 1 {
quotaRenews = time.Now().Unix() + quotaRenewalRate
ctxScheduleSessionUpdate(r)
}

// If not, pass and set the values of the session to quotamax - counter
Expand Down

0 comments on commit 78c891c

Please sign in to comment.