Skip to content

MacOS sandboxing feature #16090

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
130552c
Set isDirectory:true explicitly to help [NSURL fileURLWithPath] method
maxkatz6 Jun 19, 2024
8a6a8b2
Fix dialogs page incorrectly setting parent folder
maxkatz6 Jun 23, 2024
0b04775
Move SecurityScopedStream out of iOS project and share it with macOS …
maxkatz6 Jun 23, 2024
bb9d653
Refactor BclStorageItem to be more reusable across platforms
maxkatz6 Jun 23, 2024
ebd9e58
[Breaking] Set BclStorageItem.CanBookmark to false, as it never was s…
maxkatz6 Jun 23, 2024
15cb712
Reimplement storage provider support on macOS, support (optional) san…
maxkatz6 Jun 23, 2024
37b18c8
Fix build
maxkatz6 Jun 23, 2024
fcac5d7
Fix AppSandboxEnabled=false usage
maxkatz6 Jun 23, 2024
5c6fd66
Merge remote-tracking branch 'origin/master' into macos-bookmarks
maxkatz6 Jul 9, 2024
3a13185
Merge branch 'master' into macos-bookmarks
maxkatz6 Jul 13, 2024
7de3a88
Re-enable BCL bookmarks, keep them base64
maxkatz6 Jul 13, 2024
74b303d
Fix nullable error
maxkatz6 Jul 13, 2024
52a2a4d
Prefix all bookmarks with a platform key
maxkatz6 Jul 15, 2024
375a9b3
Fix devtools breaking sandboxed app
maxkatz6 Jul 16, 2024
f22a32b
Try to read errors after saving bookmark
maxkatz6 Jul 16, 2024
f1d21a4
Don't crash sample app if has no access
maxkatz6 Jul 16, 2024
eaa6c62
Add internal IStorageItemWithFileSystemInfo abstraction
maxkatz6 Jul 16, 2024
c6ac64d
Log information if OpenSecurityScope returned false
maxkatz6 Jul 16, 2024
47c3e3c
Fix build
maxkatz6 Jul 16, 2024
838b390
Prefix bookmarks with "ava.v1."
maxkatz6 Jul 16, 2024
3ac1e32
Support opening old-style bookmarks to avoid breaking changes
maxkatz6 Jul 16, 2024
c3c564e
Merge branch 'master' into macos-bookmarks
maxkatz6 Jul 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
1AFD334123E03C4F0042899B /* controlhost.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1AFD334023E03C4F0042899B /* controlhost.mm */; };
37155CE4233C00EB0034DCE9 /* menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 37155CE3233C00EB0034DCE9 /* menu.h */; };
37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; };
37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* SystemDialogs.mm */; };
37C09D8821580FE4006A6758 /* StorageProvider.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* StorageProvider.mm */; };
37DDA9B0219330F8002E132B /* AvnString.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37DDA9AF219330F8002E132B /* AvnString.mm */; };
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37E2330E21583241000CB7E2 /* KeyTransform.mm */; };
520624B322973F4100C4DCEF /* menu.mm in Sources */ = {isa = PBXBuildFile; fileRef = 520624B222973F4100C4DCEF /* menu.mm */; };
Expand Down Expand Up @@ -95,7 +95,7 @@
379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = "<group>"; };
37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = "<group>"; };
37A517B22159597E00FBA241 /* Screens.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Screens.mm; sourceTree = "<group>"; };
37C09D8721580FE4006A6758 /* SystemDialogs.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SystemDialogs.mm; sourceTree = "<group>"; };
37C09D8721580FE4006A6758 /* StorageProvider.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = StorageProvider.mm; sourceTree = "<group>"; };
37DDA9AF219330F8002E132B /* AvnString.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnString.mm; sourceTree = "<group>"; };
37DDA9B121933371002E132B /* AvnString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnString.h; sourceTree = "<group>"; };
37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = "<group>"; };
Expand Down Expand Up @@ -202,7 +202,7 @@
523484CB26EA68AA00EA0C2C /* trayicon.h */,
1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */,
37A517B22159597E00FBA241 /* Screens.mm */,
37C09D8721580FE4006A6758 /* SystemDialogs.mm */,
37C09D8721580FE4006A6758 /* StorageProvider.mm */,
EDF8CDCC2964CB01001EE34F /* PlatformSettings.mm */,
AB7A61F02147C815003C5833 /* Products */,
AB661C1C2148230E00291242 /* Frameworks */,
Expand Down Expand Up @@ -339,7 +339,7 @@
1AFD334123E03C4F0042899B /* controlhost.mm in Sources */,
1A465D10246AB61600C5858B /* dnd.mm in Sources */,
AB00E4F72147CA920032A60A /* main.mm in Sources */,
37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */,
37C09D8821580FE4006A6758 /* StorageProvider.mm in Sources */,
1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */,
F10084862BFF1FB40024303E /* TopLevelImpl.mm in Sources */,
1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,91 @@ - (void)popupAction:(id)sender {

@end

class SystemDialogs : public ComSingleObject<IAvnSystemDialogs, &IID_IAvnSystemDialogs>
class StorageProvider : public ComSingleObject<IAvnStorageProvider, &IID_IAvnStorageProvider>
{
ExtensionDropdownHandler* __strong _extension_dropdown_handler;

public:
FORWARD_IUNKNOWN()

virtual HRESULT SaveBookmarkToBytes (
IAvnString* fileUriStr,
void** err,
IAvnString** ppv
) override
{
@autoreleasepool
{
if(ppv == nullptr)
return E_POINTER;

NSError* error;
auto fileUri = [NSURL URLWithString: GetNSStringAndRelease(fileUriStr)];
auto bookmarkData = [fileUri bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
if (bookmarkData)
{
*ppv = CreateByteArray((void*)bookmarkData.bytes, (int)bookmarkData.length);
}
if (error != nil)
{
*err = CreateAvnString([error localizedDescription]);
}
return S_OK;
}
}

virtual HRESULT ReadBookmarkFromBytes (
void* ptr,
int len,
IAvnString** ppv
) override {
@autoreleasepool
{
if(ppv == nullptr)
return E_POINTER;

auto bookmarkData = [[NSData alloc] initWithBytes:ptr length:len];
auto fileUri = [NSURL URLByResolvingBookmarkData: bookmarkData
options:NSURLBookmarkResolutionWithSecurityScope|NSURLBookmarkResolutionWithoutUI
relativeToURL:nil
bookmarkDataIsStale:nil
error:nil];

if (fileUri)
{
*ppv = CreateAvnString([fileUri absoluteString]);
}
return S_OK;
}
}

virtual void ReleaseBookmark (
IAvnString* fileUriStr
) override {
// no-op
}

virtual bool OpenSecurityScope (
IAvnString* fileUriStr
) override {
@autoreleasepool
{
auto fileUri = [NSURL URLWithString: GetNSStringAndRelease(fileUriStr)];
auto success = [fileUri startAccessingSecurityScopedResource];
return success;
}
}

virtual void CloseSecurityScope (
IAvnString* fileUriStr
) override {
@autoreleasepool
{
auto fileUri = [NSURL URLWithString: GetNSStringAndRelease(fileUriStr)];
[fileUri stopAccessingSecurityScopedResource];
}
}

virtual void SelectFolderDialog (IAvnWindow* parentWindowHandle,
IAvnSystemDialogEvents* events,
bool allowMultiple,
Expand Down Expand Up @@ -105,19 +184,9 @@ virtual void SelectFolderDialog (IAvnWindow* parentWindowHandle,

if(urls.count > 0)
{
void* strings[urls.count];

for(int i = 0; i < urls.count; i++)
{
auto url = [urls objectAtIndex:i];

auto string = [url path];

strings[i] = (void*)[string UTF8String];
}

events->OnCompleted((int)urls.count, &strings[0]);

auto uriStrings = CreateAvnStringArray(urls);
events->OnCompleted(uriStrings);

[panel orderOut:panel];

if(parentWindowHandle != nullptr)
Expand All @@ -130,7 +199,7 @@ virtual void SelectFolderDialog (IAvnWindow* parentWindowHandle,
}
}

events->OnCompleted(0, nullptr);
events->OnCompleted(nullptr);

};

Expand Down Expand Up @@ -188,19 +257,9 @@ virtual void OpenFileDialog (IAvnWindow* parentWindowHandle,

if(urls.count > 0)
{
void* strings[urls.count];

for(int i = 0; i < urls.count; i++)
{
auto url = [urls objectAtIndex:i];

auto string = [url path];

strings[i] = (void*)[string UTF8String];
}

events->OnCompleted((int)urls.count, &strings[0]);

auto uriStrings = CreateAvnStringArray(urls);
events->OnCompleted(uriStrings);

[panel orderOut:panel];

if(parentWindowHandle != nullptr)
Expand All @@ -213,7 +272,7 @@ virtual void OpenFileDialog (IAvnWindow* parentWindowHandle,
}
}

events->OnCompleted(0, nullptr);
events->OnCompleted(nullptr);

};

Expand Down Expand Up @@ -264,15 +323,11 @@ virtual void SaveFileDialog (IAvnWindow* parentWindowHandle,
auto handler = ^(NSModalResponse result) {
if(result == NSFileHandlingPanelOKButton)
{
void* strings[1];

auto url = [panel URL];

auto string = [url path];
strings[0] = (void*)[string UTF8String];

events->OnCompleted(1, &strings[0]);

auto urls = [NSArray<NSURL*> arrayWithObject:url];
auto uriStrings = CreateAvnStringArray(urls);
events->OnCompleted(uriStrings);

[panel orderOut:panel];

if(parentWindowHandle != nullptr)
Expand All @@ -284,7 +339,7 @@ virtual void SaveFileDialog (IAvnWindow* parentWindowHandle,
return;
}

events->OnCompleted(0, nullptr);
events->OnCompleted(nullptr);

};

Expand Down Expand Up @@ -519,7 +574,7 @@ void SetAccessoryView(NSSavePanel* panel,
};
};

extern IAvnSystemDialogs* CreateSystemDialogs()
extern IAvnStorageProvider* CreateStorageProvider()
{
return new SystemDialogs();
return new StorageProvider();
}
2 changes: 1 addition & 1 deletion native/Avalonia.Native/src/OSX/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ extern void PostDispatcherCallback(IAvnActionCallback* cb);
extern IAvnTopLevel* CreateAvnTopLevel(IAvnTopLevelEvents* events);
extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events);
extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events);
extern IAvnSystemDialogs* CreateSystemDialogs();
extern IAvnStorageProvider* CreateStorageProvider();
extern IAvnScreens* CreateScreens();
extern IAvnClipboard* CreateClipboard(NSPasteboard*, NSPasteboardItem*);
extern NSPasteboardItem* TryGetPasteboardItem(IAvnClipboard*);
Expand Down
4 changes: 2 additions & 2 deletions native/Avalonia.Native/src/OSX/main.mm
Original file line number Diff line number Diff line change
Expand Up @@ -276,13 +276,13 @@ virtual HRESULT CreatePlatformThreadingInterface(IAvnPlatformThreadingInterface*
}
}

virtual HRESULT CreateSystemDialogs(IAvnSystemDialogs** ppv) override
virtual HRESULT CreateStorageProvider(IAvnStorageProvider** ppv) override
{
START_COM_CALL;

@autoreleasepool
{
*ppv = ::CreateSystemDialogs();
*ppv = ::CreateStorageProvider();
return S_OK;
}
}
Expand Down
50 changes: 34 additions & 16 deletions samples/ControlCatalog/Pages/DialogsPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,17 +254,24 @@ List<FileDialogFilter> GetFilters()

if (file is not null)
{
// Sync disposal of StreamWriter is not supported on WASM
try
{
// Sync disposal of StreamWriter is not supported on WASM
#if NET6_0_OR_GREATER
await using var stream = await file.OpenWriteAsync();
await using var writer = new System.IO.StreamWriter(stream);
await using var stream = await file.OpenWriteAsync();
await using var writer = new System.IO.StreamWriter(stream);
#else
using var stream = await file.OpenWriteAsync();
using var writer = new System.IO.StreamWriter(stream);
using var stream = await file.OpenWriteAsync();
using var writer = new System.IO.StreamWriter(stream);
#endif
await writer.WriteLineAsync(openedFileContent.Text);
await writer.WriteLineAsync(openedFileContent.Text);

SetFolder(await file.GetParentAsync());
SetFolder(await file.GetParentAsync());
}
catch (Exception ex)
{
openedFileContent.Text = ex.ToString();
}
}

await SetPickerResult(file is null ? null : new[] { file });
Expand All @@ -280,8 +287,6 @@ List<FileDialogFilter> GetFilters()
});

