Skip to content

Commit d3308fa

Browse files
Merge pull request #24 from Tinder/maxwelle/enterprise-support
Adds enterprise support
2 parents f8f1317 + e7248df commit d3308fa

File tree

44 files changed

+827
-300
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+827
-300
lines changed

.bazelrc

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
common --enable_bzlmod
2+
13
build --incompatible_disallow_empty_glob
24
build --apple_platform_type=macos
35
build --incompatible_strict_action_env
@@ -8,4 +10,4 @@ build --crosstool_top=@local_config_apple_cc//:toolchain
810
build --host_crosstool_top=@local_config_apple_cc//:toolchain
911
test --test_output=errors
1012
test --test_summary=detailed
11-
common --enable_bzlmod
13+
test:record_snapshots --spawn_strategy=local

.bazelversion

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
7.3.1

MODULE.bazel

-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,5 @@ bazel_dep(name = "swift_argument_parser", version = "1.3.0", repo_name = "com_gi
88
non_module_dependencies = use_extension("//:extensions.bzl", "non_module_dependencies")
99
use_repo(
1010
non_module_dependencies,
11-
"com_github_kitura_blueecc",
1211
"com_github_kylef_pathkit",
1312
)

MODULE.bazel.lock

+5-15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Makefile

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.PHONY: record_snapshots
2+
record_snapshots:
3+
bazel test //Tests/... \
4+
--config=record_snapshots \
5+
--test_env=BUILD_WORKSPACE_DIRECTORY=$$(pwd) \
6+
--test_env=SNAPSHOT_DIRECTORY="$$(pwd)/Tests/SignHereLibraryTests" \
7+
--test_env=RERECORD_SNAPSHOTS=TRUE

Sources/SignHereLibrary/BUILD

-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ swift_library(
2626
deps = [
2727
"//Sources/CoreLibrary",
2828
"@com_github_apple_swift_argument_parser//:ArgumentParser",
29-
"@com_github_kitura_blueecc//:BlueECC",
3029
],
3130
)
3231

Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift

+14-11
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand {
126126
case certificateSigningRequestSubject = "certificateSigningRequestSubject"
127127
case profileName = "profileName"
128128
case autoRegenerate = "autoRegenerate"
129+
case enterprise = "enterprise"
129130
}
130131

131132
@Option(help: "The key identifier of the private key (https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests)")
@@ -189,6 +190,9 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand {
189190
@Flag(help: "Defines if the profile should be regenerated in case it already exists (optional)")
190191
internal var autoRegenerate = false
191192

193+
@Flag(help: "Controls if the enterprise API should be used.")
194+
internal var enterprise: Bool = false
195+
192196
private let files: Files
193197
private let log: Log
194198
private let shell: Shell
@@ -206,10 +210,7 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand {
206210
shell = shellImp
207211
uuid = UUIDImp()
208212
iTunesConnectService = iTunesConnectServiceImp(
209-
network: NetworkImp(),
210-
files: filesImp,
211-
shell: shellImp,
212-
clock: clockImp
213+
enterprise: false
213214
)
214215
}
215216

@@ -236,7 +237,8 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand {
236237
bundleIdentifierName: String?,
237238
platform: String,
238239
profileName: String?,
239-
autoRegenerate: Bool
240+
autoRegenerate: Bool,
241+
enterprise: Bool
240242
) {
241243
self.files = files
242244
self.log = log
@@ -261,24 +263,23 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand {
261263
self.platform = platform
262264
self.profileName = profileName
263265
self.autoRegenerate = autoRegenerate
266+
self.enterprise = enterprise
264267
}
265268

266269
internal init(from decoder: Decoder) throws {
267270
let filesImp: Files = FilesImp()
268271
let clockImp: Clock = ClockImp()
269272
let shellImp: Shell = ShellImp()
270273
let container: KeyedDecodingContainer<CodingKeys> = try decoder.container(keyedBy: CodingKeys.self)
274+
let enterprise: Bool = try container.decode(Bool.self, forKey: .enterprise)
271275
self.init(
272276
files: filesImp,
273277
log: LogImp(),
274278
jsonWebTokenService: JSONWebTokenServiceImp(clock: clockImp),
275279
shell: shellImp,
276280
uuid: UUIDImp(),
277281
iTunesConnectService: iTunesConnectServiceImp(
278-
network: NetworkImp(),
279-
files: filesImp,
280-
shell: shellImp,
281-
clock: clockImp
282+
enterprise: enterprise
282283
),
283284
keyIdentifier: try container.decode(String.self, forKey: .keyIdentifier),
284285
issuerID: try container.decode(String.self, forKey: .issuerID),
@@ -296,15 +297,17 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand {
296297
bundleIdentifierName: try container.decodeIfPresent(String.self, forKey: .bundleIdentifierName),
297298
platform: try container.decode(String.self, forKey: .platform),
298299
profileName: try container.decodeIfPresent(String.self, forKey: .profileName),
299-
autoRegenerate: try container.decode(Bool.self, forKey: .autoRegenerate)
300+
autoRegenerate: try container.decode(Bool.self, forKey: .autoRegenerate),
301+
enterprise: enterprise
300302
)
301303
}
302304

303305
internal func run() throws {
304306
let jsonWebToken: String = try jsonWebTokenService.createToken(
305307
keyIdentifier: keyIdentifier,
306308
issuerID: issuerID,
307-
secretKey: try files.read(Path(itunesConnectKeyPath))
309+
secretKey: try files.read(Path(itunesConnectKeyPath)),
310+
enterprise: enterprise
308311
)
309312
let deviceIDs: Set<String> = try iTunesConnectService.fetchITCDeviceIDs(jsonWebToken: jsonWebToken)
310313
guard let profileName, let profile = try? fetchProvisioningProfile(jsonWebToken: jsonWebToken, name: profileName)

Sources/SignHereLibrary/Commands/DeleteProvisioningProfileCommand.swift

+14-11
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ internal struct DeleteProvisioningProfileCommand: ParsableCommand {
2424
case keyIdentifier = "keyIdentifier"
2525
case issuerID = "issuerID"
2626
case itunesConnectKeyPath = "itunesConnectKeyPath"
27+
case enterprise = "enterprise"
2728
}
2829

2930
@Option(help: "The iTunes Connect API ID of the provisioning profile to delete (https://developer.apple.com/documentation/appstoreconnectapi/profile)")
@@ -38,6 +39,9 @@ internal struct DeleteProvisioningProfileCommand: ParsableCommand {
3839
@Option(help: "The path to the private key (https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests)")
3940
internal var itunesConnectKeyPath: String
4041

42+
@Flag(help: "Controls if the enterprise API should be used.")
43+
internal var enterprise: Bool = false
44+
4145
private let files: Files
4246
private let jsonWebTokenService: JSONWebTokenService
4347
private let iTunesConnectService: iTunesConnectService
@@ -47,10 +51,7 @@ internal struct DeleteProvisioningProfileCommand: ParsableCommand {
4751
files = filesImp
4852
jsonWebTokenService = JSONWebTokenServiceImp(clock: ClockImp())
4953
iTunesConnectService = iTunesConnectServiceImp(
50-
network: NetworkImp(),
51-
files: filesImp,
52-
shell: ShellImp(),
53-
clock: ClockImp()
54+
enterprise: false
5455
)
5556
}
5657

@@ -61,7 +62,8 @@ internal struct DeleteProvisioningProfileCommand: ParsableCommand {
6162
provisioningProfileId: String,
6263
keyIdentifier: String,
6364
issuerID: String,
64-
itunesConnectKeyPath: String
65+
itunesConnectKeyPath: String,
66+
enterprise: Bool
6567
) {
6668
self.files = files
6769
self.jsonWebTokenService = jsonWebTokenService
@@ -70,32 +72,33 @@ internal struct DeleteProvisioningProfileCommand: ParsableCommand {
7072
self.keyIdentifier = keyIdentifier
7173
self.issuerID = issuerID
7274
self.itunesConnectKeyPath = itunesConnectKeyPath
75+
self.enterprise = enterprise
7376
}
7477

7578
internal init(from decoder: Decoder) throws {
7679
let filesImp: Files = FilesImp()
7780
let container: KeyedDecodingContainer<CodingKeys> = try decoder.container(keyedBy: CodingKeys.self)
81+
let enterprise: Bool = try container.decode(Bool.self, forKey: .enterprise)
7882
self.init(
7983
files: filesImp,
8084
jsonWebTokenService: JSONWebTokenServiceImp(clock: ClockImp()),
8185
iTunesConnectService: iTunesConnectServiceImp(
82-
network: NetworkImp(),
83-
files: filesImp,
84-
shell: ShellImp(),
85-
clock: ClockImp()
86+
enterprise: enterprise
8687
),
8788
provisioningProfileId: try container.decode(String.self, forKey: .provisioningProfileId),
8889
keyIdentifier: try container.decode(String.self, forKey: .keyIdentifier),
8990
issuerID: try container.decode(String.self, forKey: .issuerID),
90-
itunesConnectKeyPath: try container.decode(String.self, forKey: .itunesConnectKeyPath)
91+
itunesConnectKeyPath: try container.decode(String.self, forKey: .itunesConnectKeyPath),
92+
enterprise: enterprise
9193
)
9294
}
9395

9496
internal func run() throws {
9597
let jsonWebToken: String = try jsonWebTokenService.createToken(
9698
keyIdentifier: keyIdentifier,
9799
issuerID: issuerID,
98-
secretKey: try files.read(Path(itunesConnectKeyPath))
100+
secretKey: try files.read(Path(itunesConnectKeyPath)),
101+
enterprise: enterprise
99102
)
100103
try iTunesConnectService.deleteProvisioningProfile(
101104
jsonWebToken: jsonWebToken,

Sources/SignHereLibrary/Models/ProfileType.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ enum ProfileType {
2828

2929
var usesDevices: Bool {
3030
switch self {
31-
case .appStore: return false
31+
case .appStore, .inHouse: return false
3232
default: return true
3333
}
3434
}

Sources/SignHereLibrary/Services/JSONWebTokenService.swift

+11-13
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import CoreLibrary
99
import Foundation
10-
import CryptorECC
10+
import CryptoKit
1111

1212
// ME: Documented here https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests
1313

@@ -16,7 +16,8 @@ internal protocol JSONWebTokenService {
1616
func createToken(
1717
keyIdentifier: String,
1818
issuerID: String,
19-
secretKey: Data
19+
secretKey: Data,
20+
enterprise: Bool
2021
) throws -> String
2122
}
2223

@@ -44,7 +45,8 @@ internal class JSONWebTokenServiceImp: JSONWebTokenService {
4445
func createToken(
4546
keyIdentifier: String,
4647
issuerID: String,
47-
secretKey: Data
48+
secretKey: Data,
49+
enterprise: Bool
4850
) throws -> String {
4951
let header: JSONWebTokenHeader = .init(
5052
alg: "ES256",
@@ -57,33 +59,29 @@ internal class JSONWebTokenServiceImp: JSONWebTokenService {
5759
iss: issuerID,
5860
iat: currentTime.timeIntervalSince1970,
5961
exp: expirationTime.timeIntervalSince1970,
60-
aud: "appstoreconnect-v1"
62+
aud: enterprise ? "apple-developer-enterprise-v1" : "appstoreconnect-v1"
6163
)
6264
let jsonEncoder: JSONEncoder = .init()
6365
var components: [String] = [
6466
urlBase64Encode(data: try jsonEncoder.encode(header)),
6567
".",
6668
urlBase64Encode(data: try jsonEncoder.encode(payload)),
6769
]
68-
let signature: String = try createSignedHeaderPayload(data: Data(components.joined().utf8), secretKey: secretKey)
70+
let signature: String = try createSignature(data: Data(components.joined().utf8), secretKey: secretKey)
6971
components.append(contentsOf: [
7072
".",
7173
signature
7274
])
7375
return components.joined()
7476
}
7577

76-
private func createSignedHeaderPayload(data: Data, secretKey: Data) throws -> String {
78+
private func createSignature(data: Data, secretKey: Data) throws -> String {
7779
guard let keyString = String(data: secretKey, encoding: .utf8) else {
7880
throw Error.unableToCreateKeyString
7981
}
80-
let privateKey: ECPrivateKey = try .init(key: keyString)
81-
guard privateKey.curve == .prime256v1
82-
else {
83-
throw Error.unableToCreatePrivateKey
84-
}
85-
let signedData: ECSignature = try data.sign(with: privateKey)
86-
return urlBase64Encode(data: signedData.r + signedData.s)
82+
let key = try P256.Signing.PrivateKey(pemRepresentation: keyString)
83+
let signature = try key.signature(for: data)
84+
return urlBase64Encode(data: signature.rawRepresentation)
8785
}
8886

8987
private func urlBase64Encode(data: Data) -> String {

0 commit comments

Comments
 (0)