Skip to content

Commit 2348346

Browse files
committed
feat: support 'notifications' auth status
1 parent 4280ffc commit 2348346

File tree

6 files changed

+53
-5
lines changed

6 files changed

+53
-5
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ This native Node.js module allows you to manage an app's access to:
4747
- Reminders
4848
- Screen Capture
4949
- Speech Recognition
50+
- Notifications
5051

5152
If you need to ask for permissions, your app must be allowed to ask for permission :
5253

@@ -59,9 +60,9 @@ If you're using macOS 12.3 or newer, you'll need to ensure you have Python insta
5960

6061
### `permissions.getAuthStatus(type)`
6162

62-
- `type` String - The type of system component to which you are requesting access. Can be one of `accessibility`, `bluetooth`, `calendar`, `camera`, `contacts`, `full-disk-access`, `input-monitoring`, `location`, `microphone`,`photos`, `reminders`, `screen`, or `speech-recognition`.
63+
- `type` String - The type of system component to which you are requesting access. Can be one of `accessibility`, `bluetooth`, `calendar`, `camera`, `contacts`, `full-disk-access`, `input-monitoring`, `location`, `microphone`,`notifications`, `photos`, `reminders`, `screen`, or `speech-recognition`.
6364

64-
Returns `String` - Can be one of `not determined`, `denied`, `authorized`, or `restricted`.
65+
Returns `String` - Can be one of `not determined`, `denied`, `authorized`, `limited`, `provisional`, or `restricted`.
6566

6667
Checks the authorization status of the application to access `type` on macOS.
6768

@@ -72,6 +73,7 @@ Return Value Descriptions:
7273
- `denied` - The user explicitly denied access to `type` data for the application.
7374
- `authorized` - The application is authorized to access `type` data.
7475
- `limited` - The application is authorized for limited access to `type` data. Currently only applicable to the `photos` type.
76+
- 'provisional' - The application is provisionally authorized to access `type` data. Currently only applicable to the `notifications` type.
7577

7678
**Notes:**
7779

@@ -96,6 +98,7 @@ const types = [
9698
'location',
9799
'microphone',
98100
'music-library',
101+
'notifications',
99102
'photos-add-only',
100103
'photos-read-write',
101104
'reminders',

binding.gyp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
"-framework IOKit",
3434
"-framework Photos",
3535
"-framework Speech",
36-
"-framework StoreKit"
36+
"-framework StoreKit",
37+
"-framework UserNotifications",
3738
]
3839
}
3940
}]

index.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export function askForPhotosAccess(accessType?: 'add-only' | 'read-write'): Prom
1515
export function askForRemindersAccess(): Promise<Omit<PermissionType, 'restricted'>>
1616
export function askForSpeechRecognitionAccess(): Promise<Omit<PermissionType, 'restricted'>>
1717
export function askForScreenCaptureAccess(openPreferences?: boolean): undefined
18-
export function getAuthStatus(authType: AuthType): PermissionType | 'not determined'
18+
export function getAuthStatus(authType: AuthType): PermissionType | 'not determined' | 'provisional' | 'limited'
1919

2020
export type AuthType =
2121
| 'accessibility'
@@ -28,6 +28,7 @@ export type AuthType =
2828
| 'location'
2929
| 'microphone'
3030
| 'music-library'
31+
| 'notifications'
3132
| 'photos-add-only'
3233
| 'photos-read-write'
3334
| 'reminders'

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ function getAuthStatus(type) {
1313
'location',
1414
'microphone',
1515
'music-library',
16+
'notifications',
1617
'photos-add-only',
1718
'photos-read-write',
1819
'reminders',

permissions.mm

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#import <Photos/Photos.h>
1414
#import <Speech/Speech.h>
1515
#import <StoreKit/StoreKit.h>
16+
#import <UserNotifications/UserNotifications.h>
1617
#import <pwd.h>
1718

1819
/***** HELPER FUNCTIONS *****/
@@ -22,6 +23,7 @@
2223
const std::string kRestricted{"restricted"};
2324
const std::string kNotDetermined{"not determined"};
2425
const std::string kLimited{"limited"};
26+
const std::string kProvisional{"provisional"};
2527

2628
// Check if a bundle ID is valid (i.e., corresponds to an installed app)
2729
bool IsValidBundleID(NSString *bundleID) {
@@ -104,6 +106,19 @@ IOHIDRequestType GetInputMonitoringAccessType(const std::string &type) {
104106
}
105107
}
106108

109+
const std::string &StringFromNotificationStatus(UNAuthorizationStatus status) {
110+
switch (status) {
111+
case UNAuthorizationStatusAuthorized:
112+
return kAuthorized;
113+
case UNAuthorizationStatusDenied:
114+
return kDenied;
115+
case UNAuthorizationStatusProvisional:
116+
return kProvisional;
117+
default:
118+
return kNotDetermined;
119+
}
120+
}
121+
107122
std::string StringFromSpeechRecognitionStatus(
108123
SFSpeechRecognizerAuthorizationStatus status) {
109124
switch (status) {
@@ -376,6 +391,30 @@ bool HasOpenSystemPreferencesDialog() {
376391
return StringFromSpeechRecognitionStatus(status);
377392
}
378393

394+
// Returns a status indicating whether the user has authorized
395+
// Notifications access.
396+
std::string NotificationAuthStatus() {
397+
NSURL *bundle_url = [[NSBundle mainBundle] bundleURL];
398+
if (!IsValidBundleID([bundle_url path])) {
399+
return kNotDetermined;
400+
}
401+
402+
__block std::string ret = "not determined";
403+
dispatch_group_t g = dispatch_group_create();
404+
dispatch_group_enter(g);
405+
406+
UNUserNotificationCenter *center =
407+
[UNUserNotificationCenter currentNotificationCenter];
408+
[center getNotificationSettingsWithCompletionHandler:^(
409+
UNNotificationSettings *settings) {
410+
ret = StringFromNotificationStatus(settings.authorizationStatus);
411+
dispatch_group_leave(g);
412+
}];
413+
414+
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
415+
return ret;
416+
}
417+
379418
// Returns a status indicating whether the user has authorized location
380419
// access.
381420
std::string LocationAuthStatus() {
@@ -444,6 +483,8 @@ bool HasOpenSystemPreferencesDialog() {
444483
auth_status = MusicLibraryAuthStatus();
445484
} else if (type == "input-monitoring") {
446485
auth_status = InputMonitoringAuthStatus();
486+
} else if (type == "notifications") {
487+
auth_status = NotificationAuthStatus();
447488
}
448489

449490
return Napi::Value::From(env, auth_status);

test/module.spec.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@ describe('node-mac-permissions', () => {
2929
'location',
3030
'microphone',
3131
'music-library',
32+
'notifications',
3233
'photos-add-only',
3334
'photos-read-write',
3435
'reminders',
3536
'speech-recognition',
3637
'screen',
3738
]
3839

39-
const statuses = ['not determined', 'denied', 'authorized', 'restricted']
40+
const statuses = ['not determined', 'denied', 'authorized', 'restricted', 'provisional']
4041
for (const type of types) {
4142
const status = getAuthStatus(type)
4243
expect(statuses).to.contain(status)

0 commit comments

Comments
 (0)