Skip to content
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

[fix/share-sheet-biometrical-unlock] Biometrical Unlock in Share Sheet #1129

Merged
merged 8 commits into from
Aug 1, 2022
2 changes: 1 addition & 1 deletion ios-sdk
2 changes: 2 additions & 0 deletions ownCloud Share Extension/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
<string>group.com.owncloud.ios-app</string>
<key>OCAppIdentifierPrefix</key>
<string>$(AppIdentifierPrefix)</string>
<key>OCAppComponentIdentifier</key>
<string>shareExtension</string>
<key>OCHasFileProvider</key>
<true/>
<key>OCKeychainAccessGroupIdentifier</key>
Expand Down
8 changes: 8 additions & 0 deletions ownCloud Share Extension/ShareNavigationController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ class ShareNavigationController: AppExtensionNavigationController {
self.setViewControllers([viewController], animated: false)
}
}

override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)

OCAppIdentity.shared.hostAppBundleIdentifier = parent?.oc_hostAppBundleIdentifier

Log.debug("Extension Host App Bundle ID: \(OCAppIdentity.shared.hostAppBundleIdentifier ?? "nil")")
}
}

extension UserInterfaceContext : UserInterfaceContextProvider {
Expand Down
16 changes: 16 additions & 0 deletions ownCloud.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,8 @@
DC3BE0D82077BC5D002A0AC0 /* ownCloudSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 239369782076110900BCE21A /* ownCloudSDK.framework */; };
DC3BE0DA2077BC6B002A0AC0 /* ownCloudSDK.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 239369782076110900BCE21A /* ownCloudSDK.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
DC3BE0DF2077CC14002A0AC0 /* ClientRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3BE0DD2077CC13002A0AC0 /* ClientRootViewController.swift */; };
DC3DDF06287E1C0800E5586D /* UIViewController+HostBundleID.m in Sources */ = {isa = PBXBuildFile; fileRef = DC3DDF03287E1AC200E5586D /* UIViewController+HostBundleID.m */; };
DC3DDF07287E1C0E00E5586D /* UIViewController+HostBundleID.h in Headers */ = {isa = PBXBuildFile; fileRef = DC3DDF02287E1AC200E5586D /* UIViewController+HostBundleID.h */; settings = {ATTRIBUTES = (Public, ); }; };
DC3DEC7B22AFA1F000F3352D /* DownloadItemsHUDViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3DEC7A22AFA1F000F3352D /* DownloadItemsHUDViewController.swift */; };
DC3F4522271A23A000ED2383 /* AcknowledgementsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3F4521271A23A000ED2383 /* AcknowledgementsTableViewController.swift */; };
DC4332002472E1B4002DC0E5 /* OCLicenseEMMProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = DC4331FE2472E1B4002DC0E5 /* OCLicenseEMMProvider.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -1239,6 +1241,8 @@
DC3BE0DC2077CC13002A0AC0 /* ClientQueryViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientQueryViewController.swift; sourceTree = "<group>"; };
DC3BE0DD2077CC13002A0AC0 /* ClientRootViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientRootViewController.swift; sourceTree = "<group>"; };
DC3BE0E02077CD4B002A0AC0 /* Synchronized.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Synchronized.swift; sourceTree = "<group>"; };
DC3DDF02287E1AC200E5586D /* UIViewController+HostBundleID.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+HostBundleID.h"; sourceTree = "<group>"; };
DC3DDF03287E1AC200E5586D /* UIViewController+HostBundleID.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+HostBundleID.m"; sourceTree = "<group>"; };
DC3DEC7A22AFA1F000F3352D /* DownloadItemsHUDViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadItemsHUDViewController.swift; sourceTree = "<group>"; };
DC3DEC7C22AFFE8E00F3352D /* KVOWaiter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KVOWaiter.swift; sourceTree = "<group>"; };
DC3DEC7F22B03AE700F3352D /* CardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2329,6 +2333,15 @@
path = Client;
sourceTree = "<group>";
};
DC3DDEFE287E1AA500E5586D /* UIKit Extensions */ = {
isa = PBXGroup;
children = (
DC3DDF03287E1AC200E5586D /* UIViewController+HostBundleID.m */,
DC3DDF02287E1AC200E5586D /* UIViewController+HostBundleID.h */,
);
path = "UIKit Extensions";
sourceTree = "<group>";
};
DC422448207CAED60006A2A6 /* Theming */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -2651,6 +2664,7 @@
DCC832E5242CB14E00153F8C /* Notifications */,
DC774E5422F44DF6000B11A1 /* SDK Extensions */,
DC0030BE2350B1CE00BB8570 /* Tools */,
DC3DDEFE287E1AA500E5586D /* UIKit Extensions */,
DC774E5B22F44E4A000B11A1 /* ZIP Archive */,
DC774E6522F44EA7000B11A1 /* Resources */,
);
Expand Down Expand Up @@ -3157,6 +3171,7 @@
DCFEFE2A236876BD009A142F /* OCLicenseManager.h in Headers */,
DCFEFE4923687C83009A142F /* OCLicenseEntitlement.h in Headers */,
DC4332002472E1B4002DC0E5 /* OCLicenseEMMProvider.h in Headers */,
DC3DDF07287E1C0E00E5586D /* UIViewController+HostBundleID.h in Headers */,
DCFEFE39236877A7009A142F /* OCLicenseFeature.h in Headers */,
DC23D1DA238F391200423F62 /* OCLicenseAppStoreReceipt.h in Headers */,
DC70398526128B89009F2DC1 /* NSString+ByteCountParser.h in Headers */,
Expand Down Expand Up @@ -4292,6 +4307,7 @@
DCFEFE50236880B5009A142F /* OCLicenseOffer.m in Sources */,
DC0030C12350B1CE00BB8570 /* NSData+Encoding.m in Sources */,
DC774E5F22F44E57000B11A1 /* ZIPArchive.m in Sources */,
DC3DDF06287E1C0800E5586D /* UIViewController+HostBundleID.m in Sources */,
DCDBB60B2525306000FAD707 /* NotificationAuthErrorForwarder.m in Sources */,
DCD71E8027427463001592C6 /* BuildOptions.m in Sources */,
DC080CE5238AE3F40044C5D2 /* OCLicenseAppStoreProvider.m in Sources */,
Expand Down
3 changes: 3 additions & 0 deletions ownCloudAppFramework/AppLock Settings/AppLockSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ NS_ASSUME_NONNULL_BEGIN
@property(assign,nonatomic) BOOL lockEnabled;
@property(assign,nonatomic) NSInteger lockDelay;
@property(assign,nonatomic) BOOL biometricalSecurityEnabled;
@property(assign,nonatomic) BOOL biometricalSecurityEnabledinShareSheet;
@property(readonly,nonatomic,nullable) NSURL *biometricalAuthenticationRedirectionTargetURL;

