Skip to content

Commit

Permalink
Update secure storage
Browse files Browse the repository at this point in the history
  • Loading branch information
aromanov91 committed Sep 26, 2024
1 parent c988451 commit d0eb677
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 28 deletions.
23 changes: 15 additions & 8 deletions Sources/OversizeAppStoreServices/Auth/EnvAuthenticator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@
//

import AppStoreConnect
import Foundation

public struct EnvAuthenticator: Authenticator {
public enum Error: Swift.Error {
case missingEnvironmentVariable(String)
}

var jwt: JWT
private let secureStorage: KeychainService = .init()

private let secureStorage: SecureStorageService = .init()
private var jwt: JWT

public init() throws {
guard let appStoreCertificate = secureStorage.getAppStoreCertificate(with: "AppStore.KeyID") else {
throw Error.missingEnvironmentVariable("AppStore.KeyID")
guard let keyLabel = UserDefaults.standard.string(forKey: "AppStore.Account") else {
throw Error.missingEnvironmentVariable("AppStore.Account")
}

guard let appStoreCertificate = secureStorage.getAppStoreCertificate(with: keyLabel) else {
throw Error.missingEnvironmentVariable("AppStore.Key.Default")
}

let privateKey = try JWT.PrivateKey(pemRepresentation: appStoreCertificate.privateKey)
Expand All @@ -33,3 +34,9 @@ public struct EnvAuthenticator: Authenticator {
try jwt.token()
}
}

public extension EnvAuthenticator {
enum Error: Swift.Error {
case missingEnvironmentVariable(String)
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
//
// Copyright © 2024 Alexander Romanov
// AppStoreCertificate.swift, created on 12.09.2024
//
//

import SwiftUI

@propertyWrapper
public struct AppStoreCertificate: DynamicProperty {
public struct AppStoreCertificateStorage: DynamicProperty {
private let label: String
private let storage: SecureStorageService = .init()
private let storage: KeychainService = .init()

public init(_ label: String) {
self.label = label
}

public var wrappedValue: SecureStorageService.AppStoreCertificate? {
public var wrappedValue: KeychainService.AppStoreCertificate? {
get { storage.getAppStoreCertificate(with: label) }
nonmutating set {
if let newValue {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
//
// Copyright © 2022 Alexander Romanov
// SecureStorageService.swift
// KeychainService.swift
//

import Foundation
import OversizeCore
import Security

public final class SecureStorageService {
public final class KeychainService {
enum KeychainError: Error {
case itemAlreadyExist
case itemNotFound
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,59 +3,60 @@
// AppStoreCertificate.swift, created on 12.09.2024
//

import Security
import Foundation
import Security

public extension SecureStorageService {
public extension KeychainService {
struct AppStoreCertificate {
public var issuerId: String
public var keyId: String
public var privateKey: String

public init(issuerId: String, keyId: String, privateKey: String) {
self.issuerId = issuerId
self.keyId = keyId
self.privateKey = privateKey
}
}

func addAppStoreCertificate(_ certificate: AppStoreCertificate, with label: String) {
var query: [CFString: Any] = [:]
query[kSecClass] = kSecClassGenericPassword
query[kSecAttrLabel] = label
query[kSecAttrAccount] = certificate.issuerId

let combinedData = "\(certificate.keyId):\(certificate.privateKey)"
query[kSecValueData] = combinedData.data(using: .utf8)

do {
try addItem(query: query)
} catch {
return
}
}

func updateAppStoreCertificate(_ certificate: AppStoreCertificate, with label: String) {
deleteAppStoreCertificate(with: label)
addAppStoreCertificate(certificate, with: label)
}

func getAppStoreCertificate(with label: String) -> AppStoreCertificate? {
var query: [CFString: Any] = [:]
query[kSecClass] = kSecClassGenericPassword
query[kSecAttrLabel] = label

var result: [CFString: Any]?

do {
result = try findItem(query: query)
} catch {
return nil
}

if let issuerId = result?[kSecAttrAccount] as? String,
let data = result?[kSecValueData] as? Data,
let combinedString = String(data: data, encoding: .utf8) {
let combinedString = String(data: data, encoding: .utf8)
{
let components = combinedString.split(separator: ":")
if components.count == 2 {
let keyId = String(components[0])
Expand All @@ -65,12 +66,12 @@ public extension SecureStorageService {
}
return nil
}

func deleteAppStoreCertificate(with label: String) {
var query: [CFString: Any] = [:]
query[kSecClass] = kSecClassGenericPassword
query[kSecAttrLabel] = label

do {
try deleteItem(query: query)
} catch {
Expand Down
8 changes: 8 additions & 0 deletions Sources/OversizeAppStoreServices/ServiceRegistering.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import Factory

public extension Container {
var keychainService: Factory<KeychainService> {
self { KeychainService() }
}

var appsService: Factory<AppsService> {
self { AppsService() }
}
Expand All @@ -21,4 +25,8 @@ public extension Container {
var reviewService: Factory<ReviewService> {
self { ReviewService() }
}

var usersService: Factory<UsersService> {
self { UsersService() }
}
}
30 changes: 30 additions & 0 deletions Sources/OversizeAppStoreServices/Services/UsersService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// Copyright © 2024 Alexander Romanov
// UsersService.swift, created on 22.09.2024
//

import AppStoreConnect
import OversizeModels

public actor UsersService {
private let client: AppStoreConnectClient?

public init() {
do {
client = try AppStoreConnectClient(authenticator: EnvAuthenticator())
} catch {
client = nil
}
}

public func fetchCustomerReviews(versionId _: String) async -> Result<[User], AppError> {
guard let client = client else { return .failure(.network(type: .unauthorized)) }
let request = Resources.v1.users.get()
do {
let data = try await client.send(request).data
return .success(data)
} catch {
return .failure(.network(type: .noResponse))
}
}
}

0 comments on commit d0eb677

Please sign in to comment.