You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

213 lines
6.4 KiB

  1. //
  2. // FLEXKeychainTableViewController.m
  3. // FLEX
  4. //
  5. // Created by ray on 2019/8/17.
  6. // Copyright © 2019 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXKeychain.h"
  9. #import "FLEXKeychainQuery.h"
  10. #import "FLEXKeychainTableViewController.h"
  11. #import "FLEXUtility.h"
  12. #import "UIPasteboard+FLEX.h"
  13. @interface FLEXKeychainTableViewController ()
  14. @property (nonatomic) NSMutableArray<NSDictionary *> *keychainItems;
  15. @property (nonatomic) NSString *headerTitle;
  16. @end
  17. @implementation FLEXKeychainTableViewController
  18. - (void)viewDidLoad
  19. {
  20. [super viewDidLoad];
  21. self.navigationItem.rightBarButtonItems = @[
  22. [[UIBarButtonItem alloc]
  23. initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:@selector(trashPressed)
  24. ],
  25. [[UIBarButtonItem alloc]
  26. initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addPressed)
  27. ],
  28. ];
  29. [self refreshkeychainItems];
  30. [self updateHeaderTitle];
  31. }
  32. - (void)refreshkeychainItems
  33. {
  34. self.keychainItems = [FLEXKeychain allAccounts].mutableCopy;
  35. }
  36. - (void)updateHeaderTitle
  37. {
  38. self.headerTitle = [NSString stringWithFormat:@"%@ items", @(self.keychainItems.count)];
  39. }
  40. - (FLEXKeychainQuery *)queryForItemAtIndex:(NSInteger)idx
  41. {
  42. NSDictionary *item = self.keychainItems[idx];
  43. FLEXKeychainQuery *query = [FLEXKeychainQuery new];
  44. query.service = item[kFLEXKeychainWhereKey];
  45. query.account = item[kFLEXKeychainAccountKey];
  46. [query fetch:nil];
  47. return query;
  48. }
  49. - (void)deleteItem:(NSDictionary *)item
  50. {
  51. NSError *error = nil;
  52. BOOL success = [FLEXKeychain
  53. deletePasswordForService:item[kFLEXKeychainWhereKey]
  54. account:item[kFLEXKeychainAccountKey]
  55. error:&error
  56. ];
  57. if (!success) {
  58. [FLEXAlert makeAlert:^(FLEXAlert *make) {
  59. make.title(@"Error Deleting Item");
  60. make.message(error.localizedDescription);
  61. } showFrom:self];
  62. }
  63. }
  64. #pragma mark Buttons
  65. - (void)trashPressed
  66. {
  67. [FLEXAlert makeSheet:^(FLEXAlert *make) {
  68. make.title(@"Clear Keychain");
  69. make.message(@"This will remove all keychain items for this app.\n");
  70. make.message(@"This action cannot be undone. Are you sure?");
  71. make.button(@"Yes, clear the keychain").destructiveStyle().handler(^(NSArray *strings) {
  72. for (id account in self.keychainItems) {
  73. [self deleteItem:account];
  74. }
  75. [self refreshkeychainItems];
  76. [self.tableView reloadData];
  77. });
  78. make.button(@"Cancel").cancelStyle();
  79. } showFrom:self];
  80. }
  81. - (void)addPressed
  82. {
  83. [FLEXAlert makeAlert:^(FLEXAlert *make) {
  84. make.title(@"Add Keychain Item");
  85. make.textField(@"Service name, i.e. Instagram");
  86. make.textField(@"Account, i.e. username@example.com");
  87. make.textField(@"Password");
  88. make.button(@"Cancel").cancelStyle();
  89. make.button(@"Save").handler(^(NSArray<NSString *> *strings) {
  90. // Display errors
  91. NSError *error = nil;
  92. if (![FLEXKeychain setPassword:strings[2] forService:strings[0] account:strings[1] error:&error]) {
  93. [FLEXAlert showAlert:@"Error" message:error.localizedDescription from:self];
  94. }
  95. [self refreshkeychainItems];
  96. [self.tableView reloadData];
  97. });
  98. } showFrom:self];
  99. }
  100. #pragma mark - FLEXGlobalsEntry
  101. + (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row
  102. {
  103. return @"🔑 Keychain";
  104. }
  105. + (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row {
  106. FLEXKeychainTableViewController *viewController = [self new];
  107. viewController.title = [self globalsEntryTitle:row];
  108. return viewController;
  109. }
  110. #pragma mark - Table View Data Source
  111. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  112. {
  113. return self.keychainItems.count;
  114. }
  115. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  116. {
  117. static NSString *CellIdentifier = @"Cell";
  118. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  119. if (!cell) {
  120. cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
  121. cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  122. cell.textLabel.font = [FLEXUtility defaultTableViewCellLabelFont];
  123. }
  124. NSDictionary *item = self.keychainItems[indexPath.row];
  125. id account = item[kFLEXKeychainAccountKey];
  126. if ([account isKindOfClass:[NSString class]]) {
  127. cell.textLabel.text = account;
  128. } else {
  129. cell.textLabel.text = [NSString stringWithFormat:
  130. @"[%@]\n\n%@",
  131. NSStringFromClass([account class]),
  132. [account description]
  133. ];
  134. }
  135. return cell;
  136. }
  137. - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
  138. {
  139. return self.headerTitle;
  140. }
  141. - (void)tableView:(UITableView *)tv commitEditingStyle:(UITableViewCellEditingStyle)style forRowAtIndexPath:(NSIndexPath *)ip
  142. {
  143. if (style == UITableViewCellEditingStyleDelete) {
  144. [self deleteItem:self.keychainItems[ip.row]];
  145. [self.keychainItems removeObjectAtIndex:ip.row];
  146. [tv deleteRowsAtIndexPaths:@[ip] withRowAnimation:UITableViewRowAnimationAutomatic];
  147. }
  148. }
  149. #pragma mark - Table View Delegate
  150. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  151. {
  152. FLEXKeychainQuery *query = [self queryForItemAtIndex:indexPath.row];
  153. [FLEXAlert makeAlert:^(FLEXAlert *make) {
  154. make.title(query.service);
  155. make.message(@"Service: ").message(query.service);
  156. make.message(@"\nAccount: ").message(query.account);
  157. make.message(@"\nPassword: ").message(query.password);
  158. make.button(@"Copy Service").handler(^(NSArray<NSString *> *strings) {
  159. [UIPasteboard.generalPasteboard flex_copy:query.service];
  160. });
  161. make.button(@"Copy Account").handler(^(NSArray<NSString *> *strings) {
  162. [UIPasteboard.generalPasteboard flex_copy:query.account];
  163. });
  164. make.button(@"Copy Password").handler(^(NSArray<NSString *> *strings) {
  165. [UIPasteboard.generalPasteboard flex_copy:query.password];
  166. });
  167. make.button(@"Dismiss").cancelStyle();
  168. } showFrom:self];
  169. [tableView deselectRowAtIndexPath:indexPath animated:YES];
  170. }
  171. @end