@property(readonly,nonatomic) BOOL isPasscodeEnforced;
@property(readonly,nonatomic) NSInteger requiredPasscodeDigits;
Expand All @@ -46,5 +48,6 @@ extern OCClassSettingsKey OCClassSettingsKeyRequiredPasscodeDigits;
extern OCClassSettingsKey OCClassSettingsKeyMaximumPasscodeDigits;
extern OCClassSettingsKey OCClassSettingsKeyPasscodeLockDelay;
extern OCClassSettingsKey OCClassSettingsKeyPasscodeUseBiometricalUnlock;
extern OCClassSettingsKey OCClassSettingsKeyPasscodeShareSheetBiometricalUnlockByApp;

NS_ASSUME_NONNULL_END
147 changes: 139 additions & 8 deletions ownCloudAppFramework/AppLock Settings/AppLockSettings.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/

#import "AppLockSettings.h"
#import "Branding.h"

@implementation AppLockSettings

Expand Down Expand Up @@ -54,7 +55,19 @@ + (OCClassSettingsIdentifier)classSettingsIdentifier
OCClassSettingsKeyPasscodeEnforced : @(NO),
OCClassSettingsKeyRequiredPasscodeDigits : @(4),
OCClassSettingsKeyMaximumPasscodeDigits : @(6),
OCClassSettingsKeyPasscodeUseBiometricalUnlock : @(NO)
OCClassSettingsKeyPasscodeUseBiometricalUnlock : @(NO),
OCClassSettingsKeyPasscodeShareSheetBiometricalUnlockByApp : @{
@"default" : @{
@"allow" : @(YES)
},

// For unknown reasons invoking biometric authentication from the
// share sheet in Boxer leads to dismissal of the entire share sheet,
// so (as of July 2022) we hardcode it as an exception here
@"com.air-watch.boxer" : @{
@"allow" : @(NO)
}
}
});
}

Expand Down Expand Up @@ -89,9 +102,16 @@ + (OCClassSettingsMetadataCollection)classSettingsMetadata
OCClassSettingsMetadataKeyCategory : @"Passcode"
},

