Opening a file using Sandbox - objective-c

OK, so I know that under the new SandBox guidelines, opening a file must abide by certain rules (a.k.a. the opening action must be triggered by the user using an NSOpenPanel, given of course the necessary "open" permissions).
However, here's the catch :
In my application, I've got an NSOutlineView with a complete file manager in it (the full tree structure)
The user is supposed to be able to select a file from the outline view and then the app will open it.
How am I supposed to do that, given that the app should be 100% sandbox-compliant? Is there any known workaround? Is it even possible?
Any ideas?

Short answer: You can't do that. In order to show the contents of a folder within your app's UI, you'd first have to get the user to open it either using an open panel or dragging it in from the Finder.

You can do this, as #omz said, your application needs to request permission to access the folder containing all the files/folders that your application is showing. You don't need permission of individual files, but can get an entire directory structure as a single permission, and then store that as a security scoped bookmark so future executions of your application will already have that permission.
You could even at app launch ask the user for permission to access the entire hard drive, or their entire user directory.
You can use this class I wrote to wrap all that up into a single function call, which will then persist the permission so they are only asked on first run. https://github.com/leighmcculloch/AppSandboxFileAccess
Alternatively if you want to do it with NSOpenPanel manually, just take a look at the code in AppSandboxFileAccess as it uses it to get permissions and then persist those permissions.

Related

Microsoft Graph .NET SDK: Getting items from mycompany.sharepoint.com/Shared%20Documents

Debugging the code, I see the request is like:
https://graph.microsoft.com:443/v1.0/drives/the-shared-docs-id/items/the-root-id/children
I know for sure that the-shared-docs-id and the-root-id are right.
Furthermore, this same request returns the expected results when issued from the Graph explorer.
However, the .NET SDK returns nothing. No items at all.
The same user (me) created the files and folders, and authorized the app to access OneDrive. This user is even the admin of everything.
I was testing with two different apps, one with Files.ReadWrite scope only, and other with Files.ReadWrite.All.
I thought Files.ReadWrite.All was needed to access other user's files. It turns out, it is also needed to access the shared folder, even being the same (admin) user who created the shared items (files and folders), and even having full permissions to access them.
I think this is weird... but it looks like this is the way it is.
Using the app with Files.ReadWrite.All scope, I was able to see the items in the shared folder.
Using the Files.ReadWrite scope, I get an empty list of items.

Sandboxed Applications, Where to Save Files Without User Interaction

