diff --git a/docs/CMake API.md b/docs/CMake API.md index a2fecc77ff..c1e90ae122 100644 --- a/docs/CMake API.md +++ b/docs/CMake API.md @@ -427,6 +427,22 @@ attributes directly to these creation functions, rather than adding them later. plist if `APP_SANDBOX_ENABLED` is `TRUE`. Each key should be in the form `com.apple.security.*` where `*` is a specific entitlement. +`APP_SANDBOX_FILE_ACCESS_HOME_RO` +- A set of space-separated paths that will be added to this target's entitlements plist for + accessing read-only paths relative to the home directory if `APP_SANDBOX_ENABLED` is `TRUE`. + +`APP_SANDBOX_FILE_ACCESS_HOME_RW` +- A set of space-separated paths that will be added to this target's entitlements plist for + accessing read/write paths relative to the home directory if `APP_SANDBOX_ENABLED` is `TRUE`. + +`APP_SANDBOX_FILE_ACCESS_ABS_RO` +- A set of space-separated paths that will be added to this target's entitlements plist for + accessing read-only absolute paths if `APP_SANDBOX_ENABLED` is `TRUE`. + +`APP_SANDBOX_FILE_ACCESS_ABS_RW` +- A set of space-separated paths that will be added to this target's entitlements plist for + accessing read/write absolute paths if `APP_SANDBOX_ENABLED` is `TRUE`. + `PLIST_TO_MERGE` - A string to insert into an app/plugin's Info.plist. diff --git a/extras/Build/CMake/JUCEUtils.cmake b/extras/Build/CMake/JUCEUtils.cmake index 0bcb2e8b2c..245fe233e6 100644 --- a/extras/Build/CMake/JUCEUtils.cmake +++ b/extras/Build/CMake/JUCEUtils.cmake @@ -318,6 +318,10 @@ function(_juce_write_configure_time_info target) _juce_append_target_property(file_content APP_SANDBOX_INHERIT ${target} JUCE_APP_SANDBOX_INHERIT) _juce_append_target_property(file_content HARDENED_RUNTIME_OPTIONS ${target} JUCE_HARDENED_RUNTIME_OPTIONS) _juce_append_target_property(file_content APP_SANDBOX_OPTIONS ${target} JUCE_APP_SANDBOX_OPTIONS) + _juce_append_target_property(file_content APP_SANDBOX_FILE_ACCESS_HOME_RO ${target} JUCE_APP_SANDBOX_FILE_ACCESS_HOME_RO) + _juce_append_target_property(file_content APP_SANDBOX_FILE_ACCESS_HOME_RW ${target} JUCE_APP_SANDBOX_FILE_ACCESS_HOME_RW) + _juce_append_target_property(file_content APP_SANDBOX_FILE_ACCESS_ABS_RO ${target} JUCE_APP_SANDBOX_FILE_ACCESS_ABS_RO) + _juce_append_target_property(file_content APP_SANDBOX_FILE_ACCESS_ABS_RW ${target} JUCE_APP_SANDBOX_FILE_ACCESS_ABS_RW) _juce_append_target_property(file_content APP_GROUPS_ENABLED ${target} JUCE_APP_GROUPS_ENABLED) _juce_append_target_property(file_content APP_GROUP_IDS ${target} JUCE_APP_GROUP_IDS) _juce_append_target_property(file_content IS_PLUGIN ${target} JUCE_IS_PLUGIN) @@ -1546,6 +1550,10 @@ function(_juce_initialise_target target) VST3_CATEGORIES HARDENED_RUNTIME_OPTIONS APP_SANDBOX_OPTIONS + APP_SANDBOX_FILE_ACCESS_HOME_RO + APP_SANDBOX_FILE_ACCESS_HOME_RW + APP_SANDBOX_FILE_ACCESS_ABS_RO + APP_SANDBOX_FILE_ACCESS_ABS_RW DOCUMENT_EXTENSIONS AAX_CATEGORY IPHONE_SCREEN_ORIENTATIONS # iOS only diff --git a/extras/Build/juce_build_tools/utils/juce_Entitlements.cpp b/extras/Build/juce_build_tools/utils/juce_Entitlements.cpp index bb4c02f1d8..faac95a587 100644 --- a/extras/Build/juce_build_tools/utils/juce_Entitlements.cpp +++ b/extras/Build/juce_build_tools/utils/juce_Entitlements.cpp @@ -79,7 +79,7 @@ namespace build_tools if (isAppGroupsEnabled) { auto appGroups = StringArray::fromTokens (appGroupIdString, ";", {}); - auto groups = String (""); + String groups = ""; for (auto group : appGroups) groups += "\n\t\t" + group.trim() + ""; @@ -101,13 +101,27 @@ namespace build_tools { // no other sandbox options can be specified if sandbox inheritance is enabled! jassert (appSandboxOptions.isEmpty()); + jassert (appSandboxTemporaryPaths.empty()); entitlements.set ("com.apple.security.inherit", ""); } if (isAppSandboxEnabled) + { for (auto& option : appSandboxOptions) entitlements.set (option, ""); + + for (auto& option : appSandboxTemporaryPaths) + { + String paths = ""; + + for (const auto& path : option.values) + paths += "\n\t\t" + path + ""; + + paths += "\n\t"; + entitlements.set (option.key, paths); + } + } } if (isNetworkingMulticastEnabled) diff --git a/extras/Build/juce_build_tools/utils/juce_Entitlements.h b/extras/Build/juce_build_tools/utils/juce_Entitlements.h index b7d1538597..a706fd355f 100644 --- a/extras/Build/juce_build_tools/utils/juce_Entitlements.h +++ b/extras/Build/juce_build_tools/utils/juce_Entitlements.h @@ -49,6 +49,14 @@ namespace build_tools StringArray hardenedRuntimeOptions; StringArray appSandboxOptions; + struct KeyAndStringArray + { + String key; + StringArray values; + }; + + std::vector appSandboxTemporaryPaths; + private: StringPairArray getEntitlements() const; }; diff --git a/extras/Build/juceaide/Main.cpp b/extras/Build/juceaide/Main.cpp index cdfcda1dba..4a9e61e62d 100644 --- a/extras/Build/juceaide/Main.cpp +++ b/extras/Build/juceaide/Main.cpp @@ -343,6 +343,29 @@ juce::build_tools::EntitlementOptions parseEntitlementsOptions (const juce::File updateField ("APP_SANDBOX_OPTIONS", result.appSandboxOptions); updateField ("NETWORK_MULTICAST_ENABLED", result.isNetworkingMulticastEnabled); + struct SandboxTemporaryAccessKey + { + juce::String cMakeVar, key; + }; + + SandboxTemporaryAccessKey sandboxTemporaryAccessKeys[] + { + { "APP_SANDBOX_FILE_ACCESS_HOME_RO", "home-relative-path.read-only" }, + { "APP_SANDBOX_FILE_ACCESS_HOME_RW", "home-relative-path.read-write" }, + { "APP_SANDBOX_FILE_ACCESS_ABS_RO", "absolute-path.read-only" }, + { "APP_SANDBOX_FILE_ACCESS_ABS_RW", "absolute-path.read-write" } + }; + + for (const auto& entry : sandboxTemporaryAccessKeys) + { + juce::StringArray values; + updateField (entry.cMakeVar, values); + + if (! values.isEmpty()) + result.appSandboxTemporaryPaths.push_back ({ "com.apple.security.temporary-exception.files." + entry.key, + std::move (values) }); + } + result.type = type; return result; diff --git a/extras/Projucer/Source/Project/UI/jucer_ContentViewComponents.h b/extras/Projucer/Source/Project/UI/jucer_ContentViewComponents.h index ab970eac39..e6c79f8e4f 100644 --- a/extras/Projucer/Source/Project/UI/jucer_ContentViewComponents.h +++ b/extras/Projucer/Source/Project/UI/jucer_ContentViewComponents.h @@ -302,8 +302,7 @@ public: for (auto& pp : properties) { - const auto propertyHeight = pp->getPreferredHeight() - + (getHeightMultiplier (pp.get()) * pp->getPreferredHeight()); + const auto propertyHeight = jmax (pp->getPreferredHeight(), getApproximateLabelHeight (*pp)); auto iter = std::find_if (propertyComponentsWithInfo.begin(), propertyComponentsWithInfo.end(), [&pp] (const std::unique_ptr& w) { return &w->propertyComponent == pp.get(); }); @@ -418,17 +417,17 @@ private: } } - int getHeightMultiplier (PropertyComponent* pp) + static int getApproximateLabelHeight (const PropertyComponent& pp) { auto availableTextWidth = ProjucerLookAndFeel::getTextWidthForPropertyComponent (pp); - auto font = ProjucerLookAndFeel::getPropertyComponentFont(); - auto nameWidth = font.getStringWidthFloat (pp->getName()); - if (availableTextWidth == 0) return 0; - return static_cast (nameWidth / (float) availableTextWidth); + const auto font = ProjucerLookAndFeel::getPropertyComponentFont(); + const auto labelWidth = font.getStringWidthFloat (pp.getName()); + const auto numLines = (int) (labelWidth / (float) availableTextWidth) + 1; + return (int) std::round ((float) numLines * font.getHeight() * 1.1f); } //============================================================================== diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h index f5333e283a..7723b12ccb 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h @@ -74,6 +74,10 @@ public: appSandboxValue (settings, Ids::appSandbox, getUndoManager()), appSandboxInheritanceValue (settings, Ids::appSandboxInheritance, getUndoManager()), appSandboxOptionsValue (settings, Ids::appSandboxOptions, getUndoManager(), Array(), ","), + appSandboxHomeDirROValue (settings, Ids::appSandboxHomeDirRO, getUndoManager()), + appSandboxHomeDirRWValue (settings, Ids::appSandboxHomeDirRW, getUndoManager()), + appSandboxAbsDirROValue (settings, Ids::appSandboxAbsDirRO, getUndoManager()), + appSandboxAbsDirRWValue (settings, Ids::appSandboxAbsDirRW, getUndoManager()), hardenedRuntimeValue (settings, Ids::hardenedRuntime, getUndoManager()), hardenedRuntimeOptionsValue (settings, Ids::hardenedRuntimeOptions, getUndoManager(), Array(), ","), microphonePermissionNeededValue (settings, Ids::microphonePermissionNeeded, getUndoManager()), @@ -173,6 +177,21 @@ public: bool isAppSandboxInhertianceEnabled() const { return appSandboxInheritanceValue.get(); } Array getAppSandboxOptions() const { return *appSandboxOptionsValue.get().getArray(); } + auto getAppSandboxTemporaryPaths() const + { + std::vector result; + + for (const auto& entry : sandboxFileAccessProperties) + { + auto paths = getCommaOrWhitespaceSeparatedItems (entry.property.get()); + + if (! paths.isEmpty()) + result.push_back ({ "com.apple.security.temporary-exception.files." + entry.key, std::move (paths) }); + } + + return result; + } + Array getValidArchs() const { return *validArchsValue.get().getArray(); } bool isMicrophonePermissionEnabled() const { return microphonePermissionNeededValue.get(); } @@ -457,29 +476,36 @@ public: { "Temporary Exception: Audio Unit Hosting", "temporary-exception.audio-unit-host" }, { "Temporary Exception: Global Mach Service", "temporary-exception.mach-lookup.global-name" }, { "Temporary Exception: Global Mach Service Dynamic Registration", "temporary-exception.mach-register.global-name" }, - { "Temporary Exception: Home Directory File Access (Read Only)", "temporary-exception.files.home-relative-path.read-only" }, - { "Temporary Exception: Home Directory File Access (Read/Write)", "temporary-exception.files.home-relative-path.read-write" }, - { "Temporary Exception: Absolute Path File Access (Read Only)", "temporary-exception.files.absolute-path.read-only" }, - { "Temporary Exception: Absolute Path File Access (Read/Write)", "temporary-exception.files.absolute-path.read-write" }, { "Temporary Exception: IOKit User Client Class", "temporary-exception.iokit-user-client-class" }, { "Temporary Exception: Shared Preference Domain (Read Only)", "temporary-exception.shared-preference.read-only" }, { "Temporary Exception: Shared Preference Domain (Read/Write)", "temporary-exception.shared-preference.read-write" } }; StringArray sandboxKeys; - Array sanboxValues; + Array sandboxValues; for (auto& opt : sandboxOptions) { sandboxKeys.add (opt.first); - sanboxValues.add ("com.apple.security." + opt.second); + sandboxValues.add ("com.apple.security." + opt.second); } props.add (new MultiChoicePropertyComponentWithEnablement (appSandboxOptionsValue, appSandboxValue, "App Sandbox Options", sandboxKeys, - sanboxValues)); + sandboxValues)); + + for (const auto& entry : sandboxFileAccessProperties) + { + props.add (new TextPropertyComponentWithEnablement (entry.property, + appSandboxValue, + entry.label, + 8192, + true), + "A list of the corresponding paths (separated by newlines or whitespace). " + "See Apple's File Access Temporary Exceptions documentation."); + } props.add (new ChoicePropertyComponent (hardenedRuntimeValue, "Use Hardened Runtime"), "Enable this to use the hardened runtime required for app notarization."); @@ -3085,6 +3111,7 @@ private: options.appGroupIdString = getAppGroupIdString(); options.hardenedRuntimeOptions = getHardenedRuntimeOptions(); options.appSandboxOptions = getAppSandboxOptions(); + options.appSandboxTemporaryPaths = getAppSandboxTemporaryPaths(); const auto entitlementsFile = getTargetFolder().getChildFile (target.getEntitlementsFilename()); build_tools::overwriteFileIfDifferentOrThrow (entitlementsFile, options.getEntitlementsFileContent()); @@ -3566,6 +3593,7 @@ private: duplicateAppExResourcesFolderValue, iosDeviceFamilyValue, iPhoneScreenOrientationValue, iPadScreenOrientationValue, customXcodeResourceFoldersValue, customXcassetsFolderValue, appSandboxValue, appSandboxInheritanceValue, appSandboxOptionsValue, + appSandboxHomeDirROValue, appSandboxHomeDirRWValue, appSandboxAbsDirROValue, appSandboxAbsDirRWValue, hardenedRuntimeValue, hardenedRuntimeOptionsValue, microphonePermissionNeededValue, microphonePermissionsTextValue, cameraPermissionNeededValue, cameraPermissionTextValue, @@ -3576,5 +3604,19 @@ private: networkingMulticastValue, iosDevelopmentTeamIDValue, iosAppGroupsIDValue, keepCustomXcodeSchemesValue, useHeaderMapValue, customLaunchStoryboardValue, exporterBundleIdentifierValue, suppressPlistResourceUsageValue, useLegacyBuildSystemValue, buildNumber; + struct SandboxFileAccessProperty + { + const ValueTreePropertyWithDefault& property; + const String label, key; + }; + + const std::vector sandboxFileAccessProperties + { + { appSandboxHomeDirROValue, "App sandbox temporary exception: home directory read only file access", "home-relative-path.read-only" }, + { appSandboxHomeDirRWValue, "App sandbox temporary exception: home directory read/write file access", "home-relative-path.read-write" }, + { appSandboxAbsDirROValue, "App sandbox temporary exception: absolute path read only file access", "absolute-path.read-only" }, + { appSandboxAbsDirRWValue, "App sandbox temporary exception: absolute path read/write file access", "absolute-path.read-write" } + }; + JUCE_DECLARE_NON_COPYABLE (XcodeProjectExporter) }; diff --git a/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h b/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h index 018706b09c..a502beb990 100644 --- a/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h +++ b/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h @@ -196,6 +196,10 @@ namespace Ids DECLARE_ID (appSandbox); DECLARE_ID (appSandboxInheritance); DECLARE_ID (appSandboxOptions); + DECLARE_ID (appSandboxHomeDirRO); + DECLARE_ID (appSandboxHomeDirRW); + DECLARE_ID (appSandboxAbsDirRO); + DECLARE_ID (appSandboxAbsDirRW); DECLARE_ID (hardenedRuntime); DECLARE_ID (hardenedRuntimeOptions); DECLARE_ID (microphonePermissionNeeded); diff --git a/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.cpp b/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.cpp index ead1009214..943521b80c 100644 --- a/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.cpp +++ b/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.cpp @@ -94,23 +94,21 @@ int ProjucerLookAndFeel::getTabButtonBestWidth (TabBarButton& button, int) return 120; } -void ProjucerLookAndFeel::drawPropertyComponentLabel (Graphics& g, int width, int height, PropertyComponent& component) +void ProjucerLookAndFeel::drawPropertyComponentLabel (Graphics& g, int, int height, PropertyComponent& component) { - ignoreUnused (width); - g.setColour (component.findColour (defaultTextColourId) .withMultipliedAlpha (component.isEnabled() ? 1.0f : 0.6f)); - auto textWidth = getTextWidthForPropertyComponent (&component); + auto textWidth = getTextWidthForPropertyComponent (component); g.setFont (getPropertyComponentFont()); - g.drawFittedText (component.getName(), 0, 0, textWidth - 5, height, Justification::centredLeft, 5, 1.0f); + g.drawFittedText (component.getName(), 0, 0, textWidth, height, Justification::centredLeft, 5, 1.0f); } Rectangle ProjucerLookAndFeel::getPropertyComponentContentPosition (PropertyComponent& component) { - const auto textW = getTextWidthForPropertyComponent (&component); - return { textW, 0, component.getWidth() - textW, component.getHeight() - 1 }; + const auto paddedTextW = getTextWidthForPropertyComponent (component) + 5; + return { paddedTextW , 0, component.getWidth() - paddedTextW, component.getHeight() - 1 }; } void ProjucerLookAndFeel::drawButtonBackground (Graphics& g, diff --git a/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.h b/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.h index 33b25809dc..c9a7745377 100644 --- a/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.h +++ b/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.h @@ -81,8 +81,8 @@ public: const bool filled, const Justification justification); static Path getChoiceComponentArrowPath (Rectangle arrowZone); - static Font getPropertyComponentFont() { return { 14.0f, Font::FontStyleFlags::bold }; } - static int getTextWidthForPropertyComponent (PropertyComponent* pp) { return jmin (200, pp->getWidth() / 2); } + static Font getPropertyComponentFont() { return { 14.0f, Font::FontStyleFlags::bold }; } + static int getTextWidthForPropertyComponent (const PropertyComponent& pc) { return jmin (200, pc.getWidth() / 2); } static ColourScheme getProjucerDarkColourScheme() {