Skip to content

Commit

Permalink
Merge pull request #18 from omarzl/task/keychain-creation-log
Browse files Browse the repository at this point in the history
Added new keychain path log
  • Loading branch information
tinder-maxwellelliott authored May 23, 2024
2 parents 778e10c + 6c3b211 commit 24ffc37
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 6 deletions.
6 changes: 3 additions & 3 deletions MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 48 additions & 0 deletions Sources/SignHereLibrary/Commands/CreateKeychainCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ internal struct CreateKeychainCommand: ParsableCommand {
case unableToUnlockKeychain(keychainName: String, output: ShellOutput)
case unableToUpdateKeychainLockTimeout(keychainName: String, output: ShellOutput)
case unableToCreateKeychain(keychainName: String, output: ShellOutput)
case unableToListKeychains(output: ShellOutput)
case unableToFindKeychain(keychainName: String, output: ShellOutput)

var description: String {
switch self {
Expand All @@ -47,6 +49,19 @@ internal struct CreateKeychainCommand: ParsableCommand {
- Output: \(output.outputString)
- Error: \(output.errorString)
"""
case let .unableToListKeychains(output: output):
return """
[CreateKeychainCommand] Unable to list keychains
- Output: \(output.outputString)
- Error: \(output.errorString)
"""
case let .unableToFindKeychain(keychainName: keychainName, output: output):
return """
[CreateKeychainCommand] Unable to find keychain
- Keychain Name: \(keychainName)
- Output: \(output.outputString)
- Error: \(output.errorString)
"""
}
}
}
Expand All @@ -64,21 +79,25 @@ internal struct CreateKeychainCommand: ParsableCommand {

private let shell: Shell
private let keychain: Keychain
private let log: Log

internal init() {
let shellImp: Shell = ShellImp()
shell = shellImp
keychain = KeychainImp(shell: shellImp, processInfo: ProcessInfoImp())
log = LogImp()
}

internal init(
shell: Shell,
keychain: Keychain,
log: Log,
keychainName: String,
keychainPassword: String
) {
self.shell = shell
self.keychain = keychain
self.log = log
self.keychainName = keychainName
self.keychainPassword = keychainPassword
}
Expand All @@ -89,6 +108,7 @@ internal struct CreateKeychainCommand: ParsableCommand {
self.init(
shell: shellImp,
keychain: KeychainImp(shell: shellImp, processInfo: ProcessInfoImp()),
log: LogImp(),
keychainName: try container.decode(String.self, forKey: .keychainName),
keychainPassword: try container.decode(String.self, forKey: .keychainPassword)
)
Expand All @@ -104,6 +124,7 @@ internal struct CreateKeychainCommand: ParsableCommand {
try keychain.setDefaultKeychain(keychainName: keychainName)
try updateKeychainLockTimeout()
try unlockKeychain()
try logKeychainPath()
}

private func createKeychain() throws {
Expand Down Expand Up @@ -157,4 +178,31 @@ internal struct CreateKeychainCommand: ParsableCommand {
)
}
}

private func logKeychainPath() throws {
let output: ShellOutput = shell.execute([
"security",
"list-keychains"
])
guard output.isSuccessful
else {
throw Error.unableToListKeychains(
output: output
)
}
guard let keychainLine = output
.outputString
.components(separatedBy: "\n")
.first(where: { $0.contains(keychainName) })
else {
throw Error.unableToFindKeychain(
keychainName: keychainName,
output: output
)
}
let keychainPath = keychainLine
.trimmingCharacters(in: .whitespacesAndNewlines)
.replacingOccurrences(of:"\"", with: "")
log.append("Keychain created in the path: \(keychainPath)")
}
}
84 changes: 81 additions & 3 deletions Tests/SignHereLibraryTests/CreateKeychainCommandTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@ import XCTest
final class CreateKeychainCommandTests: XCTestCase {
var shell: ShellMock!
var keychain: KeychainMock!
var log: LogMock!
var subject: CreateKeychainCommand!

override func setUp() {
super.setUp()
shell = .init()
keychain = .init()
log = .init()
subject = .init(
shell: shell,
keychain: keychain,
log: log,
keychainName: "keychainName",
keychainPassword: "keychainPassword"
)
Expand All @@ -33,6 +36,7 @@ final class CreateKeychainCommandTests: XCTestCase {
override func tearDown() {
shell = nil
keychain = nil
log = nil
subject = nil
super.tearDown()
}
Expand Down Expand Up @@ -69,6 +73,21 @@ final class CreateKeychainCommandTests: XCTestCase {
).description,
as: .lines
)

assertSnapshot(
matching: CreateKeychainCommand.Error.unableToListKeychains(
output: .init(status: 0, data: .init("output".utf8), errorData: .init("errorOutput".utf8))
).description,
as: .lines
)

assertSnapshot(
matching: CreateKeychainCommand.Error.unableToFindKeychain(
keychainName: "keychainName",
output: .init(status: 0, data: .init("output".utf8), errorData: .init("errorOutput".utf8))
).description,
as: .lines
)
}

func test_initDecoder() throws {
Expand All @@ -92,7 +111,8 @@ final class CreateKeychainCommandTests: XCTestCase {
var executeLaunchPaths: [ShellOutput] = [
.init(status: 1, data: .init("createKeychain".utf8), errorData: .init()),
.init(status: 0, data: .init("updateKeychainLockTimeout".utf8), errorData: .init()),
.init(status: 0, data: .init("unlockKeychain".utf8), errorData: .init())
.init(status: 0, data: .init("unlockKeychain".utf8), errorData: .init()),
.init(status: 0, data: .init("listKeychain".utf8), errorData: .init())
]
shell.executeLaunchPathHandler = { _, _, _, _ in
executeLaunchPaths.removeFirst()
Expand All @@ -119,7 +139,8 @@ final class CreateKeychainCommandTests: XCTestCase {
var executeLaunchPaths: [ShellOutput] = [
.init(status: 0, data: .init("createKeychain".utf8), errorData: .init()),
.init(status: 1, data: .init("updateKeychainLockTimeout".utf8), errorData: .init()),
.init(status: 0, data: .init("unlockKeychain".utf8), errorData: .init())
.init(status: 0, data: .init("unlockKeychain".utf8), errorData: .init()),
.init(status: 0, data: .init("listKeychain".utf8), errorData: .init())
]
shell.executeLaunchPathHandler = { _, _, _, _ in
executeLaunchPaths.removeFirst()
Expand All @@ -146,7 +167,8 @@ final class CreateKeychainCommandTests: XCTestCase {
var executeLaunchPaths: [ShellOutput] = [
.init(status: 0, data: .init("createKeychain".utf8), errorData: .init()),
.init(status: 0, data: .init("updateKeychainLockTimeout".utf8), errorData: .init()),
.init(status: 1, data: .init("unlockKeychain".utf8), errorData: .init())
.init(status: 1, data: .init("unlockKeychain".utf8), errorData: .init()),
.init(status: 0, data: .init("listKeychain".utf8), errorData: .init()),
]
shell.executeLaunchPathHandler = { _, _, _, _ in
executeLaunchPaths.removeFirst()
Expand All @@ -167,4 +189,60 @@ final class CreateKeychainCommandTests: XCTestCase {
as: .dump
)
}

func test_execute_cannotListKeychain() throws {
// GIVEN
var executeLaunchPaths: [ShellOutput] = [
.init(status: 0, data: .init("createKeychain".utf8), errorData: .init()),
.init(status: 0, data: .init("updateKeychainLockTimeout".utf8), errorData: .init()),
.init(status: 0, data: .init("unlockKeychain".utf8), errorData: .init()),
.init(status: 1, data: .init("listKeychain".utf8), errorData: .init()),
]
shell.executeLaunchPathHandler = { _, _, _, _ in
executeLaunchPaths.removeFirst()
}

// WHEN
XCTAssertThrowsError(try subject.run()) {
if case CreateKeychainCommand.Error.unableToListKeychains(output: _) = $0 {
XCTAssertTrue(true)
} else {
XCTFail($0.localizedDescription)
}
}

// THEN
assertSnapshot(
matching: shell.executeLaunchPathArgValues,
as: .dump
)
}

func test_execute_cannotFindKeychain() throws {
// GIVEN
var executeLaunchPaths: [ShellOutput] = [
.init(status: 0, data: .init("createKeychain".utf8), errorData: .init()),
.init(status: 0, data: .init("updateKeychainLockTimeout".utf8), errorData: .init()),
.init(status: 0, data: .init("unlockKeychain".utf8), errorData: .init()),
.init(status: 0, data: .init("listKeychain".utf8), errorData: .init()),
]
shell.executeLaunchPathHandler = { _, _, _, _ in
executeLaunchPaths.removeFirst()
}

// WHEN
XCTAssertThrowsError(try subject.run()) {
if case CreateKeychainCommand.Error.unableToFindKeychain(keychainName: _, output: _) = $0 {
XCTAssertTrue(true)
} else {
XCTFail($0.localizedDescription)
}
}

// THEN
assertSnapshot(
matching: shell.executeLaunchPathArgValues,
as: .dump
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[CreateKeychainCommand] Unable to list keychains
- Output: output
- Error: errorOutput
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[CreateKeychainCommand] Unable to find keychain
- Keychain Name: keychainName
- Output: output
- Error: errorOutput
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
▿ 4 elements
▿ (4 elements)
- .0: "/usr/bin/env"
▿ .1: 5 elements
- "security"
- "create-keychain"
- "-p"
- "keychainPassword"
- "keychainName"
- .2: Optional<Dictionary<String, String>>.none
- .3: Optional<Data>.none
▿ (4 elements)
- .0: "/usr/bin/env"
▿ .1: 6 elements
- "security"
- "set-keychain-settings"
- "-t"
- "7200"
- "-l"
- "keychainName"
- .2: Optional<Dictionary<String, String>>.none
- .3: Optional<Data>.none
▿ (4 elements)
- .0: "/usr/bin/env"
▿ .1: 5 elements
- "security"
- "unlock-keychain"
- "-p"
- "keychainPassword"
- "keychainName"
- .2: Optional<Dictionary<String, String>>.none
- .3: Optional<Data>.none
▿ (4 elements)
- .0: "/usr/bin/env"
▿ .1: 2 elements
- "security"
- "list-keychains"
- .2: Optional<Dictionary<String, String>>.none
- .3: Optional<Data>.none
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
▿ 4 elements
▿ (4 elements)
- .0: "/usr/bin/env"
▿ .1: 5 elements
- "security"
- "create-keychain"
- "-p"
- "keychainPassword"
- "keychainName"
- .2: Optional<Dictionary<String, String>>.none
- .3: Optional<Data>.none
▿ (4 elements)
- .0: "/usr/bin/env"
▿ .1: 6 elements
- "security"
- "set-keychain-settings"
- "-t"
- "7200"
- "-l"
- "keychainName"
- .2: Optional<Dictionary<String, String>>.none
- .3: Optional<Data>.none
▿ (4 elements)
- .0: "/usr/bin/env"
▿ .1: 5 elements
- "security"
- "unlock-keychain"
- "-p"
- "keychainPassword"
- "keychainName"
- .2: Optional<Dictionary<String, String>>.none
- .3: Optional<Data>.none
▿ (4 elements)
- .0: "/usr/bin/env"
▿ .1: 2 elements
- "security"
- "list-keychains"
- .2: Optional<Dictionary<String, String>>.none
- .3: Optional<Data>.none

0 comments on commit 24ffc37

Please sign in to comment.