OCClassSettingsKeyPasscodeUseBiometricalUnlock : @{
OCClassSettingsMetadataKeyType : OCClassSettingsMetadataTypeBoolean,
OCClassSettingsMetadataKeyDescription : @"Controls wether the biometrical unlock will be enabled automatically.",
OCClassSettingsKeyPasscodeUseBiometricalUnlock : @{
OCClassSettingsMetadataKeyType : OCClassSettingsMetadataTypeBoolean,
OCClassSettingsMetadataKeyDescription : @"Controls wether the biometrical unlock will be enabled automatically.",
OCClassSettingsMetadataKeyStatus : OCClassSettingsKeyStatusAdvanced,
OCClassSettingsMetadataKeyCategory : @"Passcode"
},

OCClassSettingsKeyPasscodeShareSheetBiometricalUnlockByApp : @{
OCClassSettingsMetadataKeyType : OCClassSettingsMetadataTypeDictionary,
OCClassSettingsMetadataKeyDescription : @"Controls the biometrical unlock availability in the share sheet, with per-app level control.",
OCClassSettingsMetadataKeyStatus : OCClassSettingsKeyStatusAdvanced,
OCClassSettingsMetadataKeyCategory : @"Passcode"
}
Expand Down Expand Up @@ -128,21 +148,131 @@ - (void)setLockDelay:(NSInteger)lockDelay

- (BOOL)biometricalSecurityEnabled
{
NSNumber *useBiometricalUnlock;
NSNumber *useBiometricalUnlockNumber;
BOOL useBiometricalUnlock = NO;

if ((useBiometricalUnlock = [_userDefaults objectForKey:@"security-settings-use-biometrical"]) != nil)
if ((useBiometricalUnlockNumber = [_userDefaults objectForKey:@"security-settings-use-biometrical"]) != nil)
{
return (useBiometricalUnlock.boolValue);
useBiometricalUnlock = useBiometricalUnlockNumber.boolValue;
}
else
{
useBiometricalUnlock = [[self classSettingForOCClassSettingsKey:OCClassSettingsKeyPasscodeUseBiometricalUnlock] boolValue];
}

return ([[self classSettingForOCClassSettingsKey:OCClassSettingsKeyPasscodeUseBiometricalUnlock] boolValue]);
if (useBiometricalUnlock)
{
// Apple share extension specific settings
if ([OCAppIdentity.sharedAppIdentity.componentIdentifier isEqual:OCAppComponentIdentifierShareExtension])
{
return ([self biometricalSecurityEnabledinShareSheet]);
}
}

return (useBiometricalUnlock);
}

- (void)setBiometricalSecurityEnabled:(BOOL)biometricalSecurityEnabled
{
[_userDefaults setBool:biometricalSecurityEnabled forKey:@"security-settings-use-biometrical"];
}

- (NSDictionary<NSString*,id> *)_shareSheetBiometricalAttributesForApp:(NSString *)hostAppID
{
NSDictionary<NSString*,NSDictionary *> *shareSheetBiometricalUnlockByApp = [self classSettingForOCClassSettingsKey:OCClassSettingsKeyPasscodeShareSheetBiometricalUnlockByApp];
NSDictionary<NSString*,id> *attributesForApp = nil;

if ([shareSheetBiometricalUnlockByApp isKindOfClass:NSDictionary.class])
{
if (shareSheetBiometricalUnlockByApp[hostAppID] != nil)
{
attributesForApp = OCTypedCast(shareSheetBiometricalUnlockByApp[hostAppID], NSDictionary);
}
else
{
attributesForApp = OCTypedCast(shareSheetBiometricalUnlockByApp[@"default"], NSDictionary);
}
}

return (attributesForApp);
}

- (NSDictionary<NSString*,id> *)_shareSheetBiometricalAttributes
{
NSString *hostAppID;

if ((hostAppID = OCAppIdentity.sharedAppIdentity.hostAppBundleIdentifier) == nil)
{
hostAppID = @"default";
}

return ([self _shareSheetBiometricalAttributesForApp:hostAppID]);
}

- (BOOL)biometricalSecurityEnabledinShareSheet
{
NSNumber *useBiometricalUnlock;

if ((useBiometricalUnlock = [_userDefaults objectForKey:@"security-settings-use-biometrical-share-sheet"]) != nil)
{
return (useBiometricalUnlock.boolValue);
}

NSDictionary<NSString*,id> *shareSheetAttributesForApp = nil;

if ((shareSheetAttributesForApp = [self _shareSheetBiometricalAttributes]) != nil)
{
NSNumber *enabled;

if ((enabled = OCTypedCast(shareSheetAttributesForApp[@"allow"], NSNumber)) != nil)
{
return (enabled.boolValue);
}
}

return (YES);
}

- (void)setBiometricalSecurityEnabledinShareSheet:(BOOL)biometricalSecurityEnabledinShareSheet
{
[_userDefaults setBool:biometricalSecurityEnabledinShareSheet forKey:@"security-settings-use-biometrical-share-sheet"];
}

- (NSURL *)biometricalAuthenticationRedirectionTargetURL
{
if ([OCAppIdentity.sharedAppIdentity.componentIdentifier isEqual:OCAppComponentIdentifierShareExtension])
{
// Only in share extension
NSDictionary<NSString*,id> *shareSheetAttributesForApp = nil;

if ((shareSheetAttributesForApp = [self _shareSheetBiometricalAttributes]) != nil)
{
NSString *trampolineURLString;

// For apps with a trampoline URL, determine the target URL to initiate the authentication trampoline
if ((trampolineURLString = shareSheetAttributesForApp[@"trampoline-url"]) != nil)
{
NSString *toAppURLScheme;

if ((toAppURLScheme = [Branding.sharedBranding appURLSchemesForBundleURLName:nil].firstObject) != nil)
{
NSString *targetURLString = [NSString stringWithFormat:@"%@://?authenticateForApp=%@", toAppURLScheme, OCAppIdentity.sharedAppIdentity.hostAppBundleIdentifier];

return ([NSURL URLWithString:targetURLString]);
}
}
}
}

return (nil);
}

// Counterpart to .biometricalAuthenticationRedirectionTargetURL for use in the app (not implemented)
//- (NSURL *)biometricalAuthenticationReturnURL
//{
// return (nil);
//}

- (BOOL)isPasscodeEnforced
{
NSNumber *isPasscodeEnforced = [self classSettingForOCClassSettingsKey:OCClassSettingsKeyPasscodeEnforced];
Expand Down Expand Up @@ -190,3 +320,4 @@ - (BOOL)lockDelayUserSettable
OCClassSettingsKey OCClassSettingsKeyMaximumPasscodeDigits = @"maximumPasscodeDigits";
OCClassSettingsKey OCClassSettingsKeyPasscodeLockDelay = @"lockDelay";
OCClassSettingsKey OCClassSettingsKeyPasscodeUseBiometricalUnlock = @"use-biometrical-unlock";
OCClassSettingsKey OCClassSettingsKeyPasscodeShareSheetBiometricalUnlockByApp = @"share-sheet-biometrical-unlock-by-app";
2 changes: 2 additions & 0 deletions ownCloudAppFramework/Branding/Branding.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ typedef NSString* BrandingImageName NS_TYPED_EXTENSIBLE_ENUM;

@property(strong,nullable,nonatomic,readonly) NSBundle *appBundle; //!< Bundle of the main app

- (NSArray<NSString *> *)appURLSchemesForBundleURLName:(nullable NSString *)bundleURLName; //!< URL schemes from the app's Info.plist matching the provided CFBundleURLName.

@property(strong) NSDictionary<OCClassSettingsKey, BrandingLegacyKeyPath> *legacyKeyPathsByClassSettingsKeys;
- (void)registerLegacyKeyPath:(BrandingLegacyKeyPath)keyPath forClassSettingsKey:(OCClassSettingsKey)classSettingsKey;

Expand Down
29 changes: 29 additions & 0 deletions ownCloudAppFramework/Branding/Branding.m
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,35 @@ - (NSDictionary *)userDefaultsDefaultValues
return ([self computedValueForClassSettingsKey:BrandingKeyUserDefaultsDefaultValues]);
}

- (NSArray<NSString *> *)appURLSchemesForBundleURLName:(nullable NSString *)bundleURLName
{
NSBundle *appBundle;
NSMutableArray<NSString *> *appURLSchemes = [NSMutableArray new];

if ((appBundle = self.appBundle) != nil)
{
NSArray<NSDictionary *> *urlSchemeDictionaries;

if ((urlSchemeDictionaries = [appBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]) != nil)
{
for (NSDictionary<NSString *, id> *urlSchemesDict in urlSchemeDictionaries)
{
if ((bundleURLName == nil) || [bundleURLName isEqual:urlSchemesDict[@"CFBundleURLName"]])
{
NSArray<NSString *> *urlSchemes;

if ((urlSchemes = urlSchemesDict[@"CFBundleURLSchemes"]) != nil)
{
[appURLSchemes addObjectsFromArray:urlSchemes];
}
}
}
}
}

return (appURLSchemes);
}

- (NSArray<BrandingFileImportMethod> *)disabledImportMethods
{
return ([self computedValueForClassSettingsKey:BrandingKeyDisabledImportMethods]);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// UIViewController+HostBundleID.h
// ownCloud
//
// Created by Felix Schwarz on 12.07.22.
// Copyright © 2022 ownCloud GmbH. All rights reserved.
//

/*
* Copyright (C) 2022, ownCloud GmbH.
*
* This code is covered by the GNU Public License Version 3.
*
* For distribution utilizing Apple mechanisms please see https://owncloud.org/contribute/iOS-license-exception/
* You should have received a copy of this license along with this program. If not, see <http://www.gnu.org/licenses/gpl-3.0.en.html>.
*
*/

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface UIViewController (HostBundleID)

@property(nullable,readonly) NSString *oc_hostAppBundleIdentifier;

@end

NS_ASSUME_NONNULL_END
Loading