You've already forked hotpocket
BTHLABS-58: Share Extension in Apple Apps
This commit is contained in:
218
services/apple/Shared (App)/HPCredentialsHelper.m
Normal file
218
services/apple/Shared (App)/HPCredentialsHelper.m
Normal file
@@ -0,0 +1,218 @@
|
||||
//
|
||||
// HPCredentialsHelper.m
|
||||
// HotPocket
|
||||
//
|
||||
// Created by Tomek Wójcik on 19/09/2025.
|
||||
//
|
||||
|
||||
#import <Security/Security.h>
|
||||
|
||||
#import "HPCredentialsHelper.h"
|
||||
|
||||
@implementation HPCredentials
|
||||
|
||||
#pragma mark - HPCredentials implementation
|
||||
|
||||
-(id)init {
|
||||
if (self = [super init]) {
|
||||
self.baseURL = nil;
|
||||
self.accessToken = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(BOOL)usable {
|
||||
return (self.baseURL != nil && self.accessToken != nil);
|
||||
}
|
||||
|
||||
-(NSURL *)rpcURL {
|
||||
return [NSURL URLWithString:self.baseURL];
|
||||
}
|
||||
|
||||
-(NSString *)description {
|
||||
NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:2];
|
||||
|
||||
if (self.baseURL == nil) {
|
||||
[attributes setValue:@"(null)" forKey:@"baseURL"];
|
||||
} else {
|
||||
[attributes setValue:self.baseURL forKey:@"baseURL"];
|
||||
}
|
||||
|
||||
if (self.accessToken == nil) {
|
||||
[attributes setValue:@"(null)" forKey:@"accessToken"];
|
||||
} else {
|
||||
[attributes setValue:@"***" forKey:@"accessToken"];
|
||||
}
|
||||
|
||||
return [NSString stringWithFormat:@"<%@: %p; %@>", NSStringFromClass([self class]), self, attributes];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation HPCredentialsHelper (HPCredentialsHelperPrivate)
|
||||
|
||||
#pragma mark - Private interface
|
||||
|
||||
-(NSString *)getService {
|
||||
#ifdef DEBUG
|
||||
return @"pl.bthlabs.HotPocket.Debug";
|
||||
#else
|
||||
return @"pl.bthlabs.HotPocket";
|
||||
#endif
|
||||
}
|
||||
|
||||
-(NSData *)getKeychainItem:(NSString *)service account:(NSString *)account {
|
||||
if (service == nil || account == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSDictionary *query = @{
|
||||
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
||||
(__bridge id)kSecAttrService: service,
|
||||
(__bridge id)kSecAttrAccount: account,
|
||||
(__bridge id)kSecReturnData: @YES,
|
||||
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne,
|
||||
(__bridge id)kSecAttrAccessGroup: @"648728X64K.pl.bthlabs.HotPocketShared",
|
||||
(__bridge id)kSecUseDataProtectionKeychain: @YES,
|
||||
};
|
||||
|
||||
CFTypeRef resultData = NULL;
|
||||
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &resultData);
|
||||
|
||||
if (status == errSecSuccess && resultData != NULL) {
|
||||
NSData *result = (__bridge_transfer NSData *)resultData;
|
||||
return result;
|
||||
} else {
|
||||
CFStringRef statusStringRef = SecCopyErrorMessageString(status, NULL);
|
||||
NSString *statusString = (__bridge NSString *)statusStringRef;
|
||||
NSLog(@"-[HPCredentialsHelper getKeychainItem:account:] service=`%@` account=`%@` status=%@", service, account, statusString);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
-(BOOL)createKeychainItemWithValue:(NSData *)value service:(NSString *)service account:(NSString *)account {
|
||||
if (value == nil || service == nil || account == nil) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSDictionary *attributes = @{
|
||||
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
||||
(__bridge id)kSecAttrService: service,
|
||||
(__bridge id)kSecAttrAccount: account,
|
||||
(__bridge id)kSecValueData: value,
|
||||
(__bridge id)kSecAttrAccessGroup: @"648728X64K.pl.bthlabs.HotPocketShared",
|
||||
(__bridge id)kSecUseDataProtectionKeychain: @YES,
|
||||
};
|
||||
|
||||
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)attributes, NULL);
|
||||
|
||||
if (status != errSecSuccess) {
|
||||
CFStringRef statusStringRef = SecCopyErrorMessageString(status, NULL);
|
||||
NSString *statusString = (__bridge NSString *)statusStringRef;
|
||||
NSLog(@"-[HPCredentialsHelper createKeychainItemWithValue:service:account:] service=`%@` account=`%@` status=%@", service, account, statusString);
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(BOOL)deleteKeychainItem:(NSString *)service account:(NSString *)account {
|
||||
if (service == nil || account == nil) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSDictionary *query = @{
|
||||
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
||||
(__bridge id)kSecAttrService: service,
|
||||
(__bridge id)kSecAttrAccount: account,
|
||||
(__bridge id)kSecAttrAccessGroup: @"648728X64K.pl.bthlabs.HotPocketShared",
|
||||
(__bridge id)kSecUseDataProtectionKeychain: @YES,
|
||||
};
|
||||
|
||||
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
|
||||
|
||||
if (status != errSecSuccess) {
|
||||
CFStringRef statusStringRef = SecCopyErrorMessageString(status, NULL);
|
||||
NSString *statusString = (__bridge NSString *)statusStringRef;
|
||||
NSLog(@"-[HPCredentialsHelper deleteKeychainItem:account:] service=`%@` account=`%@` status=%@", service, account, statusString);
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation HPCredentialsHelper
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
+(instancetype)sharedHelper {
|
||||
static HPCredentialsHelper *sharedInstance = nil;
|
||||
static dispatch_once_t initToken;
|
||||
dispatch_once(&initToken, ^{
|
||||
sharedInstance = [[self alloc] init];
|
||||
});
|
||||
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
#pragma mark - Public interface
|
||||
|
||||
-(HPCredentials *)getCredentials {
|
||||
HPCredentials *result = [[HPCredentials alloc] init];
|
||||
|
||||
NSData *itemData = [self getKeychainItem:[self getService] account:@"RPC"];
|
||||
if (itemData != nil) {
|
||||
NSError *error;
|
||||
NSDictionary *itemPayload = [NSJSONSerialization JSONObjectWithData:itemData
|
||||
options:NSJSONReadingTopLevelDictionaryAssumed
|
||||
error:&error];
|
||||
|
||||
if (error != nil) {
|
||||
NSLog(@"-[HPCredentialsHalper getCredentials] error=`%@`", error);
|
||||
} else if (itemPayload != nil) {
|
||||
result.baseURL = [itemPayload valueForKey:@"baseURL"];
|
||||
result.accessToken = [itemPayload valueForKey:@"accessToken"];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
-(BOOL)saveCredentials:(NSString *)baseURL accessToken:(NSString *)accessToken {
|
||||
NSMutableDictionary *itemPayload = [NSMutableDictionary dictionaryWithCapacity:2];
|
||||
|
||||
if (baseURL != nil) {
|
||||
[itemPayload setValue:baseURL forKey:@"baseURL"];
|
||||
}
|
||||
|
||||
if (accessToken != nil) {
|
||||
[itemPayload setValue:accessToken forKey:@"accessToken"];
|
||||
}
|
||||
|
||||
NSError *error;
|
||||
NSData *itemData = [NSJSONSerialization dataWithJSONObject:itemPayload options:0 error:&error];
|
||||
|
||||
if (error != nil) {
|
||||
NSLog(@"-[HPCredentialsHalper saveCredentials:accessToken:] error=`%@`", error);
|
||||
return NO;
|
||||
}
|
||||
|
||||
BOOL saveResult = [self createKeychainItemWithValue:itemData service:[self getService] account:@"RPC"];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"HPCredentialsChanged" object:self];
|
||||
|
||||
return saveResult;
|
||||
}
|
||||
|
||||
-(BOOL)clearCredentials {
|
||||
BOOL deleteResult = [self deleteKeychainItem:[self getService] account:@"RPC"];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"HPCredentialsChanged" object:self];
|
||||
|
||||
return deleteResult;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user