Skip to content

Commit 92fc190

Browse files
committed
Initial submission
1 parent 8b6610e commit 92fc190

File tree

9 files changed

+465
-0
lines changed

9 files changed

+465
-0
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//
2+
// LCCalendarAuthorizer.swift
3+
// LCPermissionsKit
4+
//
5+
// Created by Liu Chuan on 2023/1/17.
6+
//
7+
8+
import Cocoa
9+
import EventKit
10+
11+
/// 日历权限 授权器
12+
class LCCalendarAuthorizer: NSObject {
13+
14+
// 事件实体类型,默认为事件
15+
private var entityType: EKEntityType
16+
17+
/// 创建事件权限授权器实例
18+
static func events() -> LCCalendarAuthorizer {
19+
return LCCalendarAuthorizer(entityType: .event)
20+
}
21+
22+
/// 创建提醒事项权限授权器实例
23+
static func reminders() -> LCCalendarAuthorizer {
24+
return LCCalendarAuthorizer(entityType: .reminder)
25+
}
26+
27+
/// 初始化方法,设置实体类型
28+
///
29+
/// - Parameter entityType: 要授权的日历实体类型
30+
init(entityType: EKEntityType) {
31+
self.entityType = entityType
32+
}
33+
34+
/// 将`事件权限授权状态`转换为`自定义权限授权状态`
35+
///
36+
/// - Parameter status: EKAuthorizationStatus,事件权限的授权状态
37+
/// - Returns: LCAuthorizationStatus,对应的自定义权限授权状态
38+
private func authorizationStatus(from status: EKAuthorizationStatus) -> LCAuthorizationStatus {
39+
switch status {
40+
case .denied, .restricted:
41+
return .denied
42+
case .authorized:
43+
return .authorized
44+
case .notDetermined:
45+
return .notDetermined
46+
@unknown default:
47+
return .notDetermined
48+
}
49+
}
50+
}
51+
52+
//MARK: - LCAuthorizer
53+
extension LCCalendarAuthorizer: LCAuthorizer {
54+
55+
func authorizationStatus() -> LCAuthorizationStatus {
56+
// 获取事件(日历)权限的授权状态
57+
let status = EKEventStore.authorizationStatus(for: self.entityType)
58+
return authorizationStatus(from: status)
59+
60+
}
61+
// 请求事件(日历)权限的授权
62+
func requestAuthorization(withCompletion completionHandler: @escaping (LCAuthorizationStatus) -> Void) {
63+
// 创建事件(日历)存储对象
64+
let store = EKEventStore()
65+
// 请求事件(日历)权限
66+
store.requestAccess(to: self.entityType) { (granted, error) in
67+
completionHandler(granted ? .authorized : .denied)
68+
}
69+
}
70+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// LCContactsAuthorization.swift
3+
// LCPermissionsKit
4+
//
5+
// Created by Liu Chuan on 2023/1/17.
6+
//
7+
8+
import Foundation
9+
import Contacts
10+
11+
/// 联系人授权
12+
class LCContactsAuthorization: NSObject {
13+
14+
/// 获取通讯录实体类型的授权状态
15+
///
16+
/// - Parameter entityType: 要查询的通讯录实体类型
17+
/// - Returns: 授权状态
18+
static func authorizationStatus(for entityType: CNEntityType) -> CNAuthorizationStatus {
19+
return CNContactStore.authorizationStatus(for: entityType)
20+
}
21+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//
2+
// LCContactsAuthorizer.swift
3+
// LCPermissionsKit
4+
//
5+
// Created by Liu Chuan on 2023/1/17.
6+
//
7+
8+
import Cocoa
9+
import Contacts
10+
11+
/// 联系人授权器
12+
class LCContactsAuthorizer: NSObject {
13+
14+
/// 将 `CNAuthorizationStatus` 转换为 `LCAuthorizationStatus`
15+
///
16+
/// - Parameter status: CNAuthorizationStatus,通讯录权限的授权状态
17+
/// - Returns: LCAuthorizationStatus,对应的自定义权限授权状态
18+
private func authorizationStatus(from status: CNAuthorizationStatus) -> LCAuthorizationStatus {
19+
/// 根据通讯录权限状态进行判断并转换
20+
switch status {
21+
case .denied, .restricted:
22+
return .denied
23+
case .authorized:
24+
return .authorized
25+
case .notDetermined:
26+
return .notDetermined
27+
@unknown default:
28+
return .notDetermined
29+
}
30+
}
31+
}
32+
33+
//MARK: - LCAuthorizer
34+
extension LCContactsAuthorizer: LCAuthorizer {
35+
36+
func authorizationStatus() -> LCAuthorizationStatus {
37+
if #available(macOS 10.11, *) {
38+
// 获取系统通讯录权限状态
39+
let authorizationStatus = CNContactStore.authorizationStatus(for: .contacts)
40+
return self.authorizationStatus(from: authorizationStatus)
41+
} else { // 在 macOS 10.11 以下版本,默认返回已授权
42+
return .authorized
43+
}
44+
}
45+
46+
func requestAuthorization(withCompletion completionHandler: @escaping (LCAuthorizationStatus) -> Void) {
47+
if #available(macOS 10.11, *) { // 在 macOS 10.11 及以上版本,使用 CNContactStore 请求通讯录权限
48+
let store = CNContactStore()
49+
store.requestAccess(for: .contacts) { (granted, error) in
50+
completionHandler(granted ? .authorized : .denied)
51+
}
52+
} else { // 在 macOS 10.11 以下版本,默认返回已授权
53+
completionHandler(.authorized)
54+
}
55+
}
56+
57+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//
2+
// LCFullDiskAccessAuthorizer.swift
3+
// LCPermissionsKit
4+
//
5+
// Created by Liu Chuan on 2023/1/17.
6+
//
7+
8+
import Cocoa
9+
10+
11+
12+
class LCFullDiskAccessAuthorizer: NSObject {
13+
14+
/// 共享的单例实例
15+
static let shared = LCFullDiskAccessAuthorizer()
16+
17+
/// 文件管理器
18+
private var fileManager: FileManager
19+
20+
/// 工作区
21+
private var workspace: NSWorkspace
22+
23+
/// 用户主目录路径
24+
private var userHomeFolderPath: String?
25+
26+
// 初始化方法,可以传入`自定义的文件管理器`和`工作区`
27+
init(fileManager: FileManager = FileManager.default, workspace: NSWorkspace = NSWorkspace.shared) {
28+
self.fileManager = fileManager
29+
self.workspace = workspace
30+
}
31+
32+
/// 检查`完全磁盘访问权限`,使用`指定文件`进行检测
33+
///
34+
/// - Parameter path: 指定的文件路径
35+
/// - Returns: LCAuthorizationStatus,完全磁盘访问权限的授权状态
36+
private func checkFDA(usingFile path: String) -> LCAuthorizationStatus {
37+
// 尝试以 只读模式 打开 指定路径 的文件
38+
let file = open(path, O_RDONLY)
39+
40+
// 检查文件是否成功打开
41+
if file != -1 {
42+
// 如果成功打开,则 关闭文件 并返回 已授权 状态
43+
close(file)
44+
return .authorized
45+
}
46+
47+
// 如果打开文件失败,则检查错误码以确定授权状态
48+
if errno == EPERM || errno == EACCES {
49+
// 如果权限不足,则返回拒绝状态
50+
return .denied
51+
}
52+
53+
// 如果未确定授权状态,则返回未确定状态
54+
return .notDetermined
55+
}
56+
57+
/// 获取`完全磁盘访问权限`的`授权状态`
58+
///
59+
/// - Returns: LCAuthorizationStatus,完全磁盘访问权限的授权状态
60+
private func fullDiskAuthorizationStatus() -> LCAuthorizationStatus {
61+
// 定义测试文件路径数组
62+
let testFiles = [
63+
getUserHomeFolderPath().appending("/Library/Safari/CloudTabs.db"),
64+
getUserHomeFolderPath().appending("/Library/Safari/Bookmarks.plist"),
65+
"/Library/Application Support/com.apple.TCC/TCC.db",
66+
"/Library/Preferences/com.apple.TimeMachine.plist",
67+
]
68+
69+
// 初始化授权状态为未确定
70+
var resultStatus = LCAuthorizationStatus.notDetermined
71+
72+
// 遍历 测试文件路径 数组,检查每个文件的 磁盘访问权限
73+
for file in testFiles {
74+
let status = checkFDA(usingFile: file)
75+
76+
// 如果有一个文件已授权,则整体状态为已授权,并跳出循环
77+
if status == .authorized {
78+
resultStatus = .authorized
79+
break
80+
}
81+
82+
// 如果有一个文件权限被拒绝,则整体状态为被拒绝
83+
if status == .denied {
84+
resultStatus = .denied
85+
}
86+
}
87+
88+
// 返回最终的磁盘访问权限授权状态
89+
return resultStatus
90+
}
91+
92+
93+
/// 获取用户主目录路径
94+
///
95+
/// - Returns: 用户主目录路径
96+
private func getUserHomeFolderPath() -> String {
97+
if let path = userHomeFolderPath {
98+
return path
99+
}
100+
let isSandboxed = ProcessInfo.processInfo.environment["APP_SANDBOX_CONTAINER_ID"] != nil
101+
if isSandboxed { // 沙盒
102+
let pw = getpwuid(getuid())
103+
assert(pw != nil)
104+
userHomeFolderPath = String(cString: pw!.pointee.pw_dir)
105+
} else { // 非沙盒
106+
userHomeFolderPath = NSHomeDirectory()
107+
}
108+
return userHomeFolderPath!
109+
}
110+
111+
/// 打开系统偏好设置 - 完全磁盘访问权限
112+
private func openPreferences() {
113+
// 使用工作区打开系统偏好设置中的完全磁盘访问权限设置页面
114+
workspace.open(URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles")!)
115+
}
116+
}
117+
118+
119+
120+
//MARK: - LCAuthorizer
121+
extension LCFullDiskAccessAuthorizer: LCAuthorizer {
122+
123+
func authorizationStatus() -> LCAuthorizationStatus {
124+
//MARK: 获取 完全磁盘访问权限 的 授权状态
125+
if #available(macOS 10.14, *) {
126+
// 获取系统完全磁盘访问权限状态
127+
return fullDiskAuthorizationStatus()
128+
} else {
129+
// 在 macOS 10.14 以下版本,默认返回已授权
130+
return .authorized
131+
}
132+
}
133+
134+
func requestAuthorization(withCompletion completionHandler: @escaping (LCAuthorizationStatus) -> Void) {
135+
//MARK: 请求完全磁盘访问权限的授权
136+
if #available(macOS 10.14, *) {
137+
// 在 macOS 10.14 及以上版本,打开系统偏好设置页面
138+
openPreferences()
139+
} else {
140+
// 在 macOS 10.14 以下版本,默认返回已授权
141+
completionHandler(.authorized)
142+
}
143+
}
144+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// LCAuthorizer.swift
3+
// LCPermissionsKit
4+
//
5+
// Created by Liu Chuan on 2023/1/17.
6+
//
7+
8+
import Foundation
9+
10+
11+
/// 权限授权器的协议
12+
protocol LCAuthorizer {
13+
14+
/// 获取`权限`的`授权状态`
15+
///
16+
/// - Returns: LCAuthorizationStatus,权限的授权状态
17+
func authorizationStatus() -> LCAuthorizationStatus
18+
19+
/// `请求权限`的`授权`
20+
///
21+
/// - Parameter completionHandler: 授权请求完成后的回调,返回授权状态
22+
func requestAuthorization(withCompletion completionHandler: @escaping (LCAuthorizationStatus) -> Void)
23+
}
24+
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//
2+
// LCPhotosAuthorizer.swift
3+
// LCPermissionsKit
4+
//
5+
// Created by Liu Chuan on 2023/1/17.
6+
//
7+
8+
import Cocoa
9+
import Photos
10+
11+
12+
/// 照片权限 授权器
13+
class LCPhotosAuthorizer: NSObject {
14+
15+
/// 将`照片权限授权状态`转换为`自定义权限授权状态`
16+
///
17+
/// - Parameter status: PHAuthorizationStatus,照片权限的授权状态
18+
/// - Returns: LCAuthorizationStatus,对应的自定义权限授权状态
19+
private func authorizationStatus(from status: PHAuthorizationStatus) -> LCAuthorizationStatus {
20+
switch status {
21+
case .denied, .restricted:
22+
return .denied
23+
case .authorized:
24+
return .authorized
25+
case .notDetermined:
26+
return .notDetermined
27+
case .limited:
28+
return .limited
29+
@unknown default:
30+
return .denied
31+
}
32+
}
33+
}
34+
35+
//MARK: - LCAuthorizer
36+
extension LCPhotosAuthorizer: LCAuthorizer {
37+
38+
func authorizationStatus() -> LCAuthorizationStatus {
39+
//MARK: 获取照片权限的授权状态
40+
if #available(macOS 10.13, *) {
41+
// 获取系统照片权限状态
42+
let status = PHPhotoLibrary.authorizationStatus()
43+
return authorizationStatus(from: status)
44+
} else {
45+
// 在 macOS 10.13 以下版本,默认返回已授权
46+
return .authorized
47+
}
48+
}
49+
50+
func requestAuthorization(withCompletion completionHandler: @escaping (LCAuthorizationStatus) -> Void) {
51+
//MARK: 请求照片权限的授权
52+
if #available(macOS 10.13, *) {
53+
// 在 macOS 10.13 及以上版本,使用 PHPhotoLibrary 请求照片权限
54+
PHPhotoLibrary.requestAuthorization { status in
55+
completionHandler(self.authorizationStatus(from: status))
56+
}
57+
} else {
58+
// 在 macOS 10.13 以下版本,默认返回已授权
59+
completionHandler(.authorized)
60+
}
61+
62+
}
63+
}

0 commit comments

Comments
 (0)