await SetPickerResult(folders);

SetFolder(folders.FirstOrDefault());
};
this.Get<Button>("OpenFileFromBookmark").Click += async delegate
{
Expand All @@ -298,7 +303,6 @@ List<FileDialogFilter> GetFilters()
: null;

await SetPickerResult(folder is null ? null : new[] { folder });
SetFolder(folder);
};

this.Get<Button>("LaunchUri").Click += async delegate
Expand Down Expand Up @@ -360,16 +364,30 @@ async Task SetPickerResult(IReadOnlyCollection<IStorageItem>? items)
Content:
";

resultText += await ReadTextFromFile(file, 500);
try
{
resultText += await ReadTextFromFile(file, 500);
}
catch (Exception ex)
{
resultText += ex.ToString();
}
}

openedFileContent.Text = resultText;

var parent = await item.GetParentAsync();
SetFolder(parent);
if (parent is not null)
if (item is IStorageFolder storageFolder)
{
mappedResults.Add(FullPathOrName(parent));
SetFolder(storageFolder);
}
else
{
var parent = await item.GetParentAsync();
SetFolder(parent);
if (parent is not null)
{
mappedResults.Add(FullPathOrName(parent));
}
}

foreach (var selectedItem in items)
Expand All @@ -391,7 +409,7 @@ async Task SetPickerResult(IReadOnlyCollection<IStorageItem>? items)
}
}

public static async Task<string> ReadTextFromFile(IStorageFile file, int length)
internal static async Task<string> ReadTextFromFile(IStorageFile file, int length)
{
#if NET6_0_OR_GREATER
await using var stream = await file.OpenReadAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Android.Webkit;
using Avalonia.Logging;
using Avalonia.Platform.Storage;
using Avalonia.Platform.Storage.FileIO;
using Java.Lang;
using AndroidUri = Android.Net.Uri;
using Exception = System.Exception;
Expand Down Expand Up @@ -53,7 +54,8 @@ protected AndroidStorageItem(Activity activity, AndroidUri uri, bool needsExtern
}

Activity.ContentResolver?.TakePersistableUriPermission(Uri, ActivityFlags.GrantWriteUriPermission | ActivityFlags.GrantReadUriPermission);
return Uri.ToString();

return StorageBookmarkHelper.EncodeBookmark(AndroidStorageProvider.AndroidKey, Uri.ToString()!);
}

public async Task ReleaseBookmarkAsync()
Expand Down
Loading
Loading