| 1 | import Foundation |
| 2 | |
| 3 | /// Abstraction over session persistence. |
| 4 | /// |
| 5 | /// `AuthCore` is platform-agnostic, so it does not assume a Keychain (or any |
| 6 | /// particular storage backend). Provide a conforming type to persist sessions |
| 7 | /// however is appropriate for your platform: |
| 8 | /// |
| 9 | /// - On Apple platforms, `SmbCloudCredentialsManager` (from the `SmbCloudAuth` |
| 10 | /// product) stores sessions in the Keychain. |
| 11 | /// - For tests, scripts, or servers, use ``SmbCloudInMemoryCredentialsStore``. |
| 12 | /// - On Linux / Windows / Android, implement this protocol on top of your |
| 13 | /// platform's secure storage (e.g. libsecret, DPAPI, Android Keystore). |
| 14 | public protocol SmbCloudCredentialsStore: Sendable { |
| 15 | /// Persists (or replaces) the stored session. |
| 16 | func store(_ session: SmbCloudSession) throws |
| 17 | /// Returns the stored session, if any. |
| 18 | func current() throws -> SmbCloudSession? |
| 19 | /// Removes any stored session. |
| 20 | func clear() throws |
| 21 | } |
| 22 | |
| 23 | extension SmbCloudCredentialsStore { |
| 24 | /// Returns the stored session only if it is still valid within `leeway` seconds. |
| 25 | public func currentValidSession(leeway: TimeInterval = 60) throws -> SmbCloudSession? { |
| 26 | guard let session = try current() else { |
| 27 | return nil |
| 28 | } |
| 29 | |
| 30 | return session.isValid(leeway: leeway) ? session : nil |
| 31 | } |
| 32 | |
| 33 | /// Whether a valid (non-expired) session is currently stored. |
| 34 | public func hasValidSession(leeway: TimeInterval = 60) throws -> Bool { |
| 35 | try currentValidSession(leeway: leeway) != nil |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | /// A thread-safe, in-memory ``SmbCloudCredentialsStore``. |
| 40 | /// |
| 41 | /// Sessions live only for the lifetime of the process. This is handy for unit |
| 42 | /// tests, command-line tools, and server-side flows, and it works identically |
| 43 | /// on every platform AuthCore supports. |
| 44 | public final class SmbCloudInMemoryCredentialsStore: SmbCloudCredentialsStore, @unchecked Sendable { |
| 45 | private let lock = NSLock() |
| 46 | private var session: SmbCloudSession? |
| 47 | |
| 48 | public init(session: SmbCloudSession? = nil) { |
| 49 | self.session = session?.storedRepresentation() |
| 50 | } |
| 51 | |
| 52 | public func store(_ session: SmbCloudSession) throws { |
| 53 | lock.lock() |
| 54 | defer { lock.unlock() } |
| 55 | self.session = session.storedRepresentation() |
| 56 | } |
| 57 | |
| 58 | public func current() throws -> SmbCloudSession? { |
| 59 | lock.lock() |
| 60 | defer { lock.unlock() } |
| 61 | return session |
| 62 | } |
| 63 | |
| 64 | public func clear() throws { |
| 65 | lock.lock() |
| 66 | defer { lock.unlock() } |
| 67 | session = nil |
| 68 | } |
| 69 | } |