diff --git a/api.go b/api.go index bae09231893b..e42783995a18 100644 --- a/api.go +++ b/api.go @@ -94,7 +94,7 @@ func getSpecForOrg(orgID string) *APISpec { return nil } -func checkAndApplyTrialPeriod(keyName, apiId string, newSession *user.SessionState) { +func checkAndApplyTrialPeriod(keyName, apiId string, newSession *user.SessionState, isHashed bool) { // Check the policies to see if we are forcing an expiry on the key for _, polID := range newSession.PolicyIDs() { policiesMu.RLock() @@ -106,7 +106,7 @@ func checkAndApplyTrialPeriod(keyName, apiId string, newSession *user.SessionSta // Are we foring an expiry? if policy.KeyExpiresIn > 0 { // We are, does the key exist? - _, found := getKeyDetail(keyName, apiId, false) + _, found := getKeyDetail(keyName, apiId, isHashed) if !found { // this is a new key, lets expire it newSession.Expires = time.Now().Unix() + policy.KeyExpiresIn @@ -115,17 +115,17 @@ func checkAndApplyTrialPeriod(keyName, apiId string, newSession *user.SessionSta } } -func applyPoliciesAndSave(keyName string, session *user.SessionState, spec *APISpec) error { +func applyPoliciesAndSave(keyName string, session *user.SessionState, spec *APISpec, isHashed bool) error { // use basic middleware to apply policies to key/session (it also saves it) mw := BaseMiddleware{ Spec: spec, } - if err := mw.ApplyPolicies(keyName, session); err != nil { + if err := mw.ApplyPolicies(session); err != nil { return err } lifetime := session.Lifetime(spec.SessionLifetime) - if err := spec.SessionManager.UpdateSession(keyName, session, lifetime, false); err != nil { + if err := spec.SessionManager.UpdateSession(keyName, session, lifetime, isHashed); err != nil { return err } @@ -142,7 +142,7 @@ func resetAPILimits(accessRights map[string]user.AccessDefinition) { } } -func doAddOrUpdate(keyName string, newSession *user.SessionState, dontReset bool) error { +func doAddOrUpdate(keyName string, newSession *user.SessionState, dontReset bool, isHashed bool) error { newSession.LastUpdated = strconv.Itoa(int(time.Now().Unix())) if len(newSession.AccessRights) > 0 { @@ -164,18 +164,18 @@ func doAddOrUpdate(keyName string, newSession *user.SessionState, dontReset bool }).Error("Could not add key for this API ID, API doesn't exist.") return errors.New("API must be active to add keys") } - checkAndApplyTrialPeriod(keyName, apiId, newSession) + checkAndApplyTrialPeriod(keyName, apiId, newSession, isHashed) // Lets reset keys if they are edited by admin if !apiSpec.DontSetQuotasOnCreate { // Reset quote by default if !dontReset { - apiSpec.SessionManager.ResetQuota(keyName, newSession) + apiSpec.SessionManager.ResetQuota(keyName, newSession, isHashed) newSession.QuotaRenews = time.Now().Unix() + newSession.QuotaRenewalRate } // apply polices (if any) and save key - if err := applyPoliciesAndSave(keyName, newSession, apiSpec); err != nil { + if err := applyPoliciesAndSave(keyName, newSession, apiSpec, isHashed); err != nil { return err } } @@ -191,13 +191,13 @@ func doAddOrUpdate(keyName string, newSession *user.SessionState, dontReset bool defer apisMu.RUnlock() for _, spec := range apisByID { if !dontReset { - spec.SessionManager.ResetQuota(keyName, newSession) + spec.SessionManager.ResetQuota(keyName, newSession, isHashed) newSession.QuotaRenews = time.Now().Unix() + newSession.QuotaRenewalRate } - checkAndApplyTrialPeriod(keyName, spec.APIID, newSession) + checkAndApplyTrialPeriod(keyName, spec.APIID, newSession, isHashed) // apply polices (if any) and save key - if err := applyPoliciesAndSave(keyName, newSession, spec); err != nil { + if err := applyPoliciesAndSave(keyName, newSession, spec, isHashed); err != nil { return err } } @@ -243,7 +243,7 @@ func getKeyDetail(key, apiID string, hashed bool) (user.SessionState, bool) { return sessionManager.SessionDetail(key, hashed) } -func handleAddOrUpdate(keyName string, r *http.Request) (interface{}, int) { +func handleAddOrUpdate(keyName string, r *http.Request, isHashed bool) (interface{}, int) { var newSession user.SessionState if err := json.NewDecoder(r.Body).Decode(&newSession); err != nil { log.Error("Couldn't decode new session object: ", err) @@ -285,7 +285,7 @@ func handleAddOrUpdate(keyName string, r *http.Request) (interface{}, int) { suppressReset := r.URL.Query().Get("suppress_reset") == "1" - if err := doAddOrUpdate(keyName, &newSession, suppressReset); err != nil { + if err := doAddOrUpdate(keyName, &newSession, suppressReset, isHashed); err != nil { return apiError("Failed to create key, ensure security settings are correct."), http.StatusInternalServerError } @@ -309,7 +309,11 @@ func handleAddOrUpdate(keyName string, r *http.Request) (interface{}, int) { // add key hash for newly created key if config.Global().HashKeys && r.Method == http.MethodPost { - response.KeyHash = storage.HashKey(keyName) + if isHashed { + response.KeyHash = keyName + } else { + response.KeyHash = storage.HashKey(keyName) + } } return response, http.StatusOK @@ -358,7 +362,7 @@ func handleGetDetail(sessionKey, apiID string, byHash bool) (interface{}, int) { } mw := BaseMiddleware{Spec: spec} - mw.ApplyPolicies(sessionKey, &session) + mw.ApplyPolicies(&session) log.WithFields(logrus.Fields{ "prefix": "api", @@ -435,7 +439,7 @@ func handleDeleteKey(keyName, apiID string) (interface{}, int) { apisMu.RLock() for _, spec := range apisByID { spec.SessionManager.RemoveSession(keyName, false) - spec.SessionManager.ResetQuota(keyName, &user.SessionState{}) + spec.SessionManager.ResetQuota(keyName, &user.SessionState{}, false) } apisMu.RUnlock() @@ -456,7 +460,7 @@ func handleDeleteKey(keyName, apiID string) (interface{}, int) { } sessionManager.RemoveSession(keyName, false) - sessionManager.ResetQuota(keyName, &user.SessionState{}) + sessionManager.ResetQuota(keyName, &user.SessionState{}, false) statusObj := apiModifyKeySuccess{ Key: keyName, @@ -669,7 +673,7 @@ func keyHandler(w http.ResponseWriter, r *http.Request) { switch r.Method { case "POST", "PUT": - obj, code = handleAddOrUpdate(keyName, r) + obj, code = handleAddOrUpdate(keyName, r, isHashed) case "GET": if keyName != "" { @@ -826,7 +830,7 @@ func handleOrgAddOrUpdate(keyName string, r *http.Request) (interface{}, int) { } if r.URL.Query().Get("reset_quota") == "1" { - sessionManager.ResetQuota(keyName, newSession) + sessionManager.ResetQuota(keyName, newSession, false) newSession.QuotaRenews = time.Now().Unix() + newSession.QuotaRenewalRate rawKey := QuotaKeyPrefix + storage.HashKey(keyName) @@ -1011,15 +1015,15 @@ func createKeyHandler(w http.ResponseWriter, r *http.Request) { for apiID := range newSession.AccessRights { apiSpec := getApiSpec(apiID) if apiSpec != nil { - checkAndApplyTrialPeriod(newKey, apiID, newSession) + checkAndApplyTrialPeriod(newKey, apiID, newSession, false) // If we have enabled HMAC checking for keys, we need to generate a secret for the client to use if !apiSpec.DontSetQuotasOnCreate { // Reset quota by default - apiSpec.SessionManager.ResetQuota(newKey, newSession) + apiSpec.SessionManager.ResetQuota(newKey, newSession, false) newSession.QuotaRenews = time.Now().Unix() + newSession.QuotaRenewalRate } // apply polices (if any) and save key - if err := applyPoliciesAndSave(newKey, newSession, apiSpec); err != nil { + if err := applyPoliciesAndSave(newKey, newSession, apiSpec, false); err != nil { doJSONWrite(w, http.StatusInternalServerError, apiError("Failed to create key - "+err.Error())) return } @@ -1027,7 +1031,7 @@ func createKeyHandler(w http.ResponseWriter, r *http.Request) { // Use fallback sessionManager := FallbackKeySesionManager newSession.QuotaRenews = time.Now().Unix() + newSession.QuotaRenewalRate - sessionManager.ResetQuota(newKey, newSession) + sessionManager.ResetQuota(newKey, newSession, false) err := sessionManager.UpdateSession(newKey, newSession, -1, false) if err != nil { doJSONWrite(w, http.StatusInternalServerError, apiError("Failed to create key - "+err.Error())) @@ -1052,14 +1056,14 @@ func createKeyHandler(w http.ResponseWriter, r *http.Request) { apisMu.RLock() defer apisMu.RUnlock() for _, spec := range apisByID { - checkAndApplyTrialPeriod(newKey, spec.APIID, newSession) + checkAndApplyTrialPeriod(newKey, spec.APIID, newSession, false) if !spec.DontSetQuotasOnCreate { // Reset quote by default - spec.SessionManager.ResetQuota(newKey, newSession) + spec.SessionManager.ResetQuota(newKey, newSession, false) newSession.QuotaRenews = time.Now().Unix() + newSession.QuotaRenewalRate } // apply polices (if any) and save key - if err := applyPoliciesAndSave(newKey, newSession, spec); err != nil { + if err := applyPoliciesAndSave(newKey, newSession, spec, false); err != nil { doJSONWrite(w, http.StatusInternalServerError, apiError("Failed to create key - "+err.Error())) return } diff --git a/api_test.go b/api_test.go index 0dd4b555120b..0bea2f953148 100644 --- a/api_test.go +++ b/api_test.go @@ -352,6 +352,15 @@ func testHashKeyHandlerHelper(t *testing.T, expectedHashSize int) { Code: 200, BodyMatch: fmt.Sprintf(`"key_hash":"%s"`, myKeyHash), }, + // Update key by hash value with specifying hashed=true + { + Method: "PUT", + Path: "/tyk/keys/" + myKeyHash + "?hashed=true", + Data: string(withAccessJSON), + AdminAuth: true, + Code: 200, + BodyMatch: fmt.Sprintf(`"key":"%s"`, myKeyHash), + }, // get one key by key name { Method: "GET", diff --git a/auth_manager.go b/auth_manager.go index 9629e6591f1c..01916639124f 100644 --- a/auth_manager.go +++ b/auth_manager.go @@ -36,7 +36,7 @@ type SessionHandler interface { SessionDetail(keyName string, hashed bool) (user.SessionState, bool) Sessions(filter string) []string Store() storage.Handler - ResetQuota(string, *user.SessionState) + ResetQuota(string, *user.SessionState, bool) Stop() } @@ -183,15 +183,20 @@ func (b *DefaultSessionManager) Store() storage.Handler { return b.store } -func (b *DefaultSessionManager) ResetQuota(keyName string, session *user.SessionState) { - rawKey := QuotaKeyPrefix + storage.HashKey(keyName) +func (b *DefaultSessionManager) ResetQuota(keyName string, session *user.SessionState, isHashed bool) { + origKeyName := keyName + if !isHashed { + keyName = storage.HashKey(keyName) + } + + rawKey := QuotaKeyPrefix + keyName log.WithFields(logrus.Fields{ "prefix": "auth-mgr", - "inbound-key": obfuscateKey(keyName), + "inbound-key": obfuscateKey(origKeyName), "key": rawKey, }).Info("Reset quota for key.") - rateLimiterSentinelKey := RateLimitKeyPrefix + storage.HashKey(keyName) + ".BLOCKED" + rateLimiterSentinelKey := RateLimitKeyPrefix + keyName + ".BLOCKED" // Clear the rate limiter go b.store.DeleteRawKey(rateLimiterSentinelKey) // Fix the raw key diff --git a/middleware.go b/middleware.go index 17d5b3f48f3f..6062bf30f645 100644 --- a/middleware.go +++ b/middleware.go @@ -232,7 +232,7 @@ func (t BaseMiddleware) UpdateRequestSession(r *http.Request) bool { // ApplyPolicies will check if any policies are loaded. If any are, it // will overwrite the session state to use the policy values. -func (t BaseMiddleware) ApplyPolicies(key string, session *user.SessionState) error { +func (t BaseMiddleware) ApplyPolicies(session *user.SessionState) error { rights := session.AccessRights if rights == nil { rights = make(map[string]user.AccessDefinition) @@ -420,7 +420,7 @@ func (t BaseMiddleware) CheckSessionAndIdentityForValidKey(key string, r *http.R if found { t.Logger().Debug("--> Key found in local cache") session := cachedVal.(user.SessionState) - if err := t.ApplyPolicies(key, &session); err != nil { + if err := t.ApplyPolicies(&session); err != nil { t.Logger().Error(err) return session, false } @@ -440,7 +440,7 @@ func (t BaseMiddleware) CheckSessionAndIdentityForValidKey(key string, r *http.R } // Check for a policy, if there is a policy, pull it and overwrite the session values - if err := t.ApplyPolicies(key, &session); err != nil { + if err := t.ApplyPolicies(&session); err != nil { t.Logger().Error(err) return session, false } @@ -462,7 +462,7 @@ func (t BaseMiddleware) CheckSessionAndIdentityForValidKey(key string, r *http.R } // Check for a policy, if there is a policy, pull it and overwrite the session values - if err := t.ApplyPolicies(key, &session); err != nil { + if err := t.ApplyPolicies(&session); err != nil { t.Logger().Error(err) return session, false } diff --git a/mw_js_plugin.go b/mw_js_plugin.go index 8626572fa07d..ad91aca81fc1 100644 --- a/mw_js_plugin.go +++ b/mw_js_plugin.go @@ -564,7 +564,7 @@ func (j *JSVM) LoadTykJSApi() { return otto.Value{} } - doAddOrUpdate(apiKey, &newSession, suppressReset == "1") + doAddOrUpdate(apiKey, &newSession, suppressReset == "1", false) return otto.Value{} }) diff --git a/mw_jwt.go b/mw_jwt.go index d680671dd657..65c1ca7e4387 100644 --- a/mw_jwt.go +++ b/mw_jwt.go @@ -325,7 +325,7 @@ func (k *JWTMiddleware) processCentralisedJWT(r *http.Request, token *jwt.Token) newSession.SetPolicies(polIDs...) // multiple policies assigned to a key, check if it is applicable - if err := k.ApplyPolicies(sessionID, &newSession); err != nil { + if err := k.ApplyPolicies(&newSession); err != nil { k.reportLoginFailure(baseFieldData, r) k.Logger().WithError(err).Error("Could not several policies from scope-claim mapping to JWT to session") return errors.New("Key not authorized: could not apply several policies"), http.StatusForbidden @@ -390,7 +390,7 @@ func (k *JWTMiddleware) processCentralisedJWT(r *http.Request, token *jwt.Token) } // apply new policy to session and update session session.SetPolicies(policyID) - if err := k.ApplyPolicies(sessionID, &session); err != nil { + if err := k.ApplyPolicies(&session); err != nil { k.reportLoginFailure(baseFieldData, r) k.Logger().WithError(err).Error("Could not apply new policy from JWT to session") return errors.New("Key not authorized: could not apply new policy"), http.StatusForbidden diff --git a/mw_jwt_test.go b/mw_jwt_test.go index a888f4b09660..692a2cc390e9 100644 --- a/mw_jwt_test.go +++ b/mw_jwt_test.go @@ -482,7 +482,7 @@ func prepareJWTSessionRSAWithRawSourceOnWithClientID(isBench bool) string { } session := createJWTSessionWithRSAWithPolicy(policyID) - spec.SessionManager.ResetQuota(tokenID, session) + spec.SessionManager.ResetQuota(tokenID, session, false) spec.SessionManager.UpdateSession(tokenID, session, 60, false) jwtToken := createJWKToken(func(t *jwt.Token) { diff --git a/mw_openid.go b/mw_openid.go index 7b67b1c53c46..c2b3f6abda16 100644 --- a/mw_openid.go +++ b/mw_openid.go @@ -209,7 +209,7 @@ func (k *OpenIDMW) ProcessRequest(w http.ResponseWriter, r *http.Request, _ inte } // apply new policy to session if any and update session session.SetPolicies(policiesToApply...) - if err := k.ApplyPolicies(sessionID, &session); err != nil { + if err := k.ApplyPolicies(&session); err != nil { k.Logger().WithError(err).Error("Could not apply new policy from OIDC client to session") return errors.New("Key not authorized: could not apply new policy"), http.StatusForbidden } diff --git a/policy_test.go b/policy_test.go index 7b316b451ddf..51afc0b18925 100644 --- a/policy_test.go +++ b/policy_test.go @@ -307,7 +307,7 @@ func testPrepareApplyPolicies() (*BaseMiddleware, []testApplyPoliciesData) { policiesMu.Lock() policiesByID["acl3"] = newPolicy policiesMu.Unlock() - err := bmid.ApplyPolicies("", s) + err := bmid.ApplyPolicies(s) if err != nil { t.Fatalf("couldn't apply policy: %s", err.Error()) } @@ -408,7 +408,7 @@ func TestApplyPolicies(t *testing.T) { sess := &user.SessionState{} sess.SetPolicies(tc.policies...) errStr := "" - if err := bmid.ApplyPolicies("", sess); err != nil { + if err := bmid.ApplyPolicies(sess); err != nil { errStr = err.Error() } if tc.errMatch == "" && errStr != "" { @@ -433,7 +433,7 @@ func BenchmarkApplyPolicies(b *testing.B) { for _, tc := range tests { sess := &user.SessionState{} sess.SetPolicies(tc.policies...) - bmid.ApplyPolicies("", sess) + bmid.ApplyPolicies(sess) } } }