snapshot of https://github.com/casbin/casbin.git (master, 2026-09-15; blobs > 131072B omitted)
This commit is contained in:
188
enforcer_cached.go
Normal file
188
enforcer_cached.go
Normal file
@@ -0,0 +1,188 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package casbin
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/casbin/casbin/v3/persist/cache"
|
||||
)
|
||||
|
||||
// CachedEnforcer wraps Enforcer and provides decision cache.
|
||||
type CachedEnforcer struct {
|
||||
*Enforcer
|
||||
expireTime time.Duration
|
||||
cache cache.Cache
|
||||
enableCache int32
|
||||
locker *sync.RWMutex
|
||||
}
|
||||
|
||||
type CacheableParam interface {
|
||||
GetCacheKey() string
|
||||
}
|
||||
|
||||
// NewCachedEnforcer creates a cached enforcer via file or DB.
|
||||
func NewCachedEnforcer(params ...interface{}) (*CachedEnforcer, error) {
|
||||
e := &CachedEnforcer{}
|
||||
var err error
|
||||
e.Enforcer, err = NewEnforcer(params...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e.enableCache = 1
|
||||
e.cache, _ = cache.NewDefaultCache()
|
||||
e.locker = new(sync.RWMutex)
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// EnableCache determines whether to enable cache on Enforce(). When enableCache is enabled, cached result (true | false) will be returned for previous decisions.
|
||||
func (e *CachedEnforcer) EnableCache(enableCache bool) {
|
||||
var enabled int32
|
||||
if enableCache {
|
||||
enabled = 1
|
||||
}
|
||||
atomic.StoreInt32(&e.enableCache, enabled)
|
||||
}
|
||||
|
||||
// Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
|
||||
// if rvals is not string , ignore the cache.
|
||||
func (e *CachedEnforcer) Enforce(rvals ...interface{}) (bool, error) {
|
||||
if atomic.LoadInt32(&e.enableCache) == 0 {
|
||||
return e.Enforcer.Enforce(rvals...)
|
||||
}
|
||||
|
||||
key, ok := e.getKey(rvals...)
|
||||
if !ok {
|
||||
return e.Enforcer.Enforce(rvals...)
|
||||
}
|
||||
|
||||
if res, err := e.getCachedResult(key); err == nil {
|
||||
return res, nil
|
||||
} else if err != cache.ErrNoSuchKey {
|
||||
return res, err
|
||||
}
|
||||
|
||||
res, err := e.Enforcer.Enforce(rvals...)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
err = e.setCachedResult(key, res, e.expireTime)
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (e *CachedEnforcer) LoadPolicy() error {
|
||||
if atomic.LoadInt32(&e.enableCache) != 0 {
|
||||
if err := e.cache.Clear(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return e.Enforcer.LoadPolicy()
|
||||
}
|
||||
|
||||
func (e *CachedEnforcer) RemovePolicy(params ...interface{}) (bool, error) {
|
||||
if atomic.LoadInt32(&e.enableCache) != 0 {
|
||||
key, ok := e.getKey(params...)
|
||||
if ok {
|
||||
if err := e.cache.Delete(key); err != nil && err != cache.ErrNoSuchKey {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return e.Enforcer.RemovePolicy(params...)
|
||||
}
|
||||
|
||||
func (e *CachedEnforcer) RemovePolicies(rules [][]string) (bool, error) {
|
||||
if len(rules) != 0 {
|
||||
if atomic.LoadInt32(&e.enableCache) != 0 {
|
||||
for _, rule := range rules {
|
||||
irule := make([]interface{}, len(rule))
|
||||
for i, param := range rule {
|
||||
irule[i] = param
|
||||
}
|
||||
key, _ := e.getKey(irule...)
|
||||
if err := e.cache.Delete(key); err != nil && err != cache.ErrNoSuchKey {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return e.Enforcer.RemovePolicies(rules)
|
||||
}
|
||||
|
||||
func (e *CachedEnforcer) getCachedResult(key string) (res bool, err error) {
|
||||
e.locker.Lock()
|
||||
defer e.locker.Unlock()
|
||||
return e.cache.Get(key)
|
||||
}
|
||||
|
||||
func (e *CachedEnforcer) SetExpireTime(expireTime time.Duration) {
|
||||
e.expireTime = expireTime
|
||||
}
|
||||
|
||||
func (e *CachedEnforcer) SetCache(c cache.Cache) {
|
||||
e.cache = c
|
||||
}
|
||||
|
||||
func (e *CachedEnforcer) setCachedResult(key string, res bool, extra ...interface{}) error {
|
||||
e.locker.Lock()
|
||||
defer e.locker.Unlock()
|
||||
return e.cache.Set(key, res, extra...)
|
||||
}
|
||||
|
||||
func (e *CachedEnforcer) getKey(params ...interface{}) (string, bool) {
|
||||
return GetCacheKey(params...)
|
||||
}
|
||||
|
||||
// InvalidateCache deletes all the existing cached decisions.
|
||||
func (e *CachedEnforcer) InvalidateCache() error {
|
||||
e.locker.Lock()
|
||||
defer e.locker.Unlock()
|
||||
return e.cache.Clear()
|
||||
}
|
||||
|
||||
func GetCacheKey(params ...interface{}) (string, bool) {
|
||||
key := strings.Builder{}
|
||||
for _, param := range params {
|
||||
switch typedParam := param.(type) {
|
||||
case string:
|
||||
key.WriteString(typedParam)
|
||||
case CacheableParam:
|
||||
key.WriteString(typedParam.GetCacheKey())
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
key.WriteString("$$")
|
||||
}
|
||||
return key.String(), true
|
||||
}
|
||||
|
||||
// ClearPolicy clears all policy.
|
||||
func (e *CachedEnforcer) ClearPolicy() {
|
||||
if atomic.LoadInt32(&e.enableCache) != 0 {
|
||||
if err := e.cache.Clear(); err != nil {
|
||||
// Logger has been removed - error is ignored
|
||||
return
|
||||
}
|
||||
}
|
||||
e.Enforcer.ClearPolicy()
|
||||
}
|
||||
Reference in New Issue
Block a user