mirror of
https://github.com/SoPat712/YTLitePlus.git
synced 2026-02-10 16:58:38 -05:00
added files via upload
This commit is contained in:
144
Tweaks/FLEX/GlobalStateExplorers/Keychain/FLEXKeychain.h
Normal file
144
Tweaks/FLEX/GlobalStateExplorers/Keychain/FLEXKeychain.h
Normal file
@@ -0,0 +1,144 @@
|
||||
//
|
||||
// FLEXKeychain.h
|
||||
//
|
||||
// Derived from:
|
||||
// SSKeychain.h in SSKeychain
|
||||
// Created by Sam Soffes on 5/19/10.
|
||||
// Copyright (c) 2010-2014 Sam Soffes. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/// Error code specific to FLEXKeychain that can be returned in NSError objects.
|
||||
/// For codes returned by the operating system, refer to SecBase.h for your
|
||||
/// platform.
|
||||
typedef NS_ENUM(OSStatus, FLEXKeychainErrorCode) {
|
||||
/// Some of the arguments were invalid.
|
||||
FLEXKeychainErrorBadArguments = -1001,
|
||||
};
|
||||
|
||||
/// FLEXKeychain error domain
|
||||
extern NSString *const kFLEXKeychainErrorDomain;
|
||||
|
||||
/// Account name.
|
||||
extern NSString *const kFLEXKeychainAccountKey;
|
||||
|
||||
/// Time the item was created.
|
||||
///
|
||||
/// The value will be a string.
|
||||
extern NSString *const kFLEXKeychainCreatedAtKey;
|
||||
|
||||
/// Item class.
|
||||
extern NSString *const kFLEXKeychainClassKey;
|
||||
|
||||
/// Item description.
|
||||
extern NSString *const kFLEXKeychainDescriptionKey;
|
||||
|
||||
/// Item group.
|
||||
extern NSString *const kFLEXKeychainGroupKey;
|
||||
|
||||
/// Item label.
|
||||
extern NSString *const kFLEXKeychainLabelKey;
|
||||
|
||||
/// Time the item was last modified.
|
||||
///
|
||||
/// The value will be a string.
|
||||
extern NSString *const kFLEXKeychainLastModifiedKey;
|
||||
|
||||
/// Where the item was created.
|
||||
extern NSString *const kFLEXKeychainWhereKey;
|
||||
|
||||
/// A simple wrapper for accessing accounts, getting passwords,
|
||||
/// setting passwords, and deleting passwords using the system Keychain.
|
||||
@interface FLEXKeychain : NSObject
|
||||
|
||||
#pragma mark - Classic methods
|
||||
|
||||
/// @param serviceName The service for which to return the corresponding password.
|
||||
/// @param account The account for which to return the corresponding password.
|
||||
/// @return Returns a string containing the password for a given account and service,
|
||||
/// or `nil` if the Keychain doesn't have a password for the given parameters.
|
||||
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;
|
||||
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
|
||||
|
||||
/// Returns a nsdata containing the password for a given account and service,
|
||||
/// or `nil` if the Keychain doesn't have a password for the given parameters.
|
||||
///
|
||||
/// @param serviceName The service for which to return the corresponding password.
|
||||
/// @param account The account for which to return the corresponding password.
|
||||
/// @return Returns a nsdata containing the password for a given account and service,
|
||||
/// or `nil` if the Keychain doesn't have a password for the given parameters.
|
||||
+ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account;
|
||||
+ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
|
||||
|
||||
|
||||
/// Deletes a password from the Keychain.
|
||||
///
|
||||
/// @param serviceName The service for which to delete the corresponding password.
|
||||
/// @param account The account for which to delete the corresponding password.
|
||||
/// @return Returns `YES` on success, or `NO` on failure.
|
||||
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account;
|
||||
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
|
||||
|
||||
|
||||
/// Sets a password in the Keychain.
|
||||
///
|
||||
/// @param password The password to store in the Keychain.
|
||||
/// @param serviceName The service for which to set the corresponding password.
|
||||
/// @param account The account for which to set the corresponding password.
|
||||
/// @return Returns `YES` on success, or `NO` on failure.
|
||||
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;
|
||||
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
|
||||
|
||||
/// Sets a password in the Keychain.
|
||||
///
|
||||
/// @param password The password to store in the Keychain.
|
||||
/// @param serviceName The service for which to set the corresponding password.
|
||||
/// @param account The account for which to set the corresponding password.
|
||||
/// @return Returns `YES` on success, or `NO` on failure.
|
||||
+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account;
|
||||
+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
|
||||
|
||||
/// @return An array of dictionaries containing the Keychain's accounts, or `nil` if
|
||||
/// the Keychain doesn't have any accounts. The order of the objects in the array isn't defined.
|
||||
///
|
||||
/// @note See the `NSString` constants declared in FLEXKeychain.h for a list of keys that
|
||||
/// can be used when accessing the dictionaries returned by this method.
|
||||
+ (NSArray<NSDictionary<NSString *, id> *> *)allAccounts;
|
||||
+ (NSArray<NSDictionary<NSString *, id> *> *)allAccounts:(NSError *__autoreleasing *)error;
|
||||
|
||||
/// @param serviceName The service for which to return the corresponding accounts.
|
||||
/// @return An array of dictionaries containing the Keychain's accounts for a given `serviceName`,
|
||||
/// or `nil` if the Keychain doesn't have any accounts for the given `serviceName`.
|
||||
/// The order of the objects in the array isn't defined.
|
||||
///
|
||||
/// @note See the `NSString` constants declared in FLEXKeychain.h for a list of keys that
|
||||
/// can be used when accessing the dictionaries returned by this method.
|
||||
+ (NSArray<NSDictionary<NSString *, id> *> *)accountsForService:(NSString *)serviceName;
|
||||
+ (NSArray<NSDictionary<NSString *, id> *> *)accountsForService:(NSString *)serviceName error:(NSError *__autoreleasing *)error;
|
||||
|
||||
|
||||
#pragma mark - Configuration
|
||||
|
||||
#if __IPHONE_4_0 && TARGET_OS_IPHONE
|
||||
/// Returns the accessibility type for all future passwords saved to the Keychain.
|
||||
///
|
||||
/// @return `NULL` or one of the "Keychain Item Accessibility
|
||||
/// Constants" used for determining when a keychain item should be readable.
|
||||
+ (CFTypeRef)accessibilityType;
|
||||
|
||||
/// Sets the accessibility type for all future passwords saved to the Keychain.
|
||||
///
|
||||
/// @param accessibilityType One of the "Keychain Item Accessibility Constants"
|
||||
/// used for determining when a keychain item should be readable.
|
||||
/// If the value is `NULL` (the default), the Keychain default will be used which
|
||||
/// is highly insecure. You really should use at least `kSecAttrAccessibleAfterFirstUnlock`
|
||||
/// for background applications or `kSecAttrAccessibleWhenUnlocked` for all
|
||||
/// other applications.
|
||||
///
|
||||
/// @note See Security/SecItem.h
|
||||
+ (void)setAccessibilityType:(CFTypeRef)accessibilityType;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
121
Tweaks/FLEX/GlobalStateExplorers/Keychain/FLEXKeychain.m
Normal file
121
Tweaks/FLEX/GlobalStateExplorers/Keychain/FLEXKeychain.m
Normal file
@@ -0,0 +1,121 @@
|
||||
//
|
||||
// FLEXKeychain.m
|
||||
//
|
||||
// Forked from:
|
||||
// SSKeychain.m in SSKeychain
|
||||
// Created by Sam Soffes on 5/19/10.
|
||||
// Copyright (c) 2010-2014 Sam Soffes. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXKeychain.h"
|
||||
#import "FLEXKeychainQuery.h"
|
||||
|
||||
NSString * const kFLEXKeychainErrorDomain = @"com.flipboard.flex";
|
||||
NSString * const kFLEXKeychainAccountKey = @"acct";
|
||||
NSString * const kFLEXKeychainCreatedAtKey = @"cdat";
|
||||
NSString * const kFLEXKeychainClassKey = @"labl";
|
||||
NSString * const kFLEXKeychainDescriptionKey = @"desc";
|
||||
NSString * const kFLEXKeychainGroupKey = @"agrp";
|
||||
NSString * const kFLEXKeychainLabelKey = @"labl";
|
||||
NSString * const kFLEXKeychainLastModifiedKey = @"mdat";
|
||||
NSString * const kFLEXKeychainWhereKey = @"svce";
|
||||
|
||||
#if __IPHONE_4_0 && TARGET_OS_IPHONE
|
||||
static CFTypeRef FLEXKeychainAccessibilityType = NULL;
|
||||
#endif
|
||||
|
||||
@implementation FLEXKeychain
|
||||
|
||||
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account {
|
||||
return [self passwordForService:serviceName account:account error:nil];
|
||||
}
|
||||
|
||||
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError *__autoreleasing *)error {
|
||||
FLEXKeychainQuery *query = [FLEXKeychainQuery new];
|
||||
query.service = serviceName;
|
||||
query.account = account;
|
||||
[query fetch:error];
|
||||
return query.password;
|
||||
}
|
||||
|
||||
+ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account {
|
||||
return [self passwordDataForService:serviceName account:account error:nil];
|
||||
}
|
||||
|
||||
+ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error {
|
||||
FLEXKeychainQuery *query = [FLEXKeychainQuery new];
|
||||
query.service = serviceName;
|
||||
query.account = account;
|
||||
[query fetch:error];
|
||||
|
||||
return query.passwordData;
|
||||
}
|
||||
|
||||
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account {
|
||||
return [self deletePasswordForService:serviceName account:account error:nil];
|
||||
}
|
||||
|
||||
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account error:(NSError *__autoreleasing *)error {
|
||||
FLEXKeychainQuery *query = [FLEXKeychainQuery new];
|
||||
query.service = serviceName;
|
||||
query.account = account;
|
||||
return [query deleteItem:error];
|
||||
}
|
||||
|
||||
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account {
|
||||
return [self setPassword:password forService:serviceName account:account error:nil];
|
||||
}
|
||||
|
||||
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError *__autoreleasing *)error {
|
||||
FLEXKeychainQuery *query = [FLEXKeychainQuery new];
|
||||
query.service = serviceName;
|
||||
query.account = account;
|
||||
query.password = password;
|
||||
return [query save:error];
|
||||
}
|
||||
|
||||
+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account {
|
||||
return [self setPasswordData:password forService:serviceName account:account error:nil];
|
||||
}
|
||||
|
||||
+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error {
|
||||
FLEXKeychainQuery *query = [FLEXKeychainQuery new];
|
||||
query.service = serviceName;
|
||||
query.account = account;
|
||||
query.passwordData = password;
|
||||
return [query save:error];
|
||||
}
|
||||
|
||||
+ (NSArray *)allAccounts {
|
||||
return [self allAccounts:nil] ?: @[];
|
||||
}
|
||||
|
||||
+ (NSArray *)allAccounts:(NSError *__autoreleasing *)error {
|
||||
return [self accountsForService:nil error:error];
|
||||
}
|
||||
|
||||
+ (NSArray *)accountsForService:(NSString *)serviceName {
|
||||
return [self accountsForService:serviceName error:nil];
|
||||
}
|
||||
|
||||
+ (NSArray *)accountsForService:(NSString *)serviceName error:(NSError *__autoreleasing *)error {
|
||||
FLEXKeychainQuery *query = [FLEXKeychainQuery new];
|
||||
query.service = serviceName;
|
||||
return [query fetchAll:error];
|
||||
}
|
||||
|
||||
#if __IPHONE_4_0 && TARGET_OS_IPHONE
|
||||
+ (CFTypeRef)accessibilityType {
|
||||
return FLEXKeychainAccessibilityType;
|
||||
}
|
||||
|
||||
+ (void)setAccessibilityType:(CFTypeRef)accessibilityType {
|
||||
CFRetain(accessibilityType);
|
||||
if (FLEXKeychainAccessibilityType) {
|
||||
CFRelease(FLEXKeychainAccessibilityType);
|
||||
}
|
||||
FLEXKeychainAccessibilityType = accessibilityType;
|
||||
}
|
||||
#endif
|
||||
|
||||
@end
|
||||
112
Tweaks/FLEX/GlobalStateExplorers/Keychain/FLEXKeychainQuery.h
Normal file
112
Tweaks/FLEX/GlobalStateExplorers/Keychain/FLEXKeychainQuery.h
Normal file
@@ -0,0 +1,112 @@
|
||||
//
|
||||
// FLEXKeychainQuery.h
|
||||
//
|
||||
// Derived from:
|
||||
// SSKeychainQuery.h in SSKeychain
|
||||
// Created by Caleb Davenport on 3/19/13.
|
||||
// Copyright (c) 2010-2014 Sam Soffes. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Security/Security.h>
|
||||
|
||||
#if __IPHONE_7_0 || __MAC_10_9
|
||||
// Keychain synchronization available at compile time
|
||||
#define FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE 1
|
||||
#endif
|
||||
|
||||
#if __IPHONE_3_0 || __MAC_10_9
|
||||
// Keychain access group available at compile time
|
||||
#define FLEXKEYCHAIN_ACCESS_GROUP_AVAILABLE 1
|
||||
#endif
|
||||
|
||||
#ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE
|
||||
typedef NS_ENUM(NSUInteger, FLEXKeychainQuerySynchronizationMode) {
|
||||
FLEXKeychainQuerySynchronizationModeAny,
|
||||
FLEXKeychainQuerySynchronizationModeNo,
|
||||
FLEXKeychainQuerySynchronizationModeYes
|
||||
};
|
||||
#endif
|
||||
|
||||
/// Simple interface for querying or modifying keychain items.
|
||||
@interface FLEXKeychainQuery : NSObject
|
||||
|
||||
/// kSecAttrAccount
|
||||
@property (nonatomic, copy) NSString *account;
|
||||
|
||||
/// kSecAttrService
|
||||
@property (nonatomic, copy) NSString *service;
|
||||
|
||||
/// kSecAttrLabel
|
||||
@property (nonatomic, copy) NSString *label;
|
||||
|
||||
#ifdef FLEXKEYCHAIN_ACCESS_GROUP_AVAILABLE
|
||||
/// kSecAttrAccessGroup (only used on iOS)
|
||||
@property (nonatomic, copy) NSString *accessGroup;
|
||||
#endif
|
||||
|
||||
#ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE
|
||||
/// kSecAttrSynchronizable
|
||||
@property (nonatomic) FLEXKeychainQuerySynchronizationMode synchronizationMode;
|
||||
#endif
|
||||
|
||||
/// Root storage for password information
|
||||
@property (nonatomic, copy) NSData *passwordData;
|
||||
|
||||
/// This property automatically transitions between an object and the value of
|
||||
/// `passwordData` using NSKeyedArchiver and NSKeyedUnarchiver.
|
||||
@property (nonatomic, copy) id<NSCoding> passwordObject;
|
||||
|
||||
/// Convenience accessor for setting and getting a password string. Passes through
|
||||
/// to `passwordData` using UTF-8 string encoding.
|
||||
@property (nonatomic, copy) NSString *password;
|
||||
|
||||
|
||||
#pragma mark Saving & Deleting
|
||||
|
||||
/// Save the receiver's attributes as a keychain item. Existing items with the
|
||||
/// given account, service, and access group will first be deleted.
|
||||
///
|
||||
/// @param error Populated should an error occur.
|
||||
/// @return `YES` if saving was successful, `NO` otherwise.
|
||||
- (BOOL)save:(NSError **)error;
|
||||
|
||||
/// Delete keychain items that match the given account, service, and access group.
|
||||
///
|
||||
/// @param error Populated should an error occur.
|
||||
/// @return `YES` if saving was successful, `NO` otherwise.
|
||||
- (BOOL)deleteItem:(NSError **)error;
|
||||
|
||||
|
||||
#pragma mark Fetching
|
||||
|
||||
/// Fetch all keychain items that match the given account, service, and access
|
||||
/// group. The values of `password` and `passwordData` are ignored when fetching.
|
||||
///
|
||||
/// @param error Populated should an error occur.
|
||||
/// @return An array of dictionaries that represent all matching keychain items,
|
||||
/// or `nil` should an error occur. The order of the items is not determined.
|
||||
- (NSArray<NSDictionary<NSString *, id> *> *)fetchAll:(NSError **)error;
|
||||
|
||||
/// Fetch the keychain item that matches the given account, service, and access
|
||||
/// group. The `password` and `passwordData` properties will be populated unless
|
||||
/// an error occurs. The values of `password` and `passwordData` are ignored when
|
||||
/// fetching.
|
||||
///
|
||||
/// @param error Populated should an error occur.
|
||||
/// @return `YES` if fetching was successful, `NO` otherwise.
|
||||
- (BOOL)fetch:(NSError **)error;
|
||||
|
||||
|
||||
#pragma mark Synchronization Status
|
||||
|
||||
#ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE
|
||||
/// Returns a boolean indicating if keychain synchronization is available on the device at runtime.
|
||||
/// The #define FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE is only for compile time.
|
||||
/// If you are checking for the presence of synchronization, you should use this method.
|
||||
///
|
||||
/// @return A value indicating if keychain synchronization is available
|
||||
+ (BOOL)isSynchronizationAvailable;
|
||||
#endif
|
||||
|
||||
@end
|
||||
304
Tweaks/FLEX/GlobalStateExplorers/Keychain/FLEXKeychainQuery.m
Normal file
304
Tweaks/FLEX/GlobalStateExplorers/Keychain/FLEXKeychainQuery.m
Normal file
@@ -0,0 +1,304 @@
|
||||
//
|
||||
// FLEXKeychainQuery.m
|
||||
// FLEXKeychain
|
||||
//
|
||||
// Created by Caleb Davenport on 3/19/13.
|
||||
// Copyright (c) 2013-2014 Sam Soffes. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXKeychainQuery.h"
|
||||
#import "FLEXKeychain.h"
|
||||
|
||||
@implementation FLEXKeychainQuery
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
- (BOOL)save:(NSError *__autoreleasing *)error {
|
||||
OSStatus status = FLEXKeychainErrorBadArguments;
|
||||
if (!self.service || !self.account || !self.passwordData) {
|
||||
if (error) {
|
||||
*error = [self errorWithCode:status];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSMutableDictionary *query = nil;
|
||||
NSMutableDictionary * searchQuery = [self query];
|
||||
status = SecItemCopyMatching((__bridge CFDictionaryRef)searchQuery, nil);
|
||||
if (status == errSecSuccess) {//item already exists, update it!
|
||||
query = [[NSMutableDictionary alloc]init];
|
||||
query[(__bridge id)kSecValueData] = self.passwordData;
|
||||
#if __IPHONE_4_0 && TARGET_OS_IPHONE
|
||||
CFTypeRef accessibilityType = FLEXKeychain.accessibilityType;
|
||||
if (accessibilityType) {
|
||||
query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessibilityType;
|
||||
}
|
||||
#endif
|
||||
status = SecItemUpdate((__bridge CFDictionaryRef)(searchQuery), (__bridge CFDictionaryRef)(query));
|
||||
}else if (status == errSecItemNotFound){//item not found, create it!
|
||||
query = [self query];
|
||||
if (self.label) {
|
||||
query[(__bridge id)kSecAttrLabel] = self.label;
|
||||
}
|
||||
query[(__bridge id)kSecValueData] = self.passwordData;
|
||||
#if __IPHONE_4_0 && TARGET_OS_IPHONE
|
||||
CFTypeRef accessibilityType = FLEXKeychain.accessibilityType;
|
||||
if (accessibilityType) {
|
||||
query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessibilityType;
|
||||
}
|
||||
#endif
|
||||
status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
|
||||
}
|
||||
|
||||
if (status != errSecSuccess && error != NULL) {
|
||||
*error = [self errorWithCode:status];
|
||||
}
|
||||
|
||||
return (status == errSecSuccess);
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)deleteItem:(NSError *__autoreleasing *)error {
|
||||
OSStatus status = FLEXKeychainErrorBadArguments;
|
||||
if (!self.service || !self.account) {
|
||||
if (error) {
|
||||
*error = [self errorWithCode:status];
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSMutableDictionary *query = [self query];
|
||||
#if TARGET_OS_IPHONE
|
||||
status = SecItemDelete((__bridge CFDictionaryRef)query);
|
||||
#else
|
||||
// On Mac OS, SecItemDelete will not delete a key created in a different
|
||||
// app, nor in a different version of the same app.
|
||||
//
|
||||
// To replicate the issue, save a password, change to the code and
|
||||
// rebuild the app, and then attempt to delete that password.
|
||||
//
|
||||
// This was true in OS X 10.6 and probably later versions as well.
|
||||
//
|
||||
// Work around it by using SecItemCopyMatching and SecKeychainItemDelete.
|
||||
CFTypeRef result = NULL;
|
||||
query[(__bridge id)kSecReturnRef] = @YES;
|
||||
status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
|
||||
if (status == errSecSuccess) {
|
||||
status = SecKeychainItemDelete((SecKeychainItemRef)result);
|
||||
CFRelease(result);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (status != errSecSuccess && error != NULL) {
|
||||
*error = [self errorWithCode:status];
|
||||
}
|
||||
|
||||
return (status == errSecSuccess);
|
||||
}
|
||||
|
||||
|
||||
- (NSArray *)fetchAll:(NSError *__autoreleasing *)error {
|
||||
NSMutableDictionary *query = [self query];
|
||||
query[(__bridge id)kSecReturnAttributes] = @YES;
|
||||
query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll;
|
||||
#if __IPHONE_4_0 && TARGET_OS_IPHONE
|
||||
CFTypeRef accessibilityType = FLEXKeychain.accessibilityType;
|
||||
if (accessibilityType) {
|
||||
query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessibilityType;
|
||||
}
|
||||
#endif
|
||||
|
||||
CFTypeRef result = NULL;
|
||||
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
|
||||
if (status != errSecSuccess && error != NULL) {
|
||||
*error = [self errorWithCode:status];
|
||||
return nil;
|
||||
}
|
||||
|
||||
return (__bridge_transfer NSArray *)result ?: @[];
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)fetch:(NSError *__autoreleasing *)error {
|
||||
OSStatus status = FLEXKeychainErrorBadArguments;
|
||||
if (!self.service || !self.account) {
|
||||
if (error) {
|
||||
*error = [self errorWithCode:status];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
CFTypeRef result = NULL;
|
||||
NSMutableDictionary *query = [self query];
|
||||
query[(__bridge id)kSecReturnData] = @YES;
|
||||
query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
|
||||
status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
|
||||
|
||||
if (status != errSecSuccess) {
|
||||
if (error) {
|
||||
*error = [self errorWithCode:status];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
self.passwordData = (__bridge_transfer NSData *)result;
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Accessors
|
||||
|
||||
- (void)setPasswordObject:(id<NSCoding>)object {
|
||||
self.passwordData = [NSKeyedArchiver archivedDataWithRootObject:object];
|
||||
}
|
||||
|
||||
|
||||
- (id<NSCoding>)passwordObject {
|
||||
if (self.passwordData.length) {
|
||||
return [NSKeyedUnarchiver unarchiveObjectWithData:self.passwordData];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
- (void)setPassword:(NSString *)password {
|
||||
self.passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)password {
|
||||
if (self.passwordData.length) {
|
||||
return [[NSString alloc] initWithData:self.passwordData encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Synchronization Status
|
||||
|
||||
#ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE
|
||||
+ (BOOL)isSynchronizationAvailable {
|
||||
#if TARGET_OS_IPHONE
|
||||
return YES;
|
||||
#else
|
||||
return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_8_4;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (NSMutableDictionary *)query {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary new];
|
||||
dictionary[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword;
|
||||
|
||||
if (self.service) {
|
||||
dictionary[(__bridge id)kSecAttrService] = self.service;
|
||||
}
|
||||
|
||||
if (self.account) {
|
||||
dictionary[(__bridge id)kSecAttrAccount] = self.account;
|
||||
}
|
||||
|
||||
#ifdef FLEXKEYCHAIN_ACCESS_GROUP_AVAILABLE
|
||||
#if !TARGET_IPHONE_SIMULATOR
|
||||
if (self.accessGroup) {
|
||||
dictionary[(__bridge id)kSecAttrAccessGroup] = self.accessGroup;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE
|
||||
if ([[self class] isSynchronizationAvailable]) {
|
||||
id value;
|
||||
|
||||
switch (self.synchronizationMode) {
|
||||
case FLEXKeychainQuerySynchronizationModeNo: {
|
||||
value = @NO;
|
||||
break;
|
||||
}
|
||||
case FLEXKeychainQuerySynchronizationModeYes: {
|
||||
value = @YES;
|
||||
break;
|
||||
}
|
||||
case FLEXKeychainQuerySynchronizationModeAny: {
|
||||
value = (__bridge id)(kSecAttrSynchronizableAny);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dictionary[(__bridge id)(kSecAttrSynchronizable)] = value;
|
||||
}
|
||||
#endif
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
- (NSError *)errorWithCode:(OSStatus)code {
|
||||
static dispatch_once_t onceToken;
|
||||
static NSBundle *resourcesBundle = nil;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSURL *url = [[NSBundle bundleForClass:[self class]] URLForResource:@"FLEXKeychain" withExtension:@"bundle"];
|
||||
resourcesBundle = [NSBundle bundleWithURL:url];
|
||||
});
|
||||
|
||||
NSString *message = nil;
|
||||
switch (code) {
|
||||
case errSecSuccess: return nil;
|
||||
case FLEXKeychainErrorBadArguments: message = NSLocalizedStringFromTableInBundle(@"FLEXKeychainErrorBadArguments", @"FLEXKeychain", resourcesBundle, nil); break;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
case errSecUnimplemented: {
|
||||
message = NSLocalizedStringFromTableInBundle(@"errSecUnimplemented", @"FLEXKeychain", resourcesBundle, nil);
|
||||
break;
|
||||
}
|
||||
case errSecParam: {
|
||||
message = NSLocalizedStringFromTableInBundle(@"errSecParam", @"FLEXKeychain", resourcesBundle, nil);
|
||||
break;
|
||||
}
|
||||
case errSecAllocate: {
|
||||
message = NSLocalizedStringFromTableInBundle(@"errSecAllocate", @"FLEXKeychain", resourcesBundle, nil);
|
||||
break;
|
||||
}
|
||||
case errSecNotAvailable: {
|
||||
message = NSLocalizedStringFromTableInBundle(@"errSecNotAvailable", @"FLEXKeychain", resourcesBundle, nil);
|
||||
break;
|
||||
}
|
||||
case errSecDuplicateItem: {
|
||||
message = NSLocalizedStringFromTableInBundle(@"errSecDuplicateItem", @"FLEXKeychain", resourcesBundle, nil);
|
||||
break;
|
||||
}
|
||||
case errSecItemNotFound: {
|
||||
message = NSLocalizedStringFromTableInBundle(@"errSecItemNotFound", @"FLEXKeychain", resourcesBundle, nil);
|
||||
break;
|
||||
}
|
||||
case errSecInteractionNotAllowed: {
|
||||
message = NSLocalizedStringFromTableInBundle(@"errSecInteractionNotAllowed", @"FLEXKeychain", resourcesBundle, nil);
|
||||
break;
|
||||
}
|
||||
case errSecDecode: {
|
||||
message = NSLocalizedStringFromTableInBundle(@"errSecDecode", @"FLEXKeychain", resourcesBundle, nil);
|
||||
break;
|
||||
}
|
||||
case errSecAuthFailed: {
|
||||
message = NSLocalizedStringFromTableInBundle(@"errSecAuthFailed", @"FLEXKeychain", resourcesBundle, nil);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
message = NSLocalizedStringFromTableInBundle(@"errSecDefault", @"FLEXKeychain", resourcesBundle, nil);
|
||||
}
|
||||
#else
|
||||
default:
|
||||
message = (__bridge_transfer NSString *)SecCopyErrorMessageString(code, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
NSDictionary *userInfo = message ? @{ NSLocalizedDescriptionKey : message } : nil;
|
||||
return [NSError errorWithDomain:kFLEXKeychainErrorDomain code:code userInfo:userInfo];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// FLEXKeychainViewController.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by ray on 2019/8/17.
|
||||
// Copyright © 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXGlobalsEntry.h"
|
||||
#import "FLEXFilteringTableViewController.h"
|
||||
|
||||
@interface FLEXKeychainViewController : FLEXFilteringTableViewController <FLEXGlobalsEntry>
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,254 @@
|
||||
//
|
||||
// FLEXKeychainViewController.m
|
||||
// FLEX
|
||||
//
|
||||
// Created by ray on 2019/8/17.
|
||||
// Copyright © 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXKeychain.h"
|
||||
#import "FLEXKeychainQuery.h"
|
||||
#import "FLEXKeychainViewController.h"
|
||||
#import "FLEXTableViewCell.h"
|
||||
#import "FLEXMutableListSection.h"
|
||||
#import "FLEXUtility.h"
|
||||
#import "UIPasteboard+FLEX.h"
|
||||
#import "UIBarButtonItem+FLEX.h"
|
||||
|
||||
@interface FLEXKeychainViewController ()
|
||||
@property (nonatomic, readonly) FLEXMutableListSection<NSDictionary *> *section;
|
||||
@end
|
||||
|
||||
@implementation FLEXKeychainViewController
|
||||
|
||||
- (id)init {
|
||||
return [self initWithStyle:UITableViewStyleGrouped];
|
||||
}
|
||||
|
||||
#pragma mark - Overrides
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
[self addToolbarItems:@[
|
||||
FLEXBarButtonItemSystem(Add, self, @selector(addPressed)),
|
||||
[FLEXBarButtonItemSystem(Trash, self, @selector(trashPressed:)) flex_withTintColor:UIColor.redColor],
|
||||
]];
|
||||
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
- (NSArray<FLEXTableViewSection *> *)makeSections {
|
||||
_section = [FLEXMutableListSection list:FLEXKeychain.allAccounts.mutableCopy
|
||||
cellConfiguration:^(__kindof FLEXTableViewCell *cell, NSDictionary *item, NSInteger row) {
|
||||
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
|
||||
|
||||
id service = item[kFLEXKeychainWhereKey];
|
||||
if ([service isKindOfClass:[NSString class]]) {
|
||||
cell.textLabel.text = service;
|
||||
cell.detailTextLabel.text = [item[kFLEXKeychainAccountKey] description];
|
||||
} else {
|
||||
cell.textLabel.text = [NSString stringWithFormat:
|
||||
@"[%@]\n\n%@",
|
||||
NSStringFromClass([service class]),
|
||||
[service description]
|
||||
];
|
||||
}
|
||||
} filterMatcher:^BOOL(NSString *filterText, NSDictionary *item) {
|
||||
// Loop over contents of the keychain item looking for a match
|
||||
for (NSString *field in item.allValues) {
|
||||
if ([field isKindOfClass:[NSString class]]) {
|
||||
if ([field localizedCaseInsensitiveContainsString:filterText]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
];
|
||||
|
||||
return @[self.section];
|
||||
}
|
||||
|
||||
/// We always want to show this section
|
||||
- (NSArray<FLEXTableViewSection *> *)nonemptySections {
|
||||
return @[self.section];
|
||||
}
|
||||
|
||||
- (void)reloadSections {
|
||||
self.section.list = FLEXKeychain.allAccounts.mutableCopy;
|
||||
}
|
||||
|
||||
- (void)refreshSectionTitle {
|
||||
self.section.customTitle = FLEXPluralString(
|
||||
self.section.filteredList.count, @"items", @"item"
|
||||
);
|
||||
}
|
||||
|
||||
- (void)reloadData {
|
||||
[self reloadSections];
|
||||
[self refreshSectionTitle];
|
||||
[super reloadData];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (FLEXKeychainQuery *)queryForItemAtIndex:(NSInteger)idx {
|
||||
NSDictionary *item = self.section.filteredList[idx];
|
||||
|
||||
FLEXKeychainQuery *query = [FLEXKeychainQuery new];
|
||||
query.service = [item[kFLEXKeychainWhereKey] description];
|
||||
query.account = [item[kFLEXKeychainAccountKey] description];
|
||||
query.accessGroup = [item[kFLEXKeychainGroupKey] description];
|
||||
[query fetch:nil];
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
- (void)deleteItem:(NSDictionary *)item {
|
||||
NSError *error = nil;
|
||||
BOOL success = [FLEXKeychain
|
||||
deletePasswordForService:item[kFLEXKeychainWhereKey]
|
||||
account:item[kFLEXKeychainAccountKey]
|
||||
error:&error
|
||||
];
|
||||
|
||||
if (!success) {
|
||||
[FLEXAlert makeAlert:^(FLEXAlert *make) {
|
||||
make.title(@"Error Deleting Item");
|
||||
make.message(error.localizedDescription);
|
||||
} showFrom:self];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Buttons
|
||||
|
||||
- (void)trashPressed:(UIBarButtonItem *)sender {
|
||||
[FLEXAlert makeSheet:^(FLEXAlert *make) {
|
||||
make.title(@"Clear Keychain");
|
||||
make.message(@"This will remove all keychain items for this app.\n");
|
||||
make.message(@"This action cannot be undone. Are you sure?");
|
||||
make.button(@"Yes, clear the keychain").destructiveStyle().handler(^(NSArray *strings) {
|
||||
[self confirmClearKeychain];
|
||||
});
|
||||
make.button(@"Cancel").cancelStyle();
|
||||
} showFrom:self source:sender];
|
||||
}
|
||||
|
||||
- (void)confirmClearKeychain {
|
||||
[FLEXAlert makeAlert:^(FLEXAlert *make) {
|
||||
make.title(@"ARE YOU SURE?");
|
||||
make.message(@"This action CANNOT BE UNDONE.\nAre you sure you want to continue?\n");
|
||||
make.message(@"If you're sure, scroll to confirm.");
|
||||
make.button(@"Yes, clear the keychain").destructiveStyle().handler(^(NSArray *strings) {
|
||||
for (id account in self.section.list) {
|
||||
[self deleteItem:account];
|
||||
}
|
||||
|
||||
[self reloadData];
|
||||
});
|
||||
make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel");
|
||||
make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel");
|
||||
make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel");
|
||||
make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel");
|
||||
make.button(@"Cancel").cancelStyle();
|
||||
} showFrom:self];
|
||||
}
|
||||
|
||||
- (void)addPressed {
|
||||
[FLEXAlert makeAlert:^(FLEXAlert *make) {
|
||||
make.title(@"Add Keychain Item");
|
||||
make.textField(@"Service name, i.e. Instagram");
|
||||
make.textField(@"Account");
|
||||
make.textField(@"Password");
|
||||
make.button(@"Cancel").cancelStyle();
|
||||
make.button(@"Save").handler(^(NSArray<NSString *> *strings) {
|
||||
// Display errors
|
||||
NSError *error = nil;
|
||||
if (![FLEXKeychain setPassword:strings[2] forService:strings[0] account:strings[1] error:&error]) {
|
||||
[FLEXAlert showAlert:@"Error" message:error.localizedDescription from:self];
|
||||
}
|
||||
|
||||
[self reloadData];
|
||||
});
|
||||
} showFrom:self];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - FLEXGlobalsEntry
|
||||
|
||||
+ (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row {
|
||||
return @"🔑 Keychain";
|
||||
}
|
||||
|
||||
+ (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row {
|
||||
FLEXKeychainViewController *viewController = [self new];
|
||||
viewController.title = [self globalsEntryTitle:row];
|
||||
|
||||
return viewController;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Table View Data Source
|
||||
|
||||
- (void)tableView:(UITableView *)tv commitEditingStyle:(UITableViewCellEditingStyle)style forRowAtIndexPath:(NSIndexPath *)ip {
|
||||
if (style == UITableViewCellEditingStyleDelete) {
|
||||
// Update the model
|
||||
NSDictionary *toRemove = self.section.filteredList[ip.row];
|
||||
[self deleteItem:toRemove];
|
||||
[self.section mutate:^(NSMutableArray *list) {
|
||||
[list removeObject:toRemove];
|
||||
}];
|
||||
|
||||
// Delete the row
|
||||
[tv deleteRowsAtIndexPaths:@[ip] withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
|
||||
// Update the title by refreshing the section without disturbing the delete animation
|
||||
//
|
||||
// This is an ugly hack, but literally nothing else works, save for manually getting
|
||||
// the header and setting its title, which I personally think is worse since it
|
||||
// would need to make assumptions about the default style of the header (CAPS)
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[self refreshSectionTitle];
|
||||
[tv reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Table View Delegate
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
FLEXKeychainQuery *query = [self queryForItemAtIndex:indexPath.row];
|
||||
|
||||
[FLEXAlert makeAlert:^(FLEXAlert *make) {
|
||||
make.title(query.service);
|
||||
make.message(@"Service: ").message(query.service);
|
||||
make.message(@"\nAccount: ").message(query.account);
|
||||
make.message(@"\nPassword: ").message(query.password);
|
||||
make.message(@"\nGroup: ").message(query.accessGroup);
|
||||
|
||||
make.button(@"Copy Service").handler(^(NSArray<NSString *> *strings) {
|
||||
[UIPasteboard.generalPasteboard flex_copy:query.service];
|
||||
});
|
||||
make.button(@"Copy Account").handler(^(NSArray<NSString *> *strings) {
|
||||
[UIPasteboard.generalPasteboard flex_copy:query.account];
|
||||
});
|
||||
make.button(@"Copy Password").handler(^(NSArray<NSString *> *strings) {
|
||||
[UIPasteboard.generalPasteboard flex_copy:query.password];
|
||||
});
|
||||
make.button(@"Dismiss").cancelStyle();
|
||||
|
||||
} showFrom:self];
|
||||
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
20
Tweaks/FLEX/GlobalStateExplorers/Keychain/SSKeychain_LICENSE
Normal file
20
Tweaks/FLEX/GlobalStateExplorers/Keychain/SSKeychain_LICENSE
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2010-2012 Sam Soffes.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
Reference in New Issue
Block a user