I have a somewhat unique situation, and while I understand what the problem is, I'm not quite sure what the best workaround may be so I thought I would consult the hive mind. :)
Scenario:
I am working on an application (OS X) that is document based and uses Quicktime. When a new document is created, the user selects a movie to view within the document window. The user can then save this document. When the user later attempts to open the document, nothing happens. It just doesn't open.
What I know so far:
The problem is with the way Sandbox operates, and my limited experience working with it (my first attempt.) I have enabled Sandbox and have the app permissions set to allow any user initiated file operations, and also added permissions for the app to access the Movies folder.
When the user loads a document and the Movie is in the Movies folder, it opens and works correctly. However, if the Movie is anywhere else on the users drive, it does NOT work and performs as described above.
I realized this is because while the user can open the document (user initiated), sandbox is blocking the application when it attempts to then open the linked movie automatically (NOT user initiated) unless the movie is located within the Movies folder.
Since the application is designed to work with large files, it seems like a big limitation to force users to copy the files into their movie folder and leave them there.
(I have not yet tried working with a movie located on an external drive, but suspect that it would work as it's not a system drive.)
Question:
Is there a way to work around this, since the user initiated the task of opening the movie originally?
If not, is there perhaps a way to have the application ask the user to confirm that the movie should be loaded... perhaps like presenting an OPEN dialog box with the movie already selected so they can just click OK? This might be the best solution.
Another option might be to automatically copy the movie into the Movies folder when the user initially selects it, but again it's not really idea for my situation...
Any thoughts would be appreciated!
As you describe it, your App has privileges to access files within its own container and in the Movies folder. To access arbitrary movie files you have to:
Let the user initiate the access to a file, i.e. when a new document is created the user has to use a file chooser to access a movie file. The privileges of your sandbox are then extended to allow access to the selected file.
Those access rights are temporarily. To persist them over App restarts you have to save and restore the access right via security scoped bookmarks.
Refer to Apples documentation on PowerBox and on Security Scoped Bookmarks
Also Session 710 from WWDC 2013 might be helpful.
Related
I am writing a tool that offers the option to trash selected items (files, folders). Usually, I'd call -[NSFileManager trashItemAtURL:...] for each of those items, as it's also explained in this and in this SO question.
However, these do not work when trying to delete files from a directory owned by a different user, such as root. My tool shall offer the same option as the Finder in this case, i.e. ask the user to authorize the operation by providing the credentials of an Admin user, and then my app would move the items to the Trash like the Finder does.
I've tried solving this by using a privileged helper, as outline by the EvenBetterAuthorizationSample example code, using launchd, SMJobBless and XPC Services.
The problem with that is, however, that the privileged helper runs as the root user, without knowledge of the current user my app runs under. The result is that, when it trashes a file, it ends up in the root user's Trash folder and not, as the Finder would do it, in the user's Trash folder.
How do I solve this, i.e. how do I move items not owned by the user to the current user's trash instead of the root user's Trash?
Is there some trick I can use that would let me keep using one of the existing trash oder recycle functions?
Doing the move myself is not going to work properly because for Put Back to work, the Trash's .DS_Store file would need to be updated, and there's no API for that, AFAIK.
I have almost found a solution:
Analysis
When the helper is run, e.g. from launchd, or via AuthorizationExecuteWithPrivileges (under macOS 10.15), it may be running as root, with no knowledge of the logged-in user, hence it cannot determine the user's Trash folder.
Oddly, the Environment variables (see man env) may even show the current user's name and home dir, yet the real user id, which one can query with getuid(), will return 0 (root), which also results in NSUserName()and NSHomeDirectory() returning the root user's information. And it appears that trashItemAtURL and related functions rely on NSHomeDirectory() to determine the Trash folder location.
Half-working solution
Fortunately, there is a way to change the real user id, with setreuid.
Therefore, in my testing, when I call setreuid (501, 0) (501 being the uid of the currently logged-in user), then trashItemAtURL does move the file to the user's Trash folder, indeed, along with automatic renaming where necessary.
However, this does not make the Put Back work, the way it would when trashing the same file using the Finder.
Making Put Back work
Looks like the reason for Put Back not working comes from a deeper issue: It appears to be a long-standing bug in the macOS framework, see this bug report.
Which basically means: This is the best we can get out of it until Apple fixes the underlying bug.
The only working alternative to make Put Back work is to ask the Finder to trash the items using AppleEvents / AppleScript.
I have a sandboxed application that uses a document format which can contain embedded filenames. E.g. some of these referenced files are for image files which the user selects in order to associate the image file with data stored in the document. Whilst such images will sometimes be located in the users pictures folder, sometimes they are elsewhere. The document format itself cannot be changed for portability reasons (it is shared with an existing Windows version of the app).
In order for the app to be able to access the files whose names are embedded within the document, when the user selects a filename (using nsopenpanel) to be stored in the document like this, the app also creates an app-scoped security scoped bookmark for each such stored filename. It then stores these bookmarks by serialising them to another file. When the app runs again it loads its previously serialised bookmarks and uses them when accessing the 'embedded' filename and that works fine.
But when I copy such a document (and its associated serialised bookmarks file) from one Mac to another, it doesnt work, even though all the files the bookmarks refer to are known to exist on both Macs. What happens is that whilst the app opens the document file ok (after the user selected it with an nsopenpanel), and successfully reads in the serialised bookmarks from its associated serialised bookmarks file (which again the user has selected using nsopenpanel), and even though the exact same filenames that the bookmarks refer to are known to exist on the other Mac (and can be accessed by the user through finder etc), the sandboxed app still cannot access them. Resolving the bookmarks seems to fail.
The question is: are app-scoped bookmarks restricted to only working on the Mac on which they were created? If I cannot move an app-scoped bookmark from one system to another, how else can I achieve the effect I want without forcing the user to have to manually re-select every such 'embedded' filename with an nsopenpanel?
Tried to find the answer to this question in the Apple docs without success.
I take it it goes without saying that security-scoped bookmarks can only be used by the app that creates them.
No, because if this were possible, developers could save security-scoped bookmarks to sensitive files/directories on their own computers and then deploy their apps and have access to those same sensitive files/directories on other peoples' computers.
My application downloads a PDF and stores it in the LocalState folder for my Windows 8 app.
I have a link within the app that I would like to show the PDF when the user clicks it.
I've tried displaying it using ms-appdata:///local/pdfs/filename.pdf in a window.open call and I also tried using the InAppBrowser plugin within cordova with no luck. Additionally, I've tried the following:
var uri = new Windows.Foundation.Uri('ms-appdata:///local/pdfs/filename.pdf');
var file = Windows.Storage.StorageFile.getFileFromApplicationUriAsync(uri);
Windows.System.Launcher.launchFileAsync(file).done();
I know the file exists as I'm getting a file result back. Just not sure how to allow the user to view it.
By design, the local appdata folder on Windows is accessible only to that app, or to full-trust desktop applications (and this is probably true of similar sandboxed locations on other platforms). As a result, a Windows Store app that gets launched with Launcher.launchFileAsync won't be able access that location (nor can a webview process, which is also sandboxed). If a desktop application gets launched, on the other hand, it probably can access the file, but you can't tell ahead of time if that's the case. Bottom line is that local appdata isn't a good location for letting other apps get at the file.
You'll need to save the file in another location that is accessible to other apps. There are two approaches here, both of which will require a little user interaction to select a location, so they can place the PDFs anywhere they want:
Have the user select a save folder for your app, which they can do once. You would invoke the FolderPicker for this purpose, and save the selected folder in the FutureAccessList. This way you can have the user select the save folder, which grants you consent to save there, and by saving it in the FutureAccessList you can retrieve it in subsequent sessions without having to ask the user again. Refer to the File Picker Sample and the File Access Sample for more.
Have the user select a save location for each individual file, using the FilePicker (see the same sample), and you can also use the access cache to save permissions to those individual locations if you need them later.
There might be Cordova plugins that work with these APIs too, but I haven't checked. Either way, once the file is in an accessible location, launching the file should work just fine.
As an alternate solution, you could consider rendering the PDFs directly in your app. Windows has an API for this in Windows.Data.Pdf, with an associated sample. There might be a plugin or other JS libraries that could also work for this.
I have a mac AVRecording app, which records a video and save it to a location selected via NSSavePanel. It was working fine till I sandboxed the app.
For sandboxing I have added the following entitlements
com.apple.security.files.user-selected.read-write
com.apple.security.assets.movies.read-write
com.apple.security.files.downloads.read-write
This enables saving to Downloads and movies folder only.
How is it possible to save my file to any desired location, Desktop, Documents etc ?
It's not clear from your question whether you are referring to saving a particular file (in which case you can use the NSSavePanel and manually copy the file using NSFileManager to write the file into the user-specified new file), or whether you are referring to having the user choose a location for all future downloads.
If you want to prompt the user for a location to use for future downloads, you'll need to use the secure bookmark entitlement and secure bookmarks to retain access to the folder.
There's another stackoverflow answer about sandboxing which covers the process of saving and using the secure bookmark.
After obtaining a security scoped bookmark to a file, I am seeing mixed results with the bookmark after a user moves the file associated with the bookmark to a different location.
What I am seeing:
If the user moves the file, the bookmark resolves correctly to the
new location. However, once moved, if the user makes a change and
saves, the bookmark does not resolve and gives an error that the
file does not exist.
If the user makes changes to the file and
saves, the bookmark resolves correctly. If the user moves the file
after they save, the bookmark no longer resolves and gives an error
that the file doesn't exist.
Based on what I am seeing, it makes me think bookmarks don't work they way I think they do under sandboxing. However, I have played with a few other sandbox apps and they seem to handle it fine.
UPDATE:
The URL is to a document for my app.
All of the modify/saving that happens all goes through the normal NSDocument plumbing.
Yes, security scoped-bookmarks do remain valid after a user moves a file.
After some experimenting, it seems to take the OS a small amount of time to update whatever resource it uses to help with resolving bookmarks. It seems that I was resolving bookmarks too soon after moving the files associated with them.