Skip to content

Commit

Permalink
feat: add askForFoldersAccess()
Browse files Browse the repository at this point in the history
  • Loading branch information
codebytere committed Sep 2, 2020
1 parent 5599014 commit e74fd17
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 1 deletion.
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ This native Node.js module allows you to manage an app's access to:
* Location
* Screen Capture
* Speech Recognition
* Protected Folders

## API

Expand Down Expand Up @@ -140,6 +141,37 @@ askForRemindersAccess().then(status => {
})
```

## `permissions.askForFoldersAccess(folder)`

* `type` String - The folder to which you are requesting access. Can be one of `desktop`, `documents`, or `downloads`.

Returns `Promise<String>` - Whether or not the request succeeded or failed; can be `authorized` or `denied`.

Example:

```js
const { askForFoldersAccess } = require('node-mac-permissions')

askForFoldersAccess('desktop').then(status => {
console.log(`Access to Desktop is ${status}`)
})
```

```
<key>NSDesktopFolderUsageDescription</key>
<string>Your reason for wanting to access the Desktop folder</string>
```

```
<key>NSDocumentsFolderUsageDescription</key>
<string>Your reason for wanting to access the Documents folder</string>
```

```
<key>NSDownloadsFolderUsageDescription</key>
<string>Your reason for wanting to access the Downloads folder</string>
```

## `permissions.askForFullDiskAccess()`

There is no API for programmatically requesting Full Disk Access on macOS at this time, and so calling this method will trigger opening of System Preferences at the Full Disk pane of Security and Privacy.
Expand Down Expand Up @@ -321,4 +353,13 @@ $ tccutil reset SystemPolicyAllFiles

# Reset Contacts permissions
$ tccutil reset AddressBook

# Reset Desktop folder access
$ tccutil reset SystemPolicyDesktopFolder <bundleID>

# Reset Documents folder access
$ tccutil reset SystemPolicyDocumentsFolder <bundleID>

# Reset Downloads folder access
$ tccutil reset SystemPolicyDownloadsFolder <bundleID>
```
11 changes: 11 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,20 @@ function getAuthStatus(type) {
return permissions.getAuthStatus.call(this, type)
}

function askForFoldersAccess(folder) {
const validFolders = ['desktop', 'documents', 'downloads']

if (!validFolders.includes(folder)) {
throw new TypeError(`${folder} is not a valid protected folder`)
}

return permissions.askForFoldersAccess.call(this, folder)
}

module.exports = {
askForCalendarAccess: permissions.askForCalendarAccess,
askForContactsAccess: permissions.askForContactsAccess,
askForFoldersAccess,
askForFullDiskAccess: permissions.askForFullDiskAccess,
askForRemindersAccess: permissions.askForRemindersAccess,
askForCameraAccess: permissions.askForCameraAccess,
Expand Down
39 changes: 39 additions & 0 deletions permissions.mm
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@

/***** HELPER FUNCTIONS *****/

NSURL *URLForDirectory(NSSearchPathDirectory directory) {
NSFileManager *fm = [NSFileManager defaultManager];
return [fm URLForDirectory:directory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:false
error:nil];
}

// Dummy value to pass into function parameter for ThreadSafeFunction.
Napi::Value NoOp(const Napi::CallbackInfo &info) {
return info.Env().Undefined();
Expand Down Expand Up @@ -316,6 +325,34 @@ bool HasOpenSystemPreferencesDialog() {
return Napi::Value::From(env, auth_status);
}

// Request access to various protected folders on the system.
Napi::Promise AskForFoldersAccess(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
const std::string folder_name = info[0].As<Napi::String>().Utf8Value();

NSString *path = @"";
if (folder_name == "documents") {
NSURL *url = URLForDirectory(NSDocumentDirectory);
path = [url path];
} else if (folder_name == "downloads") {
NSURL *url = URLForDirectory(NSDownloadsDirectory);
path = [url path];
} else if (folder_name == "desktop") {
NSURL *url = URLForDirectory(NSDesktopDirectory);
path = [url path];
}

NSError *error = nil;
NSFileManager *fm = [NSFileManager defaultManager];
NSArray<NSString *> *contents __unused =
[fm contentsOfDirectoryAtPath:path error:&error];

std::string status = (error) ? "denied" : "authorized";
deferred.Resolve(Napi::String::New(env, status));
return deferred.Promise();
}

// Request Contacts access.
Napi::Promise AskForContactsAccess(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
Expand Down Expand Up @@ -627,6 +664,8 @@ void AskForAccessibilityAccess(const Napi::CallbackInfo &info) {
Napi::Function::New(env, AskForCalendarAccess));
exports.Set(Napi::String::New(env, "askForRemindersAccess"),
Napi::Function::New(env, AskForRemindersAccess));
exports.Set(Napi::String::New(env, "askForFoldersAccess"),
Napi::Function::New(env, AskForFoldersAccess));
exports.Set(Napi::String::New(env, "askForFullDiskAccess"),
Napi::Function::New(env, AskForFullDiskAccess));
exports.Set(Napi::String::New(env, "askForCameraAccess"),
Expand Down
11 changes: 10 additions & 1 deletion test/module.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const { expect } = require('chai')
const {
const {
askForFoldersAccess,
getAuthStatus,
} = require('../index')

Expand Down Expand Up @@ -33,4 +34,12 @@ describe('node-mac-permissions', () => {
}
})
})

describe('askForFoldersAccess()', () => {
it('should throw on invalid types', () => {
expect(() => {
askForFoldersAccess('bad-type')
}).to.throw(/bad-type is not a valid protected folder/)
})
})
})

0 comments on commit e74fd17

Please sign in to comment.