219 lines
6.8 KiB
Objective-C
219 lines
6.8 KiB
Objective-C
//
|
|
// 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
|