Skip to content

Commit

Permalink
Merge pull request #22 from captt-g/master
Browse files Browse the repository at this point in the history
feat: Add the EnforceCache
  • Loading branch information
hsluoyz authored Jun 23, 2021
2 parents 977bc49 + 7937a99 commit ed3f32f
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 25 deletions.
4 changes: 3 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ let package = Package(

.testTarget(
name: "CasbinTests",
dependencies: ["Casbin"]),
dependencies: ["Casbin"],
resources: [.copy("examples")]
),
]
)
2 changes: 2 additions & 0 deletions Sources/Casbin/APi/CoreApi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public protocol CoreApi:EventEmitter where K == Event {

func hasAutoBuildRoleLinksEnabled() -> Bool

func getCache() -> Cache?
func setCapacity(_ c: Int)
}


Expand Down
6 changes: 3 additions & 3 deletions Sources/Casbin/Cache/Cache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.

public protocol Cache {
public protocol Cache:AnyObject {

func setCapacity(_ c: Int)

func get<K,V>(key:K,as type: V.Type) -> V? where K:Hashable&Equatable

mutating func set<K,V>(key:K,value:V) where K:Hashable&Equatable
func set<K,V>(key:K,value:V) where K:Hashable&Equatable

func has<K>(k:K) -> Bool where K:Hashable&Equatable

mutating func clear()
func clear()

}
10 changes: 7 additions & 3 deletions Sources/Casbin/Cache/MemoryCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,24 @@

import NIOConcurrencyHelpers

public struct DefaultCache:Cache {
public final class DefaultCache:Cache {
init(lru: LruCache<Int, Bool>) {
self.lru = lru
}

public func get<K, V>(key: K, as type: V.Type) -> V? where K : Hashable {
(lru.getValue(forKey:key as! Int) as! V)
}

public mutating func set<K, V>(key: K, value: V) where K : Hashable {
public func set<K, V>(key: K, value: V) where K : Hashable {
lru.setValue(value: value as! Bool, forKey: key as! Int)
}

public func has<K>(k: K) -> Bool where K : Hashable {
get(key: k, as: Bool.self) != nil
}

public mutating func clear() {
public func clear() {
lru.clear()
}

Expand Down
39 changes: 38 additions & 1 deletion Sources/Casbin/Core/Core.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import NIOConcurrencyHelpers
extension Enforcer {
internal var core: Core {
.init(ef: self)
Expand Down Expand Up @@ -42,19 +43,28 @@ extension Enforcer {
set {self.core.storage.eft = newValue}
}

public var locks: Locks {
self.core.storage.locks
}

public var sync: Lock {
self.locks.main
}

public struct Core {
final class Storage {

var eft: Effector
var watcher:Watcher?
var rm: RoleManager
var fm: FunctionMap

var locks: Locks
init() {
self.eft = DefaultEffector.init()
self.rm = DefaultRoleManager.init(maxHierarchyLevel: 10)
self.fm = FunctionMap.default()
self.watcher = nil
self.locks = .init()
}

}
Expand All @@ -74,4 +84,31 @@ extension Enforcer {
self.ef.storage[Key.self] = .init()
}
}

public final class Locks {
public let main: Lock
var storage: [ObjectIdentifier: Lock]

init() {
self.main = .init()
self.storage = [:]
}

public func lock<Key>(for key: Key.Type) -> Lock
where Key: LockKey
{
self.main.lock()
defer { self.main.unlock() }
if let existing = self.storage[ObjectIdentifier(Key.self)] {
return existing
} else {
let new = Lock()
self.storage[ObjectIdentifier(Key.self)] = new
return new
}
}
}
}


public protocol LockKey { }
115 changes: 115 additions & 0 deletions Sources/Casbin/Enforce+Cache.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//
// Enforce+Cache.swift
// Casbin
//
// Licensed 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.

import Foundation
import NIO
/// Custom cache
/// ```
/// extension Application.Caches.Provider {
/// public static var memory: Self {
/// .init {
/// $0.caches.use { $0.caches.memory }
/// }
/// }
/// e.caches.use(.memory)
extension Enforcer {

public var caches: Caches {
.init(enforcer: self)
}
public struct Caches {
public let enforcer: Enforcer

public struct Provider {
let run: (Enforcer) -> ()

public init(_ run: @escaping (Enforcer) -> ()) {
self.run = run
}
public static var memory:Self {
.init {
$0.caches.use {
$0.caches.memory
}
}
}
}
public func use(_ provider: Provider) {
provider.run(self.enforcer)
}
private struct CacheKey: StorageKey {
typealias Value = CacheStorage
}
final class CacheStorage {
var makeCache:((Enforcer) -> Cache)?
init() {}
}
var cacheStorage: CacheStorage {
guard let storage = self.enforcer.storage[CacheKey.self] else {
let storage = CacheStorage.init()
self.enforcer.storage[CacheKey.self] = storage
return storage
}
return storage
}
public func use(_ makeCache: @escaping (Enforcer) -> Cache) {
self.cacheStorage.makeCache = makeCache
}

public var memory: Cache {
DefaultCache.init(lru: memoryStorage)
}
private var memoryStorage: LruCache<Int,Bool> {
let lock = self.enforcer.locks.lock(for: MemoryCacheKey.self)
lock.lock()
defer { lock.unlock() }
if let existing = self.enforcer.storage.get(MemoryCacheKey.self) {
return existing
} else {
let new = LruCache<Int,Bool>.init(capacity: 200)
self.enforcer.storage.set(MemoryCacheKey.self, to: new)
return new
}
}
private struct MemoryCacheKey: LockKey,StorageKey {
typealias Value = LruCache<Int,Bool>
}
}
public var cache: Cache? {
get {
guard let makeCache = self.caches.cacheStorage.makeCache else {
return nil
}
return makeCache(self)
}
set { }
}
}

extension Enforcer {
func cachedPrivateEnforce(rvals:[Any],cacheKey:Int) -> Result<(Bool,Bool,[Int]?),Error> {
if let authorized = self.cache?.get(key: cacheKey, as: Bool.self) {
return .success((authorized, true, nil))
} else {
return self.privateEnforce(rvals: rvals).map { (authorized, indices) in
self.cache?.set(key: cacheKey, value: authorized)
return (authorized,false,indices)
}
}
}


}
39 changes: 33 additions & 6 deletions Sources/Casbin/Enforcer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ public final class Enforcer {
}

try registerGFunctions().get()
if self.cache != nil {
self.on(e: Event.ClearCache, f: clearCache)
}
self.on(e: .PolicyChange, f: notifyLoggerAndWatcher)
try loadPolicy().wait()
}
Expand Down Expand Up @@ -258,7 +261,6 @@ extension Enforcer {
scope[token] = pvals[index]
}
let eval = makeExpression(scope: scope, parsed: ex)
print(eval.description)
let evalResult:Bool = try eval.evaluate()

let eft:Effect = { () -> Effect in
Expand Down Expand Up @@ -296,14 +298,33 @@ extension Enforcer {
}
public func enforce(rvals:[Any]) -> Result<Bool,Error> {
do {
var _authorized:Bool
var _cached: Bool = false
var _indices:[Int]?

if self.cache != nil {
var hasher = Hasher.init()
rvals.forEach {
if let hash = $0 as? AnyHashable {
hasher.combine(hash)
}
}
let cacheKey = hasher.finalize()
let (authorized,cached,indices) = try cachedPrivateEnforce(rvals: rvals, cacheKey: cacheKey).get()
_authorized = authorized
_cached = cached
_indices = indices
}
let (authorized,indices) = try privateEnforce(rvals: rvals).get()
_authorized = authorized
_indices = indices
if logEnabled {
self.logger.printEnforceLog(
rvals: rvals.compactMap({ $0 as? String }),
cached: false,
authorized: authorized,
cached: _cached,
authorized: _authorized,
level: logger.logLevel)
if let indices = indices {
if let indices = _indices {
if case let .success(ast) = self.getAst(key: "p") {
let allRules = ast.policy
let rules = indices.compactMap {
Expand Down Expand Up @@ -346,6 +367,14 @@ extension Enforcer {
}

extension Enforcer: CoreApi {
public func getCache() -> Cache? {
self.cache
}

public func setCapacity(_ c: Int) {
self.cache?.setCapacity(c)
}

public var enableLog: Bool {
get {
self.logEnabled
Expand Down Expand Up @@ -462,8 +491,6 @@ extension Enforcer: CoreApi {
public func hasAutoBuildRoleLinksEnabled() -> Bool {
autoBuildRoleLinks
}


}


6 changes: 6 additions & 0 deletions Sources/Casbin/Event.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public enum EventData: CustomStringConvertible {
case ClearPolicy
case ClearCache
}

public protocol EventKey:Hashable & Equatable {}

public protocol EventEmitter {
Expand All @@ -68,3 +69,8 @@ func notifyLoggerAndWatcher<T:CoreApi>(eventData:EventData,e: T) {
w.update(eventData: eventData)
}
}

func clearCache<T:CoreApi>(eventData:EventData,e: T) {
e.logger.printMgmtLog(e: eventData)
e.getCache()?.clear()
}
10 changes: 4 additions & 6 deletions Sources/Casbin/Model/DefaultModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,14 +205,12 @@ extension DefaultModel:Model {
getPolicy(sec: sec, ptype: ptype).contains(rule)
}
public func getValuesForFieldInPolicy(sec:String,ptype:String,fieldIndex:Int) -> [String] {
let policy = getPolicy(sec: sec, ptype: ptype).reduce(Set<String>()) { acc, x in
var acc = acc
let policy = getPolicy(sec: sec, ptype: ptype).reduce(into: Set<String>()) { acc, x in
acc.insert(x[fieldIndex])
return acc
}
return Array(policy)
}
return Array(policy)
}


public func removePolicy(sec:String,ptype:String,rule: [String]) -> Bool {
if let ast = model[sec]?[ptype] {
ast.policy.removeAll {
Expand Down
Loading

0 comments on commit ed3f32f

Please sign in to comment.