From 7937a994df1dbdecadf98d6c052dddbc2cbc4573 Mon Sep 17 00:00:00 2001 From: Captt-g <1061250120@qq.com> Date: Fri, 18 Jun 2021 22:49:27 +0800 Subject: [PATCH] feat: Add the EnforceCache nothing Signed-off-by: Captt-g <1061250120@qq.com> fix: update file fix: update file fix: update file fix: update file fix: update file fix: update file fix: dd --- Package.swift | 4 +- Sources/Casbin/APi/CoreApi.swift | 2 + Sources/Casbin/Cache/Cache.swift | 6 +- Sources/Casbin/Cache/MemoryCache.swift | 10 +- Sources/Casbin/Core/Core.swift | 39 ++++++- Sources/Casbin/Enforce+Cache.swift | 115 +++++++++++++++++++ Sources/Casbin/Enforcer.swift | 39 ++++++- Sources/Casbin/Event.swift | 6 + Sources/Casbin/Model/DefaultModel.swift | 10 +- Sources/Casbin/Rbac/DefaultRoleManager.swift | 10 +- 10 files changed, 216 insertions(+), 25 deletions(-) create mode 100644 Sources/Casbin/Enforce+Cache.swift diff --git a/Package.swift b/Package.swift index f9489c8..ed47279 100644 --- a/Package.swift +++ b/Package.swift @@ -40,6 +40,8 @@ let package = Package( .testTarget( name: "CasbinTests", - dependencies: ["Casbin"]), + dependencies: ["Casbin"], + resources: [.copy("examples")] + ), ] ) diff --git a/Sources/Casbin/APi/CoreApi.swift b/Sources/Casbin/APi/CoreApi.swift index f87e1c8..e86c152 100644 --- a/Sources/Casbin/APi/CoreApi.swift +++ b/Sources/Casbin/APi/CoreApi.swift @@ -58,6 +58,8 @@ public protocol CoreApi:EventEmitter where K == Event { func hasAutoBuildRoleLinksEnabled() -> Bool + func getCache() -> Cache? + func setCapacity(_ c: Int) } diff --git a/Sources/Casbin/Cache/Cache.swift b/Sources/Casbin/Cache/Cache.swift index eeb3d19..42876ec 100644 --- a/Sources/Casbin/Cache/Cache.swift +++ b/Sources/Casbin/Cache/Cache.swift @@ -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(key:K,as type: V.Type) -> V? where K:Hashable&Equatable - mutating func set(key:K,value:V) where K:Hashable&Equatable + func set(key:K,value:V) where K:Hashable&Equatable func has(k:K) -> Bool where K:Hashable&Equatable - mutating func clear() + func clear() } diff --git a/Sources/Casbin/Cache/MemoryCache.swift b/Sources/Casbin/Cache/MemoryCache.swift index 75a2219..7fc2d1c 100644 --- a/Sources/Casbin/Cache/MemoryCache.swift +++ b/Sources/Casbin/Cache/MemoryCache.swift @@ -14,12 +14,16 @@ import NIOConcurrencyHelpers -public struct DefaultCache:Cache { +public final class DefaultCache:Cache { + init(lru: LruCache) { + self.lru = lru + } + public func get(key: K, as type: V.Type) -> V? where K : Hashable { (lru.getValue(forKey:key as! Int) as! V) } - public mutating func set(key: K, value: V) where K : Hashable { + public func set(key: K, value: V) where K : Hashable { lru.setValue(value: value as! Bool, forKey: key as! Int) } @@ -27,7 +31,7 @@ public struct DefaultCache:Cache { get(key: k, as: Bool.self) != nil } - public mutating func clear() { + public func clear() { lru.clear() } diff --git a/Sources/Casbin/Core/Core.swift b/Sources/Casbin/Core/Core.swift index fb2a4fe..b6058ac 100644 --- a/Sources/Casbin/Core/Core.swift +++ b/Sources/Casbin/Core/Core.swift @@ -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) @@ -42,6 +43,14 @@ 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 { @@ -49,12 +58,13 @@ extension Enforcer { 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() } } @@ -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(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 { } diff --git a/Sources/Casbin/Enforce+Cache.swift b/Sources/Casbin/Enforce+Cache.swift new file mode 100644 index 0000000..343e241 --- /dev/null +++ b/Sources/Casbin/Enforce+Cache.swift @@ -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 { + 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.init(capacity: 200) + self.enforcer.storage.set(MemoryCacheKey.self, to: new) + return new + } + } + private struct MemoryCacheKey: LockKey,StorageKey { + typealias Value = LruCache + } + } + 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) + } + } + } + + +} diff --git a/Sources/Casbin/Enforcer.swift b/Sources/Casbin/Enforcer.swift index 12c2144..8454f3f 100644 --- a/Sources/Casbin/Enforcer.swift +++ b/Sources/Casbin/Enforcer.swift @@ -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() } @@ -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 @@ -296,14 +298,33 @@ extension Enforcer { } public func enforce(rvals:[Any]) -> Result { 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 { @@ -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 @@ -462,8 +491,6 @@ extension Enforcer: CoreApi { public func hasAutoBuildRoleLinksEnabled() -> Bool { autoBuildRoleLinks } - - } diff --git a/Sources/Casbin/Event.swift b/Sources/Casbin/Event.swift index d23bed9..17af349 100644 --- a/Sources/Casbin/Event.swift +++ b/Sources/Casbin/Event.swift @@ -51,6 +51,7 @@ public enum EventData: CustomStringConvertible { case ClearPolicy case ClearCache } + public protocol EventKey:Hashable & Equatable {} public protocol EventEmitter { @@ -68,3 +69,8 @@ func notifyLoggerAndWatcher(eventData:EventData,e: T) { w.update(eventData: eventData) } } + +func clearCache(eventData:EventData,e: T) { + e.logger.printMgmtLog(e: eventData) + e.getCache()?.clear() +} diff --git a/Sources/Casbin/Model/DefaultModel.swift b/Sources/Casbin/Model/DefaultModel.swift index 2d56490..8c5bec9 100644 --- a/Sources/Casbin/Model/DefaultModel.swift +++ b/Sources/Casbin/Model/DefaultModel.swift @@ -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()) { acc, x in - var acc = acc + let policy = getPolicy(sec: sec, ptype: ptype).reduce(into: Set()) { 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 { diff --git a/Sources/Casbin/Rbac/DefaultRoleManager.swift b/Sources/Casbin/Rbac/DefaultRoleManager.swift index 6f39cee..0aa30de 100644 --- a/Sources/Casbin/Rbac/DefaultRoleManager.swift +++ b/Sources/Casbin/Rbac/DefaultRoleManager.swift @@ -48,7 +48,7 @@ public final class DefaultRoleManager { } } if added { - if var cache = self.cache { + if let cache = self.cache { cache.clear() } } @@ -105,7 +105,7 @@ extension DefaultRoleManager : RoleManager { public func clear() -> Void { allDomains = [:] - if var cache = self.cache { + if let cache = self.cache { cache.clear() } } @@ -117,7 +117,7 @@ extension DefaultRoleManager : RoleManager { let role1 = createRole(name: name1, domain: domain) let role2 = createRole(name: name2, domain: domain) if !role1.addRole(role: role2) { - if var cache = cache { + if let cache = cache { cache.clear() } } @@ -131,7 +131,7 @@ extension DefaultRoleManager : RoleManager { let role1 = createRole(name: name1, domain: domain) let role2 = createRole(name: name2, domain: domain) role1.deleteRole(role: role2) - if var cache = self.cache { + if let cache = self.cache { cache.clear() } return .success(()) @@ -166,7 +166,7 @@ extension DefaultRoleManager : RoleManager { .hasRole(name: name2, hierarchyLevel: maxHierarchyLevel) } } - if var cache = self.cache { + if let cache = self.cache { cache.set(key: cacheKey, value: res) } return res