I'm a bit confused about where the application should send a bunch of files. Let me suppose that an application accepts a number of images with NSOpenPanel at a time from the user. The application applies graphic filters to them. And it's now ready to save processed files. Before they forced us to sandbox applications, we were allowed to export processed files to application folder in Application Support without NSSavePanel. If you wanted to save files elsewhere, then you had to use NSSavePanel. If the application is sandboxed, it cannot send files to NSApplicationSupportDirectory/{app name}(which points to the containers folder assigned to this application)? My first sandboxed application was rejected a few days ago merely because a text field showed a path to container's application support folder. So if you have a bunch of files to export, you have to prompt the user to ask where to save each file? AppSandboxDesignedGuide, which Apple, Inc. has issued, has nothing to say exactly about where to save files except that it says "Your app has unrestricted read/write access to the container and its subdirectories." I think this PDF guide is a printed version of this web site. I'm asking this question here because I have some doubts and reviewers were often wrong at least when I submitted applications to them two years ago.
Thank you for your advice.
If the files are only for the application itself to use you can save the files in "Application Support/", which under the sandbox is under your container, just as before - just use the APIs to construct the path to that folder (and create it, it doesn't exist automatically, just as before).
If you are outputting files for the user to access then you don't put them in the container - that folder is meant to be hidden from ordinary users, though yours is the first time I've heard that even showing the path got you a rejection, but Apple are pretty random.
Here are three choices of where to put your files:
First is to ask the user. This is what you would normally do anyway, you shouldn't just dump files somewhere.
Second is a situation that the sandbox makes harder - when where the file should is is implicit, e.g. a graphic conversion program might sensibly output the converted file with the same name but different extension in the same folder as the original. This was finally addressed by Apple around 10.8.3 or something with "Related Items" - Apple's docs for this are here. Essentially in the Document Types in the Info.plist you must list all the extensions you handle - both in and out - and add a NSIsRelatedItemType key with the value of YES to all those you might convert between. E.g. For TextEdit .rtf, .rtfd and .txt are flagged in this way so TextEdit can open as one format and save as another.
Third, if you wish to put all your files in one location, say in a "Converted Items" folder. Then you ask the user once to specify this folder and then save a security-scoped bookmark to that folder in your applications defaults or elsewhere in your app's container. On subsequent executions you can access this bookmark and regain access to the folder. For an introduction to this start with Apple's Security-Scoped Bookmarks and Persistent Resource Access. This is really no harder than pre-sandbox as any decent app would always ask for the location of the folder from the user, the difference is the need to save the security-scoped bookmark so the user doesn't need to give permission every time.
HTH.

Get lasting permission to write to a specific directory with the new Sandbox requirements

I need a way to get & keep permission to write to a specific directory in OS X. How can that be done while abiding with the new Sandbox requirements?
The recipe:
Ask the user to select the directory - use a standard open dialog limited to directory selection. Apart from a few special directories (music, pictures etc.) there is no way to gain access apart from asking the user.
Create a security-scoped bookmark using the URL returned by the standard open dialog, just search the Apple docs for "security-scoped bookmark".
Persist that bookmark, either in user preferences or in the Application Support folder for your app.
On application launch, or before you need access, read in the saved bookmark and activate - you'll find out how to do this in the Apple docs as above.

How to check if user selected file(s) my Sandboxed app has permission to write?

I'm enabling sandboxing in my OS X app to resubmit to the app store and I'm trying to find the most elegant way to make sure the user can only select resources my app has permissions to change.
Stripped down scenario:
user selects a picture on the file system via an NSOpenPanel
user clicks the Process Picture button on the app
app retrieves information from the internet
app alters the picture's metadata.
After the user is done selecting the pictures, I want to make sure each one is located under the Pictures folder otherwise my write to the physical file will simply silently fail.
Apple recommends the following to determine the Pictures folder's location:
The Pictures directory contains the user’s images and photos. To get
the path to this directory use the NSPicturesDirectory search path key
with the NSUserDomainMask domain.
Implementing the above gives me a path that looks like this:
/Users/thomas/Library/Containers/com.blazingfrog.latipics/Data/Pictures/picture1.jpg
But when I want to see what pictures the user selected,[myOpenPanel URLs] returns /Users/thomas/Pictures/picture1.jpg
These two paths are logically identical but look very different. How can I compare them in way that works every time?
In case it helps, to prepare my app for sandboxing I did the following
enabled Entitlements in XCode
enabled App Sandboxing
enabled File System (Read)
enabled allowing Incoming/Outgoing Network Connections
enabled Pictures Folder access (Read/Write)
You should almost never fail silently. If something goes wrong, report it to the user, as best you can. This is exactly what NSError is designed for.
You should almost never attempt to figure out if an operation will succeed before trying it. Doing so leaves you open to race conditions. Instead go ahead and try the operation; if it fails, handle that gracefully.
It sounds like you actually want the com.apple.security.files.user-selected.read-write entitlement. This will give you write access to any files the user selects using an open panel.
[[NSFileManager defaultManager] isWritableFileAtPath:path]

How do I implement a secure upload/download area?

I've been asked to create a solution where people log in and are able to upload and download off of our work server. So John uploads a photo, and Jen can download it, for example. They also have to authenticate themselves.
Can someone give me a rough overview of how to implement this? I'm familiar enough with MySQL, C#, and JavaScript.
The rough overview
This should just be a matter of planning out the pieces.
at the very top of the page, put some code that checks if a user is logged in. If not, show a login form (or redirect to...). If they are logged in, show the rest of the page. If not, you'll need some logic to show a form, and then check it once it's submitted for authentication, and set a SESSION cookie or something similar.
Once the user is logged in, on the homepage, you might have an file-upload form and a listing of existing files. How you would style would depend on how many files you might expect to have. To keep things extremely simple, you could simple iterate through whatever files are in the upload directory. If you expect many more files than that, you may consider using a db.
Handle a file upload by sanitizing filenames (checking for filetype/filesize if you want to limit those) and putting the file into the directory.
Force the users to download the files (instead of having the browser decide what to do with them) for security purposes. Implementing this on certain filetypes may also be acceptable.
Other thoughts
You probably would not want the users to be able to excecute any files, so keeping the file directory hidden would be a good idea.
Keeping track of who uploaded and downloaded what is also doable, but would add another layer of complication to the script.