mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-07-01 01:33:37 +00:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
a2105a2ae6 | |||
c1d535ef95 | |||
1083c0f733 | |||
914e4d6910 | |||
4b66e874a7 | |||
aa52da2c04 | |||
e70774f6aa | |||
bd683b16f2 | |||
017bdb85a4 | |||
99c8562cf5 | |||
12b920b0af | |||
bfb4f80b8c | |||
d661b9002b | |||
9daae8957a | |||
bde9085d31 | |||
84521cf8d6 | |||
4a6e8c0b11 | |||
ae06e8e892 | |||
57e6b6779f | |||
6136fc2e69 | |||
2c052e2657 | |||
0918e570e4 | |||
024c25bcd9 | |||
154dac3e50 | |||
d778760cc1 |
24
.github/workflows/release.yml
vendored
24
.github/workflows/release.yml
vendored
@ -5,6 +5,20 @@ on:
|
||||
tags:
|
||||
- '*'
|
||||
jobs:
|
||||
test:
|
||||
runs-on: macOS-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Setup Signing
|
||||
env:
|
||||
SIGNING_DATA: ${{ secrets.SIGNING_DATA }}
|
||||
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
||||
HOST_PROFILE_DATA: ${{ secrets.HOST_PROFILE_DATA }}
|
||||
AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- name: Test
|
||||
run: xcrun xcodebuild test -project Secretive.xcodeproj -scheme Secretive
|
||||
build:
|
||||
runs-on: macOS-latest
|
||||
timeout-minutes: 10
|
||||
@ -18,16 +32,22 @@ jobs:
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ github.ref }}
|
||||
body: ''
|
||||
body: "Build: https://github.com/maxgoedjen/secretive/actions/runs/${{ github.run_id }}"
|
||||
draft: true
|
||||
prerelease: false
|
||||
- name: Set up signing
|
||||
- name: Setup Signing
|
||||
env:
|
||||
SIGNING_DATA: ${{ secrets.SIGNING_DATA }}
|
||||
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
||||
HOST_PROFILE_DATA: ${{ secrets.HOST_PROFILE_DATA }}
|
||||
AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- name: Update Build Number
|
||||
env:
|
||||
TAG_NAME: ${{ github.ref }}
|
||||
run: |
|
||||
export CLEAN_TAG=$(echo $TAG_NAME | sed -e 's/refs\/tags\/v//')
|
||||
sed -i '' -e "s/CI_VERSION = 0.0.0/CI_VERSION = $CLEAN_TAG/g" Config/Config.xcconfig
|
||||
- name: Build
|
||||
run: xcrun xcodebuild -project Secretive.xcodeproj -scheme Secretive -configuration Release -archivePath Archive.xcarchive archive
|
||||
- name: Create ZIPs
|
||||
|
18
.github/workflows/test.yml
vendored
Normal file
18
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
name: Test
|
||||
|
||||
on: push
|
||||
jobs:
|
||||
test:
|
||||
runs-on: macOS-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Setup Signing
|
||||
env:
|
||||
SIGNING_DATA: ${{ secrets.SIGNING_DATA }}
|
||||
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
||||
HOST_PROFILE_DATA: ${{ secrets.HOST_PROFILE_DATA }}
|
||||
AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- name: Test
|
||||
run: xcrun xcodebuild test -project Secretive.xcodeproj -scheme Secretive
|
76
CODE_OF_CONDUCT.md
Normal file
76
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at max.goedjen@gmail.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
19
CONTRIBUTING.md
Normal file
19
CONTRIBUTING.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Contributing to Secretive
|
||||
|
||||
Thanks for your interest in contributing to Secretive! Before you contribute, there are a few things I'd like to lay out.
|
||||
|
||||
## Security
|
||||
|
||||
Security is obviously paramount for a project like Secretive. As such, any contributions that compromise the security or auditabilty of the project will be rejected.
|
||||
|
||||
### Dependencies
|
||||
|
||||
Secretive is desigend to be easily auditable by people who are considering using it. In keeping with this, Secretive has no third party dependencies, and any contributions which bring in new dependencies will be rejected.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
All contributors must abide by the [Code of Conduct](CODE_OF_CONDUCT.md)
|
||||
|
||||
## Secretive is Opinionated
|
||||
|
||||
I'm releasing Secretive as open source so that other people can use it and audit it, feeling comfortable in knowing that the source is available so they can see what it's doing. I have a pretty strong idea of what I'd like this project to look like, and I may respectfully decline contributions that don't line up with that vision. If you'd like to propose a change before implementing, please feel free to [Open an Issue with the proposed tag](https://github.com/maxgoedjen/secretive/issues/new?labels=proposed).
|
1
Config/Config.xcconfig
Normal file
1
Config/Config.xcconfig
Normal file
@ -0,0 +1 @@
|
||||
CI_VERSION = 0.0.0
|
41
Config/Secretive.xctestplan
Normal file
41
Config/Secretive.xctestplan
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"configurations" : [
|
||||
{
|
||||
"id" : "5896AE5A-6D5A-48D3-837B-668B646A3273",
|
||||
"name" : "Configuration 1",
|
||||
"options" : {
|
||||
|
||||
}
|
||||
}
|
||||
],
|
||||
"defaultOptions" : {
|
||||
|
||||
},
|
||||
"testTargets" : [
|
||||
{
|
||||
"parallelizable" : true,
|
||||
"target" : {
|
||||
"containerPath" : "container:Secretive.xcodeproj",
|
||||
"identifier" : "50617DAF23FCE4AB0099B055",
|
||||
"name" : "SecretKitTests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parallelizable" : true,
|
||||
"target" : {
|
||||
"containerPath" : "container:Secretive.xcodeproj",
|
||||
"identifier" : "5099A073240242BA0062B6F2",
|
||||
"name" : "SecretAgentKitTests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parallelizable" : true,
|
||||
"target" : {
|
||||
"containerPath" : "container:Secretive.xcodeproj",
|
||||
"identifier" : "50617D9323FCE48E0099B055",
|
||||
"name" : "SecretiveTests"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 1
|
||||
}
|
@ -38,7 +38,7 @@ For non-command-line based apps, like GUI Git clients, you may need to go throug
|
||||
|
||||
### Security Considerations
|
||||
|
||||
For the moment, you must build Secretive from source. For an app like this, it's critical that you trust that the app you're running is the app whose source you've checked out. To this end, Secretive has no third party dependecies, and is designed to be easy for you to audit for exploits.
|
||||
Builds are produced by GitHub Actions with an auditable build and release generation process. Each build has a "Document SHAs" step, which will output SHA checksums for the build produced by the GitHub Action, so you can verify that the source code for a given build corresponds to any given release.
|
||||
|
||||
### A Note Around Code Signing and Keychains
|
||||
|
||||
@ -47,3 +47,7 @@ While Secretive uses the Secure Enclave for key storage, it still relies on Keyc
|
||||
### Backups and Transfers to New Machines
|
||||
|
||||
Beacuse secrets in the Secure Enclave are not exportable, they are not able to be backed up, and you will not be able to transfer them to a new machine. If you get a new Mac, just create a new set of secrets specific to that Mac.
|
||||
|
||||
## Security
|
||||
|
||||
If you discover any vulnerabilities in this project, please notify max.goedjen@gmail.com
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Cocoa
|
||||
import SecretKit
|
||||
import SecretAgentKit
|
||||
import OSLog
|
||||
|
||||
@NSApplicationMain
|
||||
@ -13,7 +14,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
}()
|
||||
let notifier = Notifier()
|
||||
lazy var agent: Agent = {
|
||||
Agent(storeList: storeList, notifier: notifier)
|
||||
Agent(storeList: storeList, witness: notifier)
|
||||
}()
|
||||
lazy var socketController: SocketController = {
|
||||
let path = (NSHomeDirectory() as NSString).appendingPathComponent("socket.ssh") as String
|
||||
|
@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<string>$(CI_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import SecretKit
|
||||
import SecretAgentKit
|
||||
import UserNotifications
|
||||
|
||||
class Notifier {
|
||||
@ -10,13 +11,21 @@ class Notifier {
|
||||
}
|
||||
}
|
||||
|
||||
func notify<SecretType: Secret>(accessTo secret: SecretType) {
|
||||
func notify(accessTo secret: AnySecret, by provenance: SigningRequestProvenance) {
|
||||
let notificationCenter = UNUserNotificationCenter.current()
|
||||
let notificationContent = UNMutableNotificationContent()
|
||||
notificationContent.title = "Signed Request"
|
||||
notificationContent.body = "\(secret.name) was used to sign a request."
|
||||
notificationContent.body = "\(secret.name) was used to sign a request from \(provenance.origin.name)."
|
||||
let request = UNNotificationRequest(identifier: UUID().uuidString, content: notificationContent, trigger: nil)
|
||||
notificationCenter.add(request, withCompletionHandler: nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Notifier: SigningWitness {
|
||||
|
||||
func witness(accessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws {
|
||||
notify(accessTo: secret, by: provenance)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,23 +3,26 @@ import CryptoKit
|
||||
import OSLog
|
||||
import SecretKit
|
||||
import SecretAgentKit
|
||||
import AppKit
|
||||
|
||||
class Agent {
|
||||
public class Agent {
|
||||
|
||||
fileprivate let storeList: SecretStoreList
|
||||
fileprivate let notifier: Notifier
|
||||
fileprivate let witness: SigningWitness?
|
||||
fileprivate let writer = OpenSSHKeyWriter()
|
||||
fileprivate let requestTracer = SigningRequestTracer()
|
||||
|
||||
public init(storeList: SecretStoreList, notifier: Notifier) {
|
||||
public init(storeList: SecretStoreList, witness: SigningWitness? = nil) {
|
||||
os_log(.debug, "Agent is running")
|
||||
self.storeList = storeList
|
||||
self.notifier = notifier
|
||||
self.witness = witness
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Agent {
|
||||
|
||||
func handle(fileHandle: FileHandle) {
|
||||
public func handle(fileHandle: FileHandle) {
|
||||
os_log(.debug, "Agent handling new data")
|
||||
let data = fileHandle.availableData
|
||||
guard !data.isEmpty else { return }
|
||||
@ -40,7 +43,7 @@ extension Agent {
|
||||
os_log(.debug, "Agent returned %@", SSHAgent.ResponseType.agentIdentitiesAnswer.debugDescription)
|
||||
case .signRequest:
|
||||
response.append(SSHAgent.ResponseType.agentSignResponse.data)
|
||||
response.append(try sign(data: data))
|
||||
response.append(try sign(data: data, from: fileHandle.fileDescriptor))
|
||||
os_log(.debug, "Agent returned %@", SSHAgent.ResponseType.agentSignResponse.debugDescription)
|
||||
}
|
||||
} catch {
|
||||
@ -74,26 +77,22 @@ extension Agent {
|
||||
return countData + keyData
|
||||
}
|
||||
|
||||
func sign(data: Data) throws -> Data {
|
||||
func sign(data: Data, from pid: Int32) throws -> Data {
|
||||
let reader = OpenSSHReader(data: data)
|
||||
let writer = OpenSSHKeyWriter()
|
||||
let hash = try reader.readNextChunk()
|
||||
let matching = storeList.stores.compactMap { store -> (AnySecretStore, AnySecret)? in
|
||||
let allMatching = store.secrets.filter { secret in
|
||||
hash == writer.data(secret: secret)
|
||||
}
|
||||
if let matching = allMatching.first {
|
||||
return (store, matching)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
guard let (store, secret) = matching.first else {
|
||||
guard let (store, secret) = secret(matching: hash) else {
|
||||
os_log(.debug, "Agent did not have a key matching %@", hash as NSData)
|
||||
throw AgentError.noMatchingKey
|
||||
}
|
||||
|
||||
let provenance = requestTracer.provenance(from: pid)
|
||||
if let witness = witness {
|
||||
try witness.witness(accessTo: secret, by: provenance)
|
||||
}
|
||||
|
||||
let dataToSign = try reader.readNextChunk()
|
||||
let derSignature = try store.sign(data: dataToSign, with: secret)
|
||||
// TODO: Move this
|
||||
notifier.notify(accessTo: secret)
|
||||
|
||||
let curveData = writer.curveType(for: secret.algorithm, length: secret.keySize).data(using: .utf8)!
|
||||
|
||||
// Convert from DER formatted rep to raw (r||s)
|
||||
@ -130,6 +129,22 @@ extension Agent {
|
||||
|
||||
}
|
||||
|
||||
extension Agent {
|
||||
|
||||
func secret(matching hash: Data) -> (AnySecretStore, AnySecret)? {
|
||||
storeList.stores.compactMap { store -> (AnySecretStore, AnySecret)? in
|
||||
let allMatching = store.secrets.filter { secret in
|
||||
hash == writer.data(secret: secret)
|
||||
}
|
||||
if let matching = allMatching.first {
|
||||
return (store, matching)
|
||||
}
|
||||
return nil
|
||||
}.first
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
extension Agent {
|
||||
|
@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>$(CI_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
|
39
SecretAgentKit/SigningRequestProvenance.swift
Normal file
39
SecretAgentKit/SigningRequestProvenance.swift
Normal file
@ -0,0 +1,39 @@
|
||||
import Foundation
|
||||
import AppKit
|
||||
|
||||
public struct SigningRequestProvenance {
|
||||
|
||||
public var chain: [Process]
|
||||
public init(root: Process) {
|
||||
self.chain = [root]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension SigningRequestProvenance {
|
||||
|
||||
public var origin: Process {
|
||||
chain.last!
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension SigningRequestProvenance {
|
||||
|
||||
public struct Process {
|
||||
|
||||
public let pid: Int32
|
||||
public let name: String
|
||||
public let path: String
|
||||
let parentPID: Int32?
|
||||
|
||||
init(pid: Int32, name: String, path: String, parentPID: Int32?) {
|
||||
self.pid = pid
|
||||
self.name = name
|
||||
self.path = path
|
||||
self.parentPID = parentPID
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
35
SecretAgentKit/SigningRequestTracer.swift
Normal file
35
SecretAgentKit/SigningRequestTracer.swift
Normal file
@ -0,0 +1,35 @@
|
||||
import Foundation
|
||||
import AppKit
|
||||
|
||||
struct SigningRequestTracer {
|
||||
|
||||
func provenance(from pid: Int32) -> SigningRequestProvenance {
|
||||
let pidPointer = UnsafeMutableRawPointer.allocate(byteCount: 4, alignment: 1)
|
||||
var len = socklen_t(MemoryLayout<Int32>.size)
|
||||
getsockopt(pid, SOCK_STREAM, LOCAL_PEERPID, pidPointer, &len)
|
||||
let pid = pidPointer.load(as: Int32.self)
|
||||
let firstInfo = process(from: pid)
|
||||
|
||||
var provenance = SigningRequestProvenance(root: firstInfo)
|
||||
while NSRunningApplication(processIdentifier: provenance.origin.pid) == nil && provenance.origin.parentPID != nil {
|
||||
provenance.chain.append(process(from: provenance.origin.parentPID!))
|
||||
}
|
||||
return provenance
|
||||
}
|
||||
|
||||
func pidAndNameInfo(from pid: Int32) -> kinfo_proc {
|
||||
var len = MemoryLayout<kinfo_proc>.size
|
||||
let infoPointer = UnsafeMutableRawPointer.allocate(byteCount: len, alignment: 1)
|
||||
var name: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, pid]
|
||||
sysctl(&name, UInt32(name.count), infoPointer, &len, nil, 0)
|
||||
return infoPointer.load(as: kinfo_proc.self)
|
||||
}
|
||||
|
||||
func process(from pid: Int32) -> SigningRequestProvenance.Process {
|
||||
var pidAndNameInfo = self.pidAndNameInfo(from: pid)
|
||||
let ppid = pidAndNameInfo.kp_eproc.e_ppid != 0 ? pidAndNameInfo.kp_eproc.e_ppid : nil
|
||||
let procName = String(cString: &pidAndNameInfo.kp_proc.p_comm.0)
|
||||
return SigningRequestProvenance.Process(pid: pid, name: procName, path: "", parentPID: ppid)
|
||||
}
|
||||
|
||||
}
|
8
SecretAgentKit/SigningWitness.swift
Normal file
8
SecretAgentKit/SigningWitness.swift
Normal file
@ -0,0 +1,8 @@
|
||||
import Foundation
|
||||
import SecretKit
|
||||
|
||||
public protocol SigningWitness {
|
||||
|
||||
func witness(accessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws
|
||||
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
class SocketController {
|
||||
public class SocketController {
|
||||
|
||||
fileprivate var fileHandle: FileHandle?
|
||||
fileprivate var port: SocketPort?
|
||||
var handler: ((FileHandle) -> Void)?
|
||||
public var handler: ((FileHandle) -> Void)?
|
||||
|
||||
init(path: String) {
|
||||
public init(path: String) {
|
||||
os_log(.debug, "Socket controller setting up at %@", path)
|
||||
if let _ = try? FileManager.default.removeItem(atPath: path) {
|
||||
os_log(.debug, "Socket controller removed existing socket")
|
@ -11,24 +11,9 @@ import XCTest
|
||||
|
||||
class SecretAgentKitTests: XCTestCase {
|
||||
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testExample() throws {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
func testPerformanceExample() throws {
|
||||
// This is an example of a performance test case.
|
||||
self.measure {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>$(CI_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
|
45
SecretKitTests/OpenSSHWriterTests.swift
Normal file
45
SecretKitTests/OpenSSHWriterTests.swift
Normal file
@ -0,0 +1,45 @@
|
||||
import Foundation
|
||||
import XCTest
|
||||
@testable import SecretKit
|
||||
|
||||
class OpenSSHWriterTests: XCTestCase {
|
||||
|
||||
let writer = OpenSSHKeyWriter()
|
||||
|
||||
func testECDSA256Fingerprint() {
|
||||
XCTAssertEqual(writer.openSSHFingerprint(secret: Constants.ecdsa256Secret), "dc:60:4d:ff:c2:d9:18:8b:2f:24:40:b5:7f:43:47:e5")
|
||||
}
|
||||
|
||||
func testECDSA256PublicKey() {
|
||||
XCTAssertEqual(writer.openSSHString(secret: Constants.ecdsa256Secret),
|
||||
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOVEjgAA5PHqRgwykjN5qM21uWCHFSY/Sqo5gkHAkn+e1MMQKHOLga7ucB9b3mif33MBid59GRK9GEPVlMiSQwo=")
|
||||
}
|
||||
|
||||
func testECDSA256Hash() {
|
||||
XCTAssertEqual(writer.data(secret: Constants.ecdsa256Secret), Data(base64Encoded: "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOVEjgAA5PHqRgwykjN5qM21uWCHFSY/Sqo5gkHAkn+e1MMQKHOLga7ucB9b3mif33MBid59GRK9GEPVlMiSQwo="))
|
||||
}
|
||||
|
||||
func testECDSA384Fingerprint() {
|
||||
XCTAssertEqual(writer.openSSHFingerprint(secret: Constants.ecdsa384Secret), "66:e0:66:d7:41:ed:19:8e:e2:20:df:ce:ac:7e:2b:6e")
|
||||
}
|
||||
|
||||
func testECDSA384PublicKey() {
|
||||
XCTAssertEqual(writer.openSSHString(secret: Constants.ecdsa384Secret),
|
||||
"ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBG2MNc/C5OTHFE2tBvbZCVcpOGa8vBMquiTLkH4lwkeqOPxhi+PyYUfQZMTRJNPiTyWPoMBqNiCIFRVv60yPN/AHufHaOgbdTP42EgMlMMImkAjYUEv9DESHTVIs2PW1yQ==")
|
||||
}
|
||||
|
||||
func testECDSA384Hash() {
|
||||
XCTAssertEqual(writer.data(secret: Constants.ecdsa384Secret), Data(base64Encoded: "AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBG2MNc/C5OTHFE2tBvbZCVcpOGa8vBMquiTLkH4lwkeqOPxhi+PyYUfQZMTRJNPiTyWPoMBqNiCIFRVv60yPN/AHufHaOgbdTP42EgMlMMImkAjYUEv9DESHTVIs2PW1yQ=="))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension OpenSSHWriterTests {
|
||||
|
||||
enum Constants {
|
||||
static let ecdsa256Secret = SmartCard.Secret(id: Data(), name: "Test Key (ECDSA 256)", algorithm: .ellipticCurve, keySize: 256, publicKey: Data(base64Encoded: "BOVEjgAA5PHqRgwykjN5qM21uWCHFSY/Sqo5gkHAkn+e1MMQKHOLga7ucB9b3mif33MBid59GRK9GEPVlMiSQwo=")!)
|
||||
static let ecdsa384Secret = SmartCard.Secret(id: Data(), name: "Test Key (ECDSA 384)", algorithm: .ellipticCurve, keySize: 384, publicKey: Data(base64Encoded: "BG2MNc/C5OTHFE2tBvbZCVcpOGa8vBMquiTLkH4lwkeqOPxhi+PyYUfQZMTRJNPiTyWPoMBqNiCIFRVv60yPN/AHufHaOgbdTP42EgMlMMImkAjYUEv9DESHTVIs2PW1yQ==")!)
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
//
|
||||
// SecretKitTests.swift
|
||||
// SecretKitTests
|
||||
//
|
||||
// Created by Max Goedjen on 2/18/20.
|
||||
// Copyright © 2020 Max Goedjen. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import SecretKit
|
||||
|
||||
class SecretKitTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testExample() {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
func testPerformanceExample() {
|
||||
// This is an example of a performance test case.
|
||||
self.measure {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
50020BB024064869003D4025 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50020BAF24064869003D4025 /* AppDelegate.swift */; };
|
||||
5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.swift */; };
|
||||
50524B442420969E008DBD97 /* OpenSSHWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50524B432420969D008DBD97 /* OpenSSHWriterTests.swift */; };
|
||||
50617D8323FCE48E0099B055 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8223FCE48E0099B055 /* AppDelegate.swift */; };
|
||||
50617D8523FCE48E0099B055 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8423FCE48E0099B055 /* ContentView.swift */; };
|
||||
50617D8723FCE48E0099B055 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50617D8623FCE48E0099B055 /* Assets.xcassets */; };
|
||||
@ -16,7 +17,6 @@
|
||||
50617D8D23FCE48E0099B055 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50617D8B23FCE48E0099B055 /* Main.storyboard */; };
|
||||
50617D9923FCE48E0099B055 /* SecretiveTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D9823FCE48E0099B055 /* SecretiveTests.swift */; };
|
||||
50617DB123FCE4AB0099B055 /* SecretKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50617DA823FCE4AB0099B055 /* SecretKit.framework */; };
|
||||
50617DB823FCE4AB0099B055 /* SecretKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617DB723FCE4AB0099B055 /* SecretKitTests.swift */; };
|
||||
50617DBA23FCE4AB0099B055 /* SecretKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 50617DAA23FCE4AB0099B055 /* SecretKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
50617DBD23FCE4AB0099B055 /* SecretKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50617DA823FCE4AB0099B055 /* SecretKit.framework */; };
|
||||
50617DBE23FCE4AB0099B055 /* SecretKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 50617DA823FCE4AB0099B055 /* SecretKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
@ -30,6 +30,18 @@
|
||||
506838A12415EA5600F55094 /* AnySecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = 506838A02415EA5600F55094 /* AnySecret.swift */; };
|
||||
506838A32415EA5D00F55094 /* AnySecretStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 506838A22415EA5D00F55094 /* AnySecretStore.swift */; };
|
||||
506AB87E2412334700335D91 /* SecretAgent.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 50A3B78A24026B7500D209EA /* SecretAgent.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
50731666241DF8660023809E /* Updater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50731665241DF8660023809E /* Updater.swift */; };
|
||||
50731669241E00C20023809E /* NoticeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50731668241E00C20023809E /* NoticeView.swift */; };
|
||||
507CE4ED2420A3C70029F750 /* Agent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A3B79F24026B9900D209EA /* Agent.swift */; };
|
||||
507CE4EE2420A3CA0029F750 /* SocketController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A3B79D24026B9900D209EA /* SocketController.swift */; };
|
||||
507CE4F02420A4C50029F750 /* SigningWitness.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507CE4EF2420A4C50029F750 /* SigningWitness.swift */; };
|
||||
507CE4F42420A8C10029F750 /* SigningRequestProvenance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507CE4F32420A8C10029F750 /* SigningRequestProvenance.swift */; };
|
||||
507CE4F62420A96F0029F750 /* SigningRequestTracer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507CE4F52420A96F0029F750 /* SigningRequestTracer.swift */; };
|
||||
508A58AA241E06B40069DC07 /* PreviewUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58A9241E06B40069DC07 /* PreviewUpdater.swift */; };
|
||||
508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */; };
|
||||
508A58B5241ED48F0069DC07 /* PreviewAgentStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58B4241ED48F0069DC07 /* PreviewAgentStatusChecker.swift */; };
|
||||
508A5911241EF09C0069DC07 /* SecretAgentKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5099A06C240242BA0062B6F2 /* SecretAgentKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
508A5913241EF0B20069DC07 /* SecretKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 50617DA823FCE4AB0099B055 /* SecretKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
5099A02423FD2AAA0062B6F2 /* CreateSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099A02323FD2AAA0062B6F2 /* CreateSecretView.swift */; };
|
||||
5099A02723FE34FA0062B6F2 /* SmartCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099A02623FE34FA0062B6F2 /* SmartCard.swift */; };
|
||||
5099A02923FE35240062B6F2 /* SmartCardStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099A02823FE35240062B6F2 /* SmartCardStore.swift */; };
|
||||
@ -42,8 +54,6 @@
|
||||
50A3B79124026B7600D209EA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79024026B7600D209EA /* Assets.xcassets */; };
|
||||
50A3B79424026B7600D209EA /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79324026B7600D209EA /* Preview Assets.xcassets */; };
|
||||
50A3B79724026B7600D209EA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79524026B7600D209EA /* Main.storyboard */; };
|
||||
50A3B7A024026B9900D209EA /* SocketController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A3B79D24026B9900D209EA /* SocketController.swift */; };
|
||||
50A3B7A224026B9900D209EA /* Agent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A3B79F24026B9900D209EA /* Agent.swift */; };
|
||||
50A5C18C240E4B4B00E2996C /* SecretAgentKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5099A06C240242BA0062B6F2 /* SecretAgentKit.framework */; };
|
||||
50A5C18D240E4B4B00E2996C /* SecretAgentKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5099A06C240242BA0062B6F2 /* SecretAgentKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
50A5C18F240E4B4C00E2996C /* SecretKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50617DA823FCE4AB0099B055 /* SecretKit.framework */; };
|
||||
@ -84,14 +94,14 @@
|
||||
remoteGlobalIDString = 50617DA723FCE4AB0099B055;
|
||||
remoteInfo = SecretKit;
|
||||
};
|
||||
50617DB423FCE4AB0099B055 /* PBXContainerItemProxy */ = {
|
||||
50617DBB23FCE4AB0099B055 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 50617D7E23FCE48D0099B055;
|
||||
remoteInfo = Secretive;
|
||||
remoteGlobalIDString = 50617DA723FCE4AB0099B055;
|
||||
remoteInfo = SecretKit;
|
||||
};
|
||||
50617DBB23FCE4AB0099B055 /* PBXContainerItemProxy */ = {
|
||||
507CE4F12420A6B50029F750 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
|
||||
proxyType = 1;
|
||||
@ -105,13 +115,6 @@
|
||||
remoteGlobalIDString = 5099A06B240242BA0062B6F2;
|
||||
remoteInfo = SecretAgentKit;
|
||||
};
|
||||
5099A078240242BA0062B6F2 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 50617D7E23FCE48D0099B055;
|
||||
remoteInfo = Secretive;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@ -126,6 +129,26 @@
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
508A5910241EF0920069DC07 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
508A5911241EF09C0069DC07 /* SecretAgentKit.framework in CopyFiles */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
508A5912241EF0AC0069DC07 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
508A5913241EF0B20069DC07 /* SecretKit.framework in CopyFiles */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
50A5C18E240E4B4B00E2996C /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -153,6 +176,7 @@
|
||||
/* Begin PBXFileReference section */
|
||||
50020BAF24064869003D4025 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = "<group>"; };
|
||||
50524B432420969D008DBD97 /* OpenSSHWriterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSSHWriterTests.swift; sourceTree = "<group>"; };
|
||||
50617D7F23FCE48E0099B055 /* Secretive.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Secretive.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
50617D8223FCE48E0099B055 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
50617D8423FCE48E0099B055 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
@ -168,7 +192,6 @@
|
||||
50617DAA23FCE4AB0099B055 /* SecretKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SecretKit.h; sourceTree = "<group>"; };
|
||||
50617DAB23FCE4AB0099B055 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
50617DB023FCE4AB0099B055 /* SecretKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SecretKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
50617DB723FCE4AB0099B055 /* SecretKitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretKitTests.swift; sourceTree = "<group>"; };
|
||||
50617DB923FCE4AB0099B055 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
50617DC623FCE4EA0099B055 /* SecretStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretStore.swift; sourceTree = "<group>"; };
|
||||
50617DC823FCE50E0099B055 /* SecureEnclaveStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureEnclaveStore.swift; sourceTree = "<group>"; };
|
||||
@ -179,6 +202,16 @@
|
||||
5068389D241471CD00F55094 /* SecretStoreList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretStoreList.swift; sourceTree = "<group>"; };
|
||||
506838A02415EA5600F55094 /* AnySecret.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnySecret.swift; sourceTree = "<group>"; };
|
||||
506838A22415EA5D00F55094 /* AnySecretStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnySecretStore.swift; sourceTree = "<group>"; };
|
||||
50731665241DF8660023809E /* Updater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Updater.swift; sourceTree = "<group>"; };
|
||||
50731668241E00C20023809E /* NoticeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeView.swift; sourceTree = "<group>"; };
|
||||
507CE4EF2420A4C50029F750 /* SigningWitness.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningWitness.swift; sourceTree = "<group>"; };
|
||||
507CE4F32420A8C10029F750 /* SigningRequestProvenance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningRequestProvenance.swift; sourceTree = "<group>"; };
|
||||
507CE4F52420A96F0029F750 /* SigningRequestTracer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningRequestTracer.swift; sourceTree = "<group>"; };
|
||||
508A58A9241E06B40069DC07 /* PreviewUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewUpdater.swift; sourceTree = "<group>"; };
|
||||
508A58AB241E121B0069DC07 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
|
||||
508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentStatusChecker.swift; sourceTree = "<group>"; };
|
||||
508A58B4241ED48F0069DC07 /* PreviewAgentStatusChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewAgentStatusChecker.swift; sourceTree = "<group>"; };
|
||||
508A590F241EEF6D0069DC07 /* Secretive.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Secretive.xctestplan; sourceTree = "<group>"; };
|
||||
5099A02323FD2AAA0062B6F2 /* CreateSecretView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateSecretView.swift; sourceTree = "<group>"; };
|
||||
5099A02623FE34FA0062B6F2 /* SmartCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartCard.swift; sourceTree = "<group>"; };
|
||||
5099A02823FE35240062B6F2 /* SmartCardStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartCardStore.swift; sourceTree = "<group>"; };
|
||||
@ -274,6 +307,7 @@
|
||||
50A3B78B24026B7500D209EA /* SecretAgent */,
|
||||
5099A06D240242BA0062B6F2 /* SecretAgentKit */,
|
||||
5099A07A240242BA0062B6F2 /* SecretAgentKitTests */,
|
||||
508A58AF241E144C0069DC07 /* Config */,
|
||||
50617D8023FCE48E0099B055 /* Products */,
|
||||
5099A08B240243730062B6F2 /* Frameworks */,
|
||||
);
|
||||
@ -297,12 +331,8 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
50617D8223FCE48E0099B055 /* AppDelegate.swift */,
|
||||
50617D8423FCE48E0099B055 /* ContentView.swift */,
|
||||
50C385A42407A76D00AF2719 /* SecretDetailView.swift */,
|
||||
5099A02323FD2AAA0062B6F2 /* CreateSecretView.swift */,
|
||||
50B8550C24138C4F009958AC /* DeleteSecretView.swift */,
|
||||
50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */,
|
||||
50C385A8240B636500AF2719 /* SetupView.swift */,
|
||||
508A58B0241ED1C40069DC07 /* Views */,
|
||||
508A58B1241ED1EA0069DC07 /* Controllers */,
|
||||
50617D8623FCE48E0099B055 /* Assets.xcassets */,
|
||||
50617D8B23FCE48E0099B055 /* Main.storyboard */,
|
||||
50617D8E23FCE48E0099B055 /* Info.plist */,
|
||||
@ -317,6 +347,8 @@
|
||||
children = (
|
||||
50617D8923FCE48E0099B055 /* Preview Assets.xcassets */,
|
||||
50617DD123FCEFA90099B055 /* PreviewStore.swift */,
|
||||
508A58A9241E06B40069DC07 /* PreviewUpdater.swift */,
|
||||
508A58B4241ED48F0069DC07 /* PreviewAgentStatusChecker.swift */,
|
||||
);
|
||||
path = "Preview Content";
|
||||
sourceTree = "<group>";
|
||||
@ -347,7 +379,7 @@
|
||||
50617DB623FCE4AB0099B055 /* SecretKitTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
50617DB723FCE4AB0099B055 /* SecretKitTests.swift */,
|
||||
50524B432420969D008DBD97 /* OpenSSHWriterTests.swift */,
|
||||
50617DB923FCE4AB0099B055 /* Info.plist */,
|
||||
);
|
||||
path = SecretKitTests;
|
||||
@ -381,6 +413,38 @@
|
||||
path = OpenSSH;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
508A58AF241E144C0069DC07 /* Config */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
508A590F241EEF6D0069DC07 /* Secretive.xctestplan */,
|
||||
508A58AB241E121B0069DC07 /* Config.xcconfig */,
|
||||
);
|
||||
path = Config;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
508A58B0241ED1C40069DC07 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
50617D8423FCE48E0099B055 /* ContentView.swift */,
|
||||
50731668241E00C20023809E /* NoticeView.swift */,
|
||||
50C385A42407A76D00AF2719 /* SecretDetailView.swift */,
|
||||
5099A02323FD2AAA0062B6F2 /* CreateSecretView.swift */,
|
||||
50B8550C24138C4F009958AC /* DeleteSecretView.swift */,
|
||||
50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */,
|
||||
50C385A8240B636500AF2719 /* SetupView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
508A58B1241ED1EA0069DC07 /* Controllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
50731665241DF8660023809E /* Updater.swift */,
|
||||
508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */,
|
||||
);
|
||||
path = Controllers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5099A02523FE34DE0062B6F2 /* SmartCard */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -406,6 +470,11 @@
|
||||
children = (
|
||||
5099A06E240242BA0062B6F2 /* SecretAgentKit.h */,
|
||||
5099A089240242C20062B6F2 /* SSHAgentProtocol.swift */,
|
||||
50A3B79D24026B9900D209EA /* SocketController.swift */,
|
||||
507CE4EF2420A4C50029F750 /* SigningWitness.swift */,
|
||||
507CE4F32420A8C10029F750 /* SigningRequestProvenance.swift */,
|
||||
507CE4F52420A96F0029F750 /* SigningRequestTracer.swift */,
|
||||
50A3B79F24026B9900D209EA /* Agent.swift */,
|
||||
5099A06F240242BA0062B6F2 /* Info.plist */,
|
||||
);
|
||||
path = SecretAgentKit;
|
||||
@ -431,9 +500,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
50020BAF24064869003D4025 /* AppDelegate.swift */,
|
||||
50A3B79F24026B9900D209EA /* Agent.swift */,
|
||||
5018F54E24064786002EB505 /* Notifier.swift */,
|
||||
50A3B79D24026B9900D209EA /* SocketController.swift */,
|
||||
50A3B79024026B7600D209EA /* Assets.xcassets */,
|
||||
50A3B79524026B7600D209EA /* Main.storyboard */,
|
||||
50A3B79824026B7600D209EA /* Info.plist */,
|
||||
@ -536,12 +603,12 @@
|
||||
50617DAC23FCE4AB0099B055 /* Sources */,
|
||||
50617DAD23FCE4AB0099B055 /* Frameworks */,
|
||||
50617DAE23FCE4AB0099B055 /* Resources */,
|
||||
508A5912241EF0AC0069DC07 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
50617DB323FCE4AB0099B055 /* PBXTargetDependency */,
|
||||
50617DB523FCE4AB0099B055 /* PBXTargetDependency */,
|
||||
);
|
||||
name = SecretKitTests;
|
||||
productName = SecretKitTests;
|
||||
@ -560,6 +627,7 @@
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
507CE4F22420A6B50029F750 /* PBXTargetDependency */,
|
||||
);
|
||||
name = SecretAgentKit;
|
||||
productName = SecretAgentKit;
|
||||
@ -573,12 +641,12 @@
|
||||
5099A070240242BA0062B6F2 /* Sources */,
|
||||
5099A071240242BA0062B6F2 /* Frameworks */,
|
||||
5099A072240242BA0062B6F2 /* Resources */,
|
||||
508A5910241EF0920069DC07 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
5099A077240242BA0062B6F2 /* PBXTargetDependency */,
|
||||
5099A079240242BA0062B6F2 /* PBXTargetDependency */,
|
||||
);
|
||||
name = SecretAgentKitTests;
|
||||
productName = SecretAgentKitTests;
|
||||
@ -628,7 +696,6 @@
|
||||
};
|
||||
50617DAF23FCE4AB0099B055 = {
|
||||
CreatedOnToolsVersion = 11.3;
|
||||
TestTargetID = 50617D7E23FCE48D0099B055;
|
||||
};
|
||||
5099A06B240242BA0062B6F2 = {
|
||||
CreatedOnToolsVersion = 11.4;
|
||||
@ -636,7 +703,6 @@
|
||||
};
|
||||
5099A073240242BA0062B6F2 = {
|
||||
CreatedOnToolsVersion = 11.4;
|
||||
TestTargetID = 50617D7E23FCE48D0099B055;
|
||||
};
|
||||
50A3B78924026B7500D209EA = {
|
||||
CreatedOnToolsVersion = 11.4;
|
||||
@ -733,11 +799,16 @@
|
||||
50C385A9240B636500AF2719 /* SetupView.swift in Sources */,
|
||||
50617D8523FCE48E0099B055 /* ContentView.swift in Sources */,
|
||||
50617DD223FCEFA90099B055 /* PreviewStore.swift in Sources */,
|
||||
508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */,
|
||||
50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */,
|
||||
5099A02423FD2AAA0062B6F2 /* CreateSecretView.swift in Sources */,
|
||||
50731666241DF8660023809E /* Updater.swift in Sources */,
|
||||
50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */,
|
||||
50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */,
|
||||
50731669241E00C20023809E /* NoticeView.swift in Sources */,
|
||||
50617D8323FCE48E0099B055 /* AppDelegate.swift in Sources */,
|
||||
508A58B5241ED48F0069DC07 /* PreviewAgentStatusChecker.swift in Sources */,
|
||||
508A58AA241E06B40069DC07 /* PreviewUpdater.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -773,7 +844,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
50617DB823FCE4AB0099B055 /* SecretKitTests.swift in Sources */,
|
||||
50524B442420969E008DBD97 /* OpenSSHWriterTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -781,7 +852,12 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
507CE4EE2420A3CA0029F750 /* SocketController.swift in Sources */,
|
||||
5099A08A240242C20062B6F2 /* SSHAgentProtocol.swift in Sources */,
|
||||
507CE4ED2420A3C70029F750 /* Agent.swift in Sources */,
|
||||
507CE4F02420A4C50029F750 /* SigningWitness.swift in Sources */,
|
||||
507CE4F62420A96F0029F750 /* SigningRequestTracer.swift in Sources */,
|
||||
507CE4F42420A8C10029F750 /* SigningRequestProvenance.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -799,8 +875,6 @@
|
||||
files = (
|
||||
50020BB024064869003D4025 /* AppDelegate.swift in Sources */,
|
||||
5018F54F24064786002EB505 /* Notifier.swift in Sources */,
|
||||
50A3B7A224026B9900D209EA /* Agent.swift in Sources */,
|
||||
50A3B7A024026B9900D209EA /* SocketController.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -827,26 +901,21 @@
|
||||
target = 50617DA723FCE4AB0099B055 /* SecretKit */;
|
||||
targetProxy = 50617DB223FCE4AB0099B055 /* PBXContainerItemProxy */;
|
||||
};
|
||||
50617DB523FCE4AB0099B055 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 50617D7E23FCE48D0099B055 /* Secretive */;
|
||||
targetProxy = 50617DB423FCE4AB0099B055 /* PBXContainerItemProxy */;
|
||||
};
|
||||
50617DBC23FCE4AB0099B055 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 50617DA723FCE4AB0099B055 /* SecretKit */;
|
||||
targetProxy = 50617DBB23FCE4AB0099B055 /* PBXContainerItemProxy */;
|
||||
};
|
||||
507CE4F22420A6B50029F750 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 50617DA723FCE4AB0099B055 /* SecretKit */;
|
||||
targetProxy = 507CE4F12420A6B50029F750 /* PBXContainerItemProxy */;
|
||||
};
|
||||
5099A077240242BA0062B6F2 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 5099A06B240242BA0062B6F2 /* SecretAgentKit */;
|
||||
targetProxy = 5099A076240242BA0062B6F2 /* PBXContainerItemProxy */;
|
||||
};
|
||||
5099A079240242BA0062B6F2 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 50617D7E23FCE48D0099B055 /* Secretive */;
|
||||
targetProxy = 5099A078240242BA0062B6F2 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
@ -871,6 +940,7 @@
|
||||
/* Begin XCBuildConfiguration section */
|
||||
50617D9B23FCE48E0099B055 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 508A58AB241E121B0069DC07 /* Config.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
@ -931,6 +1001,7 @@
|
||||
};
|
||||
50617D9C23FCE48E0099B055 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 508A58AB241E121B0069DC07 /* Config.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
@ -991,6 +1062,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Secretive/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@ -1001,6 +1073,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@ -1017,6 +1090,7 @@
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Secretive/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@ -1027,6 +1101,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Host";
|
||||
@ -1153,7 +1228,6 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.SecretKitTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Secretive.app/Contents/MacOS/Secretive";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@ -1173,10 +1247,247 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.SecretKitTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Secretive.app/Contents/MacOS/Secretive";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
508A5914241EF1A00069DC07 /* Test */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 508A58AB241E121B0069DC07 /* Config.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Test;
|
||||
};
|
||||
508A5915241EF1A00069DC07 /* Test */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = Secretive/Secretive.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Secretive/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = Secretive/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Host";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Test;
|
||||
};
|
||||
508A5916241EF1A00069DC07 /* Test */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
INFOPLIST_FILE = SecretiveTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.SecretiveTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Secretive.app/Contents/MacOS/Secretive";
|
||||
};
|
||||
name = Test;
|
||||
};
|
||||
508A5917241EF1A00069DC07 /* Test */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = SecretAgent/SecretAgent.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = SecretAgent/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Secret Agent";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Test;
|
||||
};
|
||||
508A5918241EF1A00069DC07 /* Test */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = SecretKit/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretKit;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Test;
|
||||
};
|
||||
508A5919241EF1A00069DC07 /* Test */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
INFOPLIST_FILE = SecretKitTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/../Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.SecretKitTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Test;
|
||||
};
|
||||
508A591A241EF1A00069DC07 /* Test */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = SecretAgentKit/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.SecretAgentKit;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Test;
|
||||
};
|
||||
508A591B241EF1A00069DC07 /* Test */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
INFOPLIST_FILE = SecretAgentKitTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/../Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.SecretAgentKitTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Test;
|
||||
};
|
||||
5099A084240242BA0062B6F2 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@ -1240,7 +1551,6 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
INFOPLIST_FILE = SecretAgentKitTests/Info.plist;
|
||||
@ -1252,7 +1562,6 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.SecretAgentKitTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Secretive.app/Contents/MacOS/Secretive";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@ -1260,7 +1569,6 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
INFOPLIST_FILE = SecretAgentKitTests/Info.plist;
|
||||
@ -1272,7 +1580,6 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.SecretAgentKitTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Secretive.app/Contents/MacOS/Secretive";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@ -1293,7 +1600,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 0.1.0;
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
@ -1318,7 +1625,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 0.1.0;
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Secret Agent";
|
||||
@ -1333,6 +1640,7 @@
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
50617D9B23FCE48E0099B055 /* Debug */,
|
||||
508A5914241EF1A00069DC07 /* Test */,
|
||||
50617D9C23FCE48E0099B055 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
@ -1342,6 +1650,7 @@
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
50617D9E23FCE48E0099B055 /* Debug */,
|
||||
508A5915241EF1A00069DC07 /* Test */,
|
||||
50617D9F23FCE48E0099B055 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
@ -1351,6 +1660,7 @@
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
50617DA123FCE48E0099B055 /* Debug */,
|
||||
508A5916241EF1A00069DC07 /* Test */,
|
||||
50617DA223FCE48E0099B055 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
@ -1360,6 +1670,7 @@
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
50617DC023FCE4AB0099B055 /* Debug */,
|
||||
508A5918241EF1A00069DC07 /* Test */,
|
||||
50617DC123FCE4AB0099B055 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
@ -1369,6 +1680,7 @@
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
50617DC223FCE4AB0099B055 /* Debug */,
|
||||
508A5919241EF1A00069DC07 /* Test */,
|
||||
50617DC323FCE4AB0099B055 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
@ -1378,6 +1690,7 @@
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
5099A084240242BA0062B6F2 /* Debug */,
|
||||
508A591A241EF1A00069DC07 /* Test */,
|
||||
5099A085240242BA0062B6F2 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
@ -1387,6 +1700,7 @@
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
5099A087240242BA0062B6F2 /* Debug */,
|
||||
508A591B241EF1A00069DC07 /* Test */,
|
||||
5099A088240242BA0062B6F2 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
@ -1396,6 +1710,7 @@
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
50A3B79B24026B7600D209EA /* Debug */,
|
||||
508A5917241EF1A00069DC07 /* Test */,
|
||||
50A3B79C24026B7600D209EA /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1140"
|
||||
version = "1.3">
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@ -23,11 +23,47 @@
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
buildConfiguration = "Test"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<TestPlans>
|
||||
<TestPlanReference
|
||||
reference = "container:Config/Secretive.xctestplan"
|
||||
default = "YES">
|
||||
</TestPlanReference>
|
||||
</TestPlans>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "5099A073240242BA0062B6F2"
|
||||
BuildableName = "SecretAgentKitTests.xctest"
|
||||
BlueprintName = "SecretAgentKitTests"
|
||||
ReferencedContainer = "container:Secretive.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "50617DAF23FCE4AB0099B055"
|
||||
BuildableName = "SecretKitTests.xctest"
|
||||
BlueprintName = "SecretKitTests"
|
||||
ReferencedContainer = "container:Secretive.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "50617D9323FCE48E0099B055"
|
||||
BuildableName = "SecretiveTests.xctest"
|
||||
BlueprintName = "SecretiveTests"
|
||||
ReferencedContainer = "container:Secretive.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1140"
|
||||
version = "1.3">
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@ -23,11 +23,29 @@
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
buildConfiguration = "Test"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<TestPlans>
|
||||
<TestPlanReference
|
||||
reference = "container:Config/Secretive.xctestplan"
|
||||
default = "YES">
|
||||
</TestPlanReference>
|
||||
</TestPlans>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES"
|
||||
testExecutionOrdering = "random">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "5099A073240242BA0062B6F2"
|
||||
BuildableName = "SecretAgentKitTests.xctest"
|
||||
BlueprintName = "SecretAgentKitTests"
|
||||
ReferencedContainer = "container:Secretive.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1140"
|
||||
version = "1.3">
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@ -23,12 +23,16 @@
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
buildConfiguration = "Test"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<TestPlans>
|
||||
<TestPlanReference
|
||||
reference = "container:Config/Secretive.xctestplan"
|
||||
default = "YES">
|
||||
</TestPlanReference>
|
||||
</TestPlans>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1140"
|
||||
version = "1.3">
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@ -23,10 +23,16 @@
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
buildConfiguration = "Test"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<TestPlans>
|
||||
<TestPlanReference
|
||||
reference = "container:Config/Secretive.xctestplan"
|
||||
default = "YES">
|
||||
</TestPlanReference>
|
||||
</TestPlans>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
@ -61,7 +67,7 @@
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
buildConfiguration = "Test"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
|
@ -6,6 +6,7 @@ import SecretKit
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
var window: NSWindow!
|
||||
@IBOutlet var newMenuItem: NSMenuItem!
|
||||
@IBOutlet var toolbar: NSToolbar!
|
||||
let storeList: SecretStoreList = {
|
||||
let list = SecretStoreList()
|
||||
@ -13,10 +14,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
list.add(store: SmartCard.Store())
|
||||
return list
|
||||
}()
|
||||
let updater = Updater()
|
||||
let agentStatusChecker = AgentStatusChecker()
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
|
||||
let contentView = ContentView(storeList: storeList)
|
||||
let contentView = ContentView(storeList: storeList, updater: updater, agentStatusChecker: agentStatusChecker, runSetupBlock: { self.runSetup(sender: nil) })
|
||||
// Create the window and set the content view.
|
||||
window = NSWindow(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
|
||||
@ -28,15 +30,27 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
window.titleVisibility = .hidden
|
||||
window.toolbar = toolbar
|
||||
window.isReleasedWhenClosed = false
|
||||
if storeList.modifiableStore?.isAvailable ?? false {
|
||||
let plus = NSTitlebarAccessoryViewController()
|
||||
plus.view = NSButton(image: NSImage(named: NSImage.addTemplateName)!, target: self, action: #selector(add(sender:)))
|
||||
plus.layoutAttribute = .right
|
||||
window.addTitlebarAccessoryViewController(plus)
|
||||
newMenuItem.isEnabled = true
|
||||
}
|
||||
runSetupIfNeeded()
|
||||
}
|
||||
|
||||
func applicationDidBecomeActive(_ notification: Notification) {
|
||||
agentStatusChecker.check()
|
||||
}
|
||||
|
||||
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
|
||||
guard !flag else { return false }
|
||||
window.makeKeyAndOrderFront(self)
|
||||
return true
|
||||
}
|
||||
|
||||
@IBAction func add(sender: AnyObject?) {
|
||||
var addWindow: NSWindow!
|
||||
let addView = CreateSecretView(store: storeList.modifiableStore!) {
|
||||
@ -57,6 +71,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
backing: .buffered, defer: false)
|
||||
let setupView = SetupView() { success in
|
||||
self.window.endSheet(setupWindow)
|
||||
self.agentStatusChecker.check()
|
||||
}
|
||||
setupWindow.contentView = NSHostingView(rootView: setupView)
|
||||
window.beginSheet(setupWindow, completionHandler: nil)
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="16085" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15702"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16085"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
@ -9,7 +9,7 @@
|
||||
<scene sceneID="JPo-4y-FX3">
|
||||
<objects>
|
||||
<application id="hnw-xV-0zn" sceneMemberID="viewController">
|
||||
<menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
||||
<menu key="mainMenu" title="Main Menu" systemMenu="main" autoenablesItems="NO" id="AYu-sK-qS6">
|
||||
<items>
|
||||
<menuItem title="Secretive" id="1Xt-HY-uBw">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
@ -57,9 +57,9 @@
|
||||
</menuItem>
|
||||
<menuItem title="File" id="dMs-cI-mzQ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="File" id="bib-Uj-vzu">
|
||||
<menu key="submenu" title="File" autoenablesItems="NO" id="bib-Uj-vzu">
|
||||
<items>
|
||||
<menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
|
||||
<menuItem title="New" enabled="NO" keyEquivalent="n" id="Was-JA-tGl">
|
||||
<connections>
|
||||
<action selector="addWithSender:" target="Voe-Tx-rLC" id="U1t-YZ-Hn5"/>
|
||||
</connections>
|
||||
@ -102,7 +102,7 @@
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
|
||||
<items>
|
||||
<menuItem title="Set Up Helper App" id="04y-R6-7bF">
|
||||
<menuItem title="Setup Helper App" id="04y-R6-7bF">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="runSetupWithSender:" target="Voe-Tx-rLC" id="Fty-2m-eng"/>
|
||||
@ -125,6 +125,7 @@
|
||||
</application>
|
||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Secretive" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="newMenuItem" destination="Was-JA-tGl" id="C8s-uk-gMA"/>
|
||||
<outlet property="toolbar" destination="bvo-mt-QR4" id="XSF-g2-znt"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
|
@ -1,97 +0,0 @@
|
||||
import SwiftUI
|
||||
import SecretKit
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
@ObservedObject var storeList: SecretStoreList
|
||||
|
||||
@State fileprivate var active: AnySecret.ID?
|
||||
@State fileprivate var showingDeletion = false
|
||||
@State fileprivate var deletingSecret: AnySecret?
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
List(selection: $active) {
|
||||
ForEach(storeList.stores) { store in
|
||||
if store.isAvailable {
|
||||
Section(header: Text(store.name)) {
|
||||
if store.secrets.isEmpty {
|
||||
if store is AnySecretStoreModifiable {
|
||||
NavigationLink(destination: EmptyStoreModifiableView(), tag: Constants.emptyStoreModifiableTag, selection: self.$active) {
|
||||
Text("No Secrets")
|
||||
}
|
||||
} else {
|
||||
NavigationLink(destination: EmptyStoreView(), tag: Constants.emptyStoreTag, selection: self.$active) {
|
||||
Text("No Secrets")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ForEach(store.secrets) { secret in
|
||||
NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: self.$active) {
|
||||
Text(secret.name)
|
||||
}.contextMenu {
|
||||
if store is AnySecretStoreModifiable {
|
||||
Button(action: { self.delete(secret: secret) }) {
|
||||
Text("Delete")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.onAppear {
|
||||
let fallback: AnyHashable
|
||||
if self.storeList.modifiableStore?.isAvailable ?? false {
|
||||
fallback = Constants.emptyStoreModifiableTag
|
||||
} else {
|
||||
fallback = Constants.emptyStoreTag
|
||||
}
|
||||
self.active = self.storeList.stores.compactMap { $0.secrets.first }.first?.id ?? fallback
|
||||
}
|
||||
.listStyle(SidebarListStyle())
|
||||
.frame(minWidth: 100, idealWidth: 240)
|
||||
}
|
||||
.navigationViewStyle(DoubleColumnNavigationViewStyle())
|
||||
.sheet(isPresented: $showingDeletion) {
|
||||
if self.storeList.modifiableStore != nil {
|
||||
DeleteSecretView(secret: self.deletingSecret!, store: self.storeList.modifiableStore!) {
|
||||
self.showingDeletion = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
func delete<SecretType: Secret>(secret: SecretType) {
|
||||
deletingSecret = AnySecret(secret)
|
||||
self.showingDeletion = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ContentView {
|
||||
|
||||
enum Constants {
|
||||
static let emptyStoreModifiableTag: AnyHashable = "emptyStoreModifiableTag"
|
||||
static let emptyStoreTag: AnyHashable = "emptyStoreModifiableTag"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
ContentView(storeList: Preview.storeList(stores: [Preview.Store(numberOfRandomSecrets: 0)], modifiableStores: [Preview.StoreModifiable(numberOfRandomSecrets: 0)]))
|
||||
ContentView(storeList: Preview.storeList(stores: [Preview.Store()], modifiableStores: [Preview.StoreModifiable()]))
|
||||
ContentView(storeList: Preview.storeList(stores: [Preview.Store()]))
|
||||
ContentView(storeList: Preview.storeList(modifiableStores: [Preview.StoreModifiable()]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
33
Secretive/Controllers/AgentStatusChecker.swift
Normal file
33
Secretive/Controllers/AgentStatusChecker.swift
Normal file
@ -0,0 +1,33 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
import AppKit
|
||||
|
||||
protocol AgentStatusCheckerProtocol: ObservableObject {
|
||||
var running: Bool { get }
|
||||
}
|
||||
|
||||
class AgentStatusChecker: ObservableObject, AgentStatusCheckerProtocol {
|
||||
|
||||
@Published var running: Bool = false
|
||||
|
||||
init() {
|
||||
check()
|
||||
}
|
||||
|
||||
func check() {
|
||||
running = secretAgentProcess != nil
|
||||
}
|
||||
|
||||
var secretAgentProcess: NSRunningApplication? {
|
||||
NSRunningApplication.runningApplications(withBundleIdentifier: Constants.secretAgentAppID).first
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension AgentStatusChecker {
|
||||
|
||||
enum Constants {
|
||||
static let secretAgentAppID = "com.maxgoedjen.Secretive.SecretAgent"
|
||||
}
|
||||
|
||||
}
|
82
Secretive/Controllers/Updater.swift
Normal file
82
Secretive/Controllers/Updater.swift
Normal file
@ -0,0 +1,82 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
protocol UpdaterProtocol: ObservableObject {
|
||||
|
||||
var update: Release? { get }
|
||||
|
||||
}
|
||||
|
||||
class Updater: ObservableObject, UpdaterProtocol {
|
||||
|
||||
@Published var update: Release?
|
||||
|
||||
init() {
|
||||
checkForUpdates()
|
||||
let timer = Timer.scheduledTimer(withTimeInterval: 60*60*24, repeats: true) { _ in
|
||||
self.checkForUpdates()
|
||||
}
|
||||
timer.tolerance = 60*60
|
||||
}
|
||||
|
||||
func checkForUpdates() {
|
||||
URLSession.shared.dataTask(with: Constants.updateURL) { data, _, _ in
|
||||
guard let data = data else { return }
|
||||
guard let release = try? JSONDecoder().decode(Release.self, from: data) else { return }
|
||||
self.evaluate(release: release)
|
||||
}.resume()
|
||||
}
|
||||
|
||||
func evaluate(release: Release) {
|
||||
let latestVersion = semVer(from: release.name)
|
||||
let currentVersion = semVer(from: Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String)
|
||||
for (latest, current) in zip(latestVersion, currentVersion) {
|
||||
if latest > current {
|
||||
DispatchQueue.main.async {
|
||||
self.update = release
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func semVer(from stringVersion: String) -> [Int] {
|
||||
var split = stringVersion.split(separator: ".").compactMap { Int($0) }
|
||||
while split.count < 3 {
|
||||
split.append(0)
|
||||
}
|
||||
return split
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Updater {
|
||||
|
||||
enum Constants {
|
||||
static let updateURL = URL(string: "https://api.github.com/repos/maxgoedjen/secretive/releases/latest")!
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct Release: Codable {
|
||||
let name: String
|
||||
let html_url: URL
|
||||
let body: String
|
||||
}
|
||||
|
||||
|
||||
extension Release {
|
||||
|
||||
var critical: Bool {
|
||||
return body.contains(Constants.securityContent)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Release {
|
||||
|
||||
enum Constants {
|
||||
static let securityContent = "Critical Security Update"
|
||||
}
|
||||
|
||||
}
|
@ -17,9 +17,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>$(CI_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<string>$(CI_VERSION)</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
|
12
Secretive/Preview Content/PreviewAgentStatusChecker.swift
Normal file
12
Secretive/Preview Content/PreviewAgentStatusChecker.swift
Normal file
@ -0,0 +1,12 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class PreviewAgentStatusChecker: AgentStatusCheckerProtocol {
|
||||
|
||||
let running: Bool
|
||||
|
||||
init(running: Bool = true) {
|
||||
self.running = running
|
||||
}
|
||||
|
||||
}
|
27
Secretive/Preview Content/PreviewUpdater.swift
Normal file
27
Secretive/Preview Content/PreviewUpdater.swift
Normal file
@ -0,0 +1,27 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class PreviewUpdater: UpdaterProtocol {
|
||||
|
||||
let update: Release?
|
||||
|
||||
init(update: Update = .none) {
|
||||
switch update {
|
||||
case .none:
|
||||
self.update = nil
|
||||
case .advisory:
|
||||
self.update = Release(name: "10.10.10", html_url: URL(string: "https://example.com")!, body: "Some regular update")
|
||||
case .critical:
|
||||
self.update = Release(name: "10.10.10", html_url: URL(string: "https://example.com")!, body: "Critical Security Update")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension PreviewUpdater {
|
||||
|
||||
enum Update {
|
||||
case none, advisory, critical
|
||||
}
|
||||
|
||||
}
|
@ -6,6 +6,8 @@
|
||||
<true/>
|
||||
<key>com.apple.security.smartcard</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)com.maxgoedjen.Secretive</string>
|
||||
|
144
Secretive/Views/ContentView.swift
Normal file
144
Secretive/Views/ContentView.swift
Normal file
@ -0,0 +1,144 @@
|
||||
import SwiftUI
|
||||
import SecretKit
|
||||
|
||||
struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentStatusCheckerProtocol>: View {
|
||||
|
||||
@ObservedObject var storeList: SecretStoreList
|
||||
@ObservedObject var updater: UpdaterType
|
||||
@ObservedObject var agentStatusChecker: AgentStatusCheckerType
|
||||
var runSetupBlock: (() -> Void)?
|
||||
|
||||
@State fileprivate var active: AnySecret.ID?
|
||||
@State fileprivate var showingDeletion = false
|
||||
@State fileprivate var deletingSecret: AnySecret?
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
if updater.update != nil {
|
||||
updateNotice()
|
||||
}
|
||||
if !agentStatusChecker.running {
|
||||
agentNotice()
|
||||
}
|
||||
NavigationView {
|
||||
List(selection: $active) {
|
||||
ForEach(storeList.stores) { store in
|
||||
if store.isAvailable {
|
||||
Section(header: Text(store.name)) {
|
||||
if store.secrets.isEmpty {
|
||||
if store is AnySecretStoreModifiable {
|
||||
NavigationLink(destination: EmptyStoreModifiableView(), tag: Constants.emptyStoreModifiableTag, selection: self.$active) {
|
||||
Text("No Secrets")
|
||||
}
|
||||
} else {
|
||||
NavigationLink(destination: EmptyStoreView(), tag: Constants.emptyStoreTag, selection: self.$active) {
|
||||
Text("No Secrets")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ForEach(store.secrets) { secret in
|
||||
NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: self.$active) {
|
||||
Text(secret.name)
|
||||
}.contextMenu {
|
||||
if store is AnySecretStoreModifiable {
|
||||
Button(action: { self.delete(secret: secret) }) {
|
||||
Text("Delete")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.onAppear {
|
||||
self.active = self.nextDefaultSecret
|
||||
}
|
||||
.listStyle(SidebarListStyle())
|
||||
.frame(minWidth: 100, idealWidth: 240)
|
||||
}
|
||||
.navigationViewStyle(DoubleColumnNavigationViewStyle())
|
||||
.sheet(isPresented: $showingDeletion) {
|
||||
if self.storeList.modifiableStore != nil {
|
||||
DeleteSecretView(secret: self.deletingSecret!, store: self.storeList.modifiableStore!) { deleted in
|
||||
self.showingDeletion = false
|
||||
if deleted {
|
||||
self.active = self.nextDefaultSecret
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateNotice() -> some View {
|
||||
guard let update = updater.update else { return AnyView(Spacer()) }
|
||||
let severity: NoticeView.Severity
|
||||
let text: String
|
||||
if update.critical {
|
||||
severity = .critical
|
||||
text = "Critical Security Update Required"
|
||||
} else {
|
||||
severity = .advisory
|
||||
text = "Update Available"
|
||||
}
|
||||
return AnyView(NoticeView(text: text, severity: severity, actionTitle: "Update") {
|
||||
NSWorkspace.shared.open(update.html_url)
|
||||
})
|
||||
}
|
||||
|
||||
func agentNotice() -> some View {
|
||||
NoticeView(text: "Secret Agent isn't running. Run setup again to fix.", severity: .advisory, actionTitle: "Run Setup") {
|
||||
self.runSetupBlock?()
|
||||
}
|
||||
}
|
||||
|
||||
func delete<SecretType: Secret>(secret: SecretType) {
|
||||
deletingSecret = AnySecret(secret)
|
||||
self.showingDeletion = true
|
||||
}
|
||||
|
||||
var nextDefaultSecret: AnyHashable? {
|
||||
let fallback: AnyHashable
|
||||
if self.storeList.modifiableStore?.isAvailable ?? false {
|
||||
fallback = Constants.emptyStoreModifiableTag
|
||||
} else {
|
||||
fallback = Constants.emptyStoreTag
|
||||
}
|
||||
return self.storeList.stores.compactMap { $0.secrets.first }.first?.id ?? fallback
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fileprivate enum Constants {
|
||||
static let emptyStoreModifiableTag: AnyHashable = "emptyStoreModifiableTag"
|
||||
static let emptyStoreTag: AnyHashable = "emptyStoreModifiableTag"
|
||||
}
|
||||
|
||||
|
||||
#if DEBUG
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
ContentView(storeList: Preview.storeList(stores: [Preview.Store(numberOfRandomSecrets: 0)],
|
||||
modifiableStores: [Preview.StoreModifiable(numberOfRandomSecrets: 0)]),
|
||||
updater: PreviewUpdater(),
|
||||
agentStatusChecker: PreviewAgentStatusChecker())
|
||||
ContentView(storeList: Preview.storeList(stores: [Preview.Store()], modifiableStores: [Preview.StoreModifiable()]), updater: PreviewUpdater(),
|
||||
agentStatusChecker: PreviewAgentStatusChecker())
|
||||
ContentView(storeList: Preview.storeList(stores: [Preview.Store()]), updater: PreviewUpdater(),
|
||||
agentStatusChecker: PreviewAgentStatusChecker())
|
||||
ContentView(storeList: Preview.storeList(modifiableStores: [Preview.StoreModifiable()]), updater: PreviewUpdater(),
|
||||
agentStatusChecker: PreviewAgentStatusChecker())
|
||||
ContentView(storeList: Preview.storeList(stores: [Preview.Store(numberOfRandomSecrets: 0)], modifiableStores: [Preview.StoreModifiable(numberOfRandomSecrets: 0)]), updater: PreviewUpdater(update: .advisory),
|
||||
agentStatusChecker: PreviewAgentStatusChecker())
|
||||
ContentView(storeList: Preview.storeList(stores: [Preview.Store(numberOfRandomSecrets: 0)], modifiableStores: [Preview.StoreModifiable(numberOfRandomSecrets: 0)]), updater: PreviewUpdater(update: .critical),
|
||||
agentStatusChecker: PreviewAgentStatusChecker())
|
||||
ContentView(storeList: Preview.storeList(stores: [Preview.Store(numberOfRandomSecrets: 0)], modifiableStores: [Preview.StoreModifiable(numberOfRandomSecrets: 0)]), updater: PreviewUpdater(update: .critical),
|
||||
agentStatusChecker: PreviewAgentStatusChecker(running: false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -8,9 +8,9 @@ struct DeleteSecretView<StoreType: SecretStoreModifiable>: View {
|
||||
|
||||
@State var confirm = ""
|
||||
|
||||
fileprivate var dismissalBlock: () -> ()
|
||||
fileprivate var dismissalBlock: (Bool) -> ()
|
||||
|
||||
init(secret: StoreType.SecretType, store: StoreType, dismissalBlock: @escaping () -> ()) {
|
||||
init(secret: StoreType.SecretType, store: StoreType, dismissalBlock: @escaping (Bool) -> ()) {
|
||||
self.secret = secret
|
||||
self.store = store
|
||||
self.dismissalBlock = dismissalBlock
|
||||
@ -37,14 +37,16 @@ struct DeleteSecretView<StoreType: SecretStoreModifiable>: View {
|
||||
TextField(secret.name, text: $confirm)
|
||||
}
|
||||
}
|
||||
.onExitCommand(perform: dismissalBlock)
|
||||
.onExitCommand {
|
||||
self.dismissalBlock(false)
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
Spacer()
|
||||
Button(action: delete) {
|
||||
Text("Delete")
|
||||
}.disabled(confirm != secret.name)
|
||||
Button(action: dismissalBlock) {
|
||||
Button(action: { self.dismissalBlock(false) }) {
|
||||
Text("Don't Delete")
|
||||
}
|
||||
}
|
||||
@ -53,6 +55,6 @@ struct DeleteSecretView<StoreType: SecretStoreModifiable>: View {
|
||||
|
||||
func delete() {
|
||||
try! store.delete(secret: secret)
|
||||
dismissalBlock()
|
||||
self.dismissalBlock(true)
|
||||
}
|
||||
}
|
57
Secretive/Views/NoticeView.swift
Normal file
57
Secretive/Views/NoticeView.swift
Normal file
@ -0,0 +1,57 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct NoticeView: View {
|
||||
|
||||
let text: String
|
||||
let severity: Severity
|
||||
let actionTitle: String?
|
||||
let action: (() -> Void)?
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
Text(text).bold()
|
||||
Spacer()
|
||||
if action != nil {
|
||||
Button(action: action!) {
|
||||
Text(actionTitle!)
|
||||
}
|
||||
}
|
||||
}.padding().background(color)
|
||||
}
|
||||
|
||||
var color: Color {
|
||||
switch severity {
|
||||
case .advisory:
|
||||
return Color.orange
|
||||
case .critical:
|
||||
return Color.red
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension NoticeView {
|
||||
|
||||
enum Severity {
|
||||
case advisory, critical
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
struct NoticeView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
NoticeView(text: "Agent Not Running", severity: .advisory, actionTitle: "Run Setup") {
|
||||
print("OK")
|
||||
}
|
||||
NoticeView(text: "Critical Security Update Required", severity: .critical, actionTitle: "Update") {
|
||||
print("OK")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -24,11 +24,5 @@ class SecretiveTests: XCTestCase {
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
func testPerformanceExample() {
|
||||
// This is an example of a performance test case.
|
||||
self.measure {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user