Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Next Next commit
fix: added a has-item method
  • Loading branch information
denissb committed Jan 15, 2021
commit 8e95e322ac2bd8425759235cd598885f6bc606f3
9 changes: 9 additions & 0 deletions android/src/main/java/dev/mcodex/RNSensitiveInfoModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,15 @@ public void getItem(String key, ReadableMap options, Promise pm) {
}
}

@ReactMethod
public void hasItem(String key, ReadableMap options, Promise pm) {
String name = sharedPreferences(options);

String value = prefs(name).getString(key, null);

pm.resolve(value != null ? true : false);
}

@ReactMethod
public void setItem(String key, String value, ReadableMap options, Promise pm) {

Expand Down
22 changes: 22 additions & 0 deletions example/src/pages/Home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,27 @@ const Home: React.FC = () => {
}
}, []);

const hasTouchIDItem = useCallback(async () => {

try {
const hasItem = await SInfo.hasItem(
'touchIdItem',
{
sharedPreferencesName: 'exampleApp',
keychainService: 'exampleApp',
kSecAccessControl: 'kSecAccessControlBiometryAny', // Enabling FaceID
touchID: true,
showModal: true,
},
);

Alert.alert(hasItem ? "Item is present" : "item is not present");
} catch (ex) {
Alert.alert('Error', ex.message);
}
}, []);


const getTouchIDItem = useCallback(async () => {
const deviceHasSensor = await SInfo.isSensorAvailable();

Expand Down Expand Up @@ -109,6 +130,7 @@ const Home: React.FC = () => {
/>

<Button title="Get TouchID Data" onPress={getTouchIDItem} />
<Button title="Has TouchID Data" onPress={hasTouchIDItem} />

<Text>{logText}</Text>
</View>
Expand Down
4 changes: 4 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export declare function getItem(
key: string,
options: RNSensitiveInfoOptions,
): Promise<string>;
export declare function hasItem(
key: string,
options: RNSensitiveInfoOptions,
): Promise<boolean>;

interface SensitiveInfoEntry {
key: string;
Expand Down
Binary file not shown.
46 changes: 45 additions & 1 deletion ios/RNSensitiveInfo/RNSensitiveInfo.m
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,51 @@ - (NSString *)messageForError:(NSError *)error
});
}

RCT_EXPORT_METHOD(hasItem:(NSString *)key options:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){
NSString * keychainService = [RCTConvert NSString:options[@"keychainService"]];
if (keychainService == NULL) {
keychainService = @"app";
}

// Create dictionary of search parameters
NSMutableDictionary* query = [NSMutableDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassGenericPassword), kSecClass,
keychainService, kSecAttrService,
key, kSecAttrAccount,
kSecAttrSynchronizableAny, kSecAttrSynchronizable,
kCFBooleanTrue, kSecReturnAttributes,
kCFBooleanTrue, kSecReturnData,
nil];


dispatch_async(dispatch_get_main_queue(), ^{
if (UIApplication.sharedApplication.protectedDataAvailable) {
// Look up server in the keychain
NSDictionary* found = nil;
CFTypeRef foundTypeRef = NULL;
OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef) query, (CFTypeRef*)&foundTypeRef);

if (osStatus != noErr && osStatus != errSecItemNotFound) {
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
reject([NSString stringWithFormat:@"%ld",(long)error.code], [self messageForError:error], nil);
return;
}

found = (__bridge NSDictionary*)(foundTypeRef);
if (!found) {
resolve(@(FALSE));
} else {
// Found
resolve(@(TRUE));
}
} else {
// TODO: could change to instead of erroring out, listen for protectedDataDidBecomeAvailable and call getItemWIthQuery when it does
// Experiment for now by returning an error and let the js side retry
reject(@"protected_data_unavailable", @"Protected data not available yet. Retry operation", nil);
}
});
}


- (void)getItemWithQuery:(NSDictionary *)query resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
// Look up server in the keychain
NSDictionary* found = nil;
Expand All @@ -245,7 +290,6 @@ - (void)getItemWithQuery:(NSDictionary *)query resolver:(RCTPromiseResolveBlock)
// Found
NSString* value = [[NSString alloc] initWithData:[found objectForKey:(__bridge id)(kSecValueData)] encoding:NSUTF8StringEncoding];
resolve(value);

}
}

Expand Down