Chapter 11 - Chat App/ChatAppFinished/Pods/FirebaseAuth/Firebase/Auth/Source/SystemService/FIRAuthStoredUserManager.m
/*
 * Copyright 2019 Google
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#import "FIRAuthStoredUserManager.h"

/** @var kUserAccessGroupKey
    @brief Key of user access group stored in user defaults. Used for retrieve the user access
        group at launch.
 */
static NSString *kStoredUserAccessGroupKey = @"firebase_auth_stored_user_access_group";

/** @var kSharedKeychainAccountValue
    @brief Default value for kSecAttrAccount of shared keychain items.
 */
static NSString *kSharedKeychainAccountValue = @"firebase_auth_firebase_user";

/** @var kStoredUserCoderKey
    @brief The key to encode and decode the stored user.
 */
static NSString *kStoredUserCoderKey = @"firebase_auth_stored_user_coder_key";

@implementation FIRAuthStoredUserManager

#pragma mark - Initializers

- (instancetype)initWithServiceName:(NSString *)serviceName {
  self = [super init];
  if (self) {
    _keychain = [[FIRAuthKeychain alloc] initWithService:serviceName];
    _userDefaults = [[FIRAuthUserDefaultsStorage alloc] initWithService:serviceName];
  }
  return self;
}

#pragma mark - User Access Group

- (NSString *_Nullable)getStoredUserAccessGroupWithError:(NSError *_Nullable *_Nullable)outError {
  NSData *data = [self.userDefaults dataForKey:kStoredUserAccessGroupKey error:outError];
  if (data) {
    NSString *userAccessGroup = [NSString stringWithUTF8String:data.bytes];
    return userAccessGroup;
  } else {
    return nil;
  }
}

- (BOOL)setStoredUserAccessGroup:(NSString *_Nullable)accessGroup
                           error:(NSError *_Nullable *_Nullable)outError {
  NSData *data = [accessGroup dataUsingEncoding:NSUTF8StringEncoding];
  if (!data) {
    return [self.userDefaults removeDataForKey:kStoredUserAccessGroupKey error:outError];
  } else {
    return [self.userDefaults setData:data forKey:kStoredUserAccessGroupKey error:outError];
  }
}

#pragma mark - User for Access Group

- (FIRUser *)getStoredUserForAccessGroup:(NSString *)accessGroup
                       projectIdentifier:(NSString *)projectIdentifier
                                   error:(NSError *_Nullable *_Nullable)outError {


  NSMutableDictionary *query = [[NSMutableDictionary alloc] init];
  query[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword;

  query[(__bridge id)kSecAttrAccessGroup] = accessGroup;
  query[(__bridge id)kSecAttrService] = projectIdentifier;
  query[(__bridge id)kSecAttrAccount] = kSharedKeychainAccountValue;

  NSData *data = [self.keychain getItemWithQuery:query error:outError];
  NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
  FIRUser *user = [unarchiver decodeObjectOfClass:[FIRUser class] forKey:kStoredUserCoderKey];

  return user;
}

- (BOOL)setStoredUser:(FIRUser *)user
       forAccessGroup:(NSString *)accessGroup
    projectIdentifier:(NSString *)projectIdentifier
                error:(NSError *_Nullable *_Nullable)outError {
  NSMutableDictionary *query = [[NSMutableDictionary alloc] init];
  query[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword;
  query[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;

  query[(__bridge id)kSecAttrAccessGroup] = accessGroup;
  query[(__bridge id)kSecAttrService] = projectIdentifier;
  query[(__bridge id)kSecAttrAccount] = kSharedKeychainAccountValue;

  NSMutableData *data = [NSMutableData data];
  NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
  [archiver encodeObject:user forKey:kStoredUserCoderKey];
  [archiver finishEncoding];

  return [self.keychain setItem:data withQuery:query error:outError];
}

- (BOOL)removeStoredUserForAccessGroup:(NSString *)accessGroup
                     projectIdentifier:(NSString *)projectIdentifier
                                 error:(NSError *_Nullable *_Nullable)outError {
  NSMutableDictionary *query = [[NSMutableDictionary alloc] init];
  query[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword;
  query[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;

  query[(__bridge id)kSecAttrAccessGroup] = accessGroup;
  query[(__bridge id)kSecAttrService] = projectIdentifier;
  query[(__bridge id)kSecAttrAccount] = kSharedKeychainAccountValue;

  return [self.keychain removeItemWithQuery:query error:outError];
}

@end
