misc: moved all to /cert-manager and corrected API issues

This commit is contained in:
Sheen Capadngan
2025-11-26 03:19:55 +08:00
parent 19066bcca0
commit 6754da9c21
81 changed files with 3306 additions and 1051 deletions
+2 -3
View File
@@ -87,14 +87,13 @@ def bootstrap_infisical(context: Context):
ca_slug = faker.slug() ca_slug = faker.slug()
resp = client.post( resp = client.post(
"/api/v1/pki/ca/internal", "/api/v1/cert-manager/ca/internal",
headers=headers, headers=headers,
json={ json={
"projectId": project["id"], "projectId": project["id"],
"name": ca_slug, "name": ca_slug,
"type": "internal", "type": "internal",
"status": "active", "status": "active",
"enableDirectIssuance": True,
"configuration": { "configuration": {
"type": "root", "type": "root",
"organization": "Infisican Inc", "organization": "Infisican Inc",
@@ -115,7 +114,7 @@ def bootstrap_infisical(context: Context):
cert_template_slug = faker.slug() cert_template_slug = faker.slug()
resp = client.post( resp = client.post(
"/api/v2/certificate-templates", "/api/v1/cert-manager/certificate-templates",
headers=headers, headers=headers,
json={ json={
"projectId": project["id"], "projectId": project["id"],
@@ -2,7 +2,7 @@ Feature: Access Control
Scenario Outline: Access resources across different account Scenario Outline: Access resources across different account
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account0 Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account0
Then I memorize acme_account0.uri with jq "capture("/(?<id>[^/]+)$") | .id" as account0_id Then I memorize acme_account0.uri with jq "capture("/(?<id>[^/]+)$") | .id" as account0_id
When I create certificate signing request as csr When I create certificate signing request as csr
@@ -34,7 +34,7 @@ Feature: Access Control
Then the value response.status_code should not be equal to 404 Then the value response.status_code should not be equal to 404
And I put away current ACME client as client0 And I put away current ACME client as client0
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email maidu@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account1 Then I register a new ACME account with email maidu@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account1
Then I peak and memorize the next nonce as nonce Then I peak and memorize the next nonce as nonce
When I send a raw ACME request to "<url>" When I send a raw ACME request to "<url>"
@@ -53,7 +53,7 @@ Feature: Access Control
Examples: Endpoints Examples: Endpoints
| src_var | jq | dest_var | url | payload | | src_var | jq | dest_var | url | payload |
| order | . | not_used | {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/accounts/{account0_id}/orders | | | order | . | not_used | {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/accounts/{account0_id}/orders | |
| order | . | not_used | {order.uri} | | | order | . | not_used | {order.uri} | |
| order | . | not_used | {order.uri}/finalize | {\"csr\": \"\"} | | order | . | not_used | {order.uri}/finalize | {\"csr\": \"\"} |
| order | . | not_used | {order.uri}/certificate | | | order | . | not_used | {order.uri}/certificate | |
@@ -62,7 +62,7 @@ Feature: Access Control
Scenario Outline: Access resources across a different profiles Scenario Outline: Access resources across a different profiles
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account0 Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account0
Then I memorize acme_account0.uri with jq "capture("/(?<id>[^/]+)$") | .id" as account0_id Then I memorize acme_account0.uri with jq "capture("/(?<id>[^/]+)$") | .id" as account0_id
When I create certificate signing request as csr When I create certificate signing request as csr
@@ -96,7 +96,7 @@ Feature: Access Control
Given I make a random slug as profile_slug Given I make a random slug as profile_slug
Given I use AUTH_TOKEN for authentication Given I use AUTH_TOKEN for authentication
When I send a "POST" request to "/api/v1/pki/certificate-profiles" with JSON payload When I send a "POST" request to "/api/v1/cert-manager/certificate-profiles" with JSON payload
""" """
{ {
"projectId": "{PROJECT_ID}", "projectId": "{PROJECT_ID}",
@@ -110,10 +110,10 @@ Feature: Access Control
""" """
Then the value response.status_code should be equal to 200 Then the value response.status_code should be equal to 200
Then I memorize response with jq ".certificateProfile.id" as profile_id Then I memorize response with jq ".certificateProfile.id" as profile_id
When I send a "GET" request to "/api/v1/pki/certificate-profiles/{profile_id}/acme/eab-secret/reveal" When I send a "GET" request to "/api/v1/cert-manager/certificate-profiles/{profile_id}/acme/eab-secret/reveal"
Then I memorize response with jq ".eabKid" as eab_kid Then I memorize response with jq ".eabKid" as eab_kid
And I memorize response with jq ".eabSecret" as eab_secret And I memorize response with jq ".eabSecret" as eab_secret
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{profile_id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{profile_id}/directory"
Then I register a new ACME account with email maidu@infisical.com and EAB key id "{eab_kid}" with secret "{eab_secret}" as acme_account1 Then I register a new ACME account with email maidu@infisical.com and EAB key id "{eab_kid}" with secret "{eab_secret}" as acme_account1
Then I peak and memorize the next nonce as nonce Then I peak and memorize the next nonce as nonce
Then I memorize <src_var> with jq "<jq>" as <dest_var> Then I memorize <src_var> with jq "<jq>" as <dest_var>
@@ -133,7 +133,7 @@ Feature: Access Control
Examples: Endpoints Examples: Endpoints
| src_var | jq | dest_var | url | payload | | src_var | jq | dest_var | url | payload |
| order | . | not_used | {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/accounts/{account0_id}/orders | | | order | . | not_used | {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/accounts/{account0_id}/orders | |
| order | . | not_used | {order.uri} | | | order | . | not_used | {order.uri} | |
| order | . | not_used | {order.uri}/finalize | {\"csr\": \"\"} | | order | . | not_used | {order.uri}/finalize | {\"csr\": \"\"} |
| order | . | not_used | {order.uri}/certificate | | | order | . | not_used | {order.uri}/certificate | |
@@ -143,7 +143,7 @@ Feature: Access Control
Scenario Outline: Access resources across a different profile with the same key pair Scenario Outline: Access resources across a different profile with the same key pair
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account0 Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account0
Then I memorize acme_account0.uri with jq "capture("/(?<id>[^/]+)$") | .id" as account0_id Then I memorize acme_account0.uri with jq "capture("/(?<id>[^/]+)$") | .id" as account0_id
When I create certificate signing request as csr When I create certificate signing request as csr
@@ -177,7 +177,7 @@ Feature: Access Control
Given I make a random slug as profile_slug Given I make a random slug as profile_slug
Given I use AUTH_TOKEN for authentication Given I use AUTH_TOKEN for authentication
When I send a "POST" request to "/api/v1/pki/certificate-profiles" with JSON payload When I send a "POST" request to "/api/v1/cert-manager/certificate-profiles" with JSON payload
""" """
{ {
"projectId": "{PROJECT_ID}", "projectId": "{PROJECT_ID}",
@@ -191,10 +191,10 @@ Feature: Access Control
""" """
Then the value response.status_code should be equal to 200 Then the value response.status_code should be equal to 200
Then I memorize response with jq ".certificateProfile.id" as profile_id Then I memorize response with jq ".certificateProfile.id" as profile_id
When I send a "GET" request to "/api/v1/pki/certificate-profiles/{profile_id}/acme/eab-secret/reveal" When I send a "GET" request to "/api/v1/cert-manager/certificate-profiles/{profile_id}/acme/eab-secret/reveal"
Then I memorize response with jq ".eabKid" as eab_kid Then I memorize response with jq ".eabKid" as eab_kid
And I memorize response with jq ".eabSecret" as eab_secret And I memorize response with jq ".eabSecret" as eab_secret
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{profile_id}/directory" with the key pair from client0 When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{profile_id}/directory" with the key pair from client0
Then I register a new ACME account with email maidu@infisical.com and EAB key id "{eab_kid}" with secret "{eab_secret}" as acme_account1 Then I register a new ACME account with email maidu@infisical.com and EAB key id "{eab_kid}" with secret "{eab_secret}" as acme_account1
Then I peak and memorize the next nonce as nonce Then I peak and memorize the next nonce as nonce
Then I memorize <src_var> with jq "<jq>" as <dest_var> Then I memorize <src_var> with jq "<jq>" as <dest_var>
@@ -214,7 +214,7 @@ Feature: Access Control
Examples: Endpoints Examples: Endpoints
| src_var | jq | dest_var | url | payload | | src_var | jq | dest_var | url | payload |
| order | . | not_used | {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/accounts/{account0_id}/orders | | | order | . | not_used | {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/accounts/{account0_id}/orders | |
| order | . | not_used | {order.uri} | | | order | . | not_used | {order.uri} | |
| order | . | not_used | {order.uri}/finalize | {\"csr\": \"\"} | | order | . | not_used | {order.uri}/finalize | {\"csr\": \"\"} |
| order | . | not_used | {order.uri}/certificate | | | order | . | not_used | {order.uri}/certificate | |
@@ -223,7 +223,7 @@ Feature: Access Control
Scenario Outline: URL mismatch Scenario Outline: URL mismatch
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
Then I memorize acme_account.uri with jq "capture("/(?<id>[^/]+)$") | .id" as account_id Then I memorize acme_account.uri with jq "capture("/(?<id>[^/]+)$") | .id" as account_id
When I create certificate signing request as csr When I create certificate signing request as csr
@@ -258,8 +258,8 @@ Feature: Access Control
Examples: Endpoints Examples: Endpoints
| src_var | jq | dest_var | actual_url | bad_url | error_detail | | src_var | jq | dest_var | actual_url | bad_url | error_detail |
| order | . | not_used | {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/accounts/{account_id}/orders | BAD | Invalid URL in the protected header | | order | . | not_used | {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/accounts/{account_id}/orders | BAD | Invalid URL in the protected header |
| order | . | not_used | {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/accounts/{account_id}/orders | https://evil.com/api/v1/pki/acme/profiles/{acme_profile.id}/accounts/{account_id}/orders | URL mismatch in the protected header | | order | . | not_used | {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/accounts/{account_id}/orders | https://evil.com/api/v1/cert-manager/acme/profiles/{acme_profile.id}/accounts/{account_id}/orders | URL mismatch in the protected header |
| order | . | not_used | {order.uri} | BAD | Invalid URL in the protected header | | order | . | not_used | {order.uri} | BAD | Invalid URL in the protected header |
| order | . | not_used | {order.uri} | https://example.com/acmes/orders/FOOBAR | URL mismatch in the protected header | | order | . | not_used | {order.uri} | https://example.com/acmes/orders/FOOBAR | URL mismatch in the protected header |
| order | . | not_used | {order.uri}/finalize | BAD | Invalid URL in the protected header | | order | . | not_used | {order.uri}/finalize | BAD | Invalid URL in the protected header |
@@ -273,7 +273,7 @@ Feature: Access Control
Scenario Outline: Send KID and JWK in the same time Scenario Outline: Send KID and JWK in the same time
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
And I memorize acme_account.uri with jq "capture("/(?<id>[^/]+)$") | .id" as account_id And I memorize acme_account.uri with jq "capture("/(?<id>[^/]+)$") | .id" as account_id
When I create certificate signing request as csr When I create certificate signing request as csr
@@ -312,8 +312,8 @@ Feature: Access Control
Examples: Endpoints Examples: Endpoints
| src_var | jq | dest_var | url | | src_var | jq | dest_var | url |
| order | . | not_used | {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/accounts/{account_id}/orders | | order | . | not_used | {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/accounts/{account_id}/orders |
| order | . | not_used | {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-order | | order | . | not_used | {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/new-order |
| order | . | not_used | {order.uri} | | order | . | not_used | {order.uri} |
| order | . | not_used | {order.uri}/finalize | | order | . | not_used | {order.uri}/finalize |
| order | . | not_used | {order.uri}/certificate | | order | . | not_used | {order.uri}/certificate |
+12 -12
View File
@@ -2,13 +2,13 @@ Feature: Account
Scenario: Create a new account Scenario: Create a new account
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
And the value acme_account.uri with jq "." should match pattern {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/accounts/(.+) And the value acme_account.uri with jq "." should match pattern {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/accounts/(.+)
Scenario: Create a new account with the same key pair twice Scenario: Create a new account with the same key pair twice
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
And I memorize acme_account.uri as kid And I memorize acme_account.uri as kid
And I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account2 And I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account2
@@ -17,7 +17,7 @@ Feature: Account
Scenario: Find an existing account Scenario: Find an existing account
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
And I memorize acme_account.uri as account_uri And I memorize acme_account.uri as account_uri
And I find the existing ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as retrieved_account And I find the existing ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as retrieved_account
@@ -26,7 +26,7 @@ Feature: Account
# Note: This is a very special case for cert-manager. # Note: This is a very special case for cert-manager.
Scenario: Create a new account with EAB then retrieve it without EAB Scenario: Create a new account with EAB then retrieve it without EAB
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
And I memorize acme_account.uri as account_uri And I memorize acme_account.uri as account_uri
And I find the existing ACME account without EAB as retrieved_account And I find the existing ACME account without EAB as retrieved_account
@@ -35,13 +35,13 @@ Feature: Account
Scenario: Create a new account without EAB Scenario: Create a new account without EAB
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com without EAB Then I register a new ACME account with email fangpen@infisical.com without EAB
And the value error with jq ".type" should be equal to "urn:ietf:params:acme:error:externalAccountRequired" And the value error with jq ".type" should be equal to "urn:ietf:params:acme:error:externalAccountRequired"
Scenario Outline: Scenario: Create a new account with bad EAB credentials Scenario Outline: Scenario: Create a new account with bad EAB credentials
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "<eab_kid>" with secret "<eab_secret>" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "<eab_kid>" with secret "<eab_secret>" as acme_account
And the value error with jq ".type" should be equal to "<error_type>" And the value error with jq ".type" should be equal to "<error_type>"
And the value error with jq ".detail" should be equal to "<error_msg>" And the value error with jq ".detail" should be equal to "<error_msg>"
@@ -57,7 +57,7 @@ Feature: Account
Scenario Outline: Scenario: Create a new account with bad EAB url Scenario Outline: Scenario: Create a new account with bad EAB url
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
And I use a different new-account URL "<url>" for EAB signature And I use a different new-account URL "<url>" for EAB signature
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
And the value error with jq ".type" should be equal to "urn:ietf:params:acme:error:externalAccountRequired" And the value error with jq ".type" should be equal to "urn:ietf:params:acme:error:externalAccountRequired"
@@ -65,9 +65,9 @@ Feature: Account
Examples: Bad URLs Examples: Bad URLs
| url | | url |
| {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-account-bad | | {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/new-account-bad |
| {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-account?foo=bar | | {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/new-account?foo=bar |
| {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-account#foobar | | {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/new-account#foobar |
| {BASE_URL}/acme/new-account | | {BASE_URL}/acme/new-account |
| https://example.com/api/v1/pki/acme/profiles/{acme_profile.id}/new-account-bad | | https://example.com/api/v1/cert-manager/acme/profiles/{acme_profile.id}/new-account-bad |
| bad | | bad |
+2 -2
View File
@@ -2,7 +2,7 @@ Feature: Authorization
Scenario: Get authorization Scenario: Get authorization
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
When I create certificate signing request as csr When I create certificate signing request as csr
Then I add names to certificate signing request csr Then I add names to certificate signing request csr
@@ -14,7 +14,7 @@ Feature: Authorization
Then I create a RSA private key pair as cert_key Then I create a RSA private key pair as cert_key
And I sign the certificate signing request csr with private key cert_key and output it as csr_pem in PEM format And I sign the certificate signing request csr with private key cert_key and output it as csr_pem in PEM format
And I submit the certificate signing request PEM csr_pem certificate order to the ACME server as order And I submit the certificate signing request PEM csr_pem certificate order to the ACME server as order
And the value order.authorizations[0].uri with jq "." should match pattern {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/authorizations/(.+) And the value order.authorizations[0].uri with jq "." should match pattern {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/authorizations/(.+)
And the value order.authorizations[0].body with jq ".status" should be equal to "pending" And the value order.authorizations[0].body with jq ".status" should be equal to "pending"
And the value order.authorizations[0].body with jq ".challenges | map(pick(.type, .status)) | sort_by(.type)" should be equal to json And the value order.authorizations[0].body with jq ".challenges | map(pick(.type, .status)) | sort_by(.type)" should be equal to json
""" """
@@ -3,7 +3,7 @@ Feature: ACME Cert Profile
Scenario: Create a cert profile Scenario: Create a cert profile
Given I make a random slug as profile_slug Given I make a random slug as profile_slug
And I use AUTH_TOKEN for authentication And I use AUTH_TOKEN for authentication
When I send a "POST" request to "/api/v1/pki/certificate-profiles" with JSON payload When I send a "POST" request to "/api/v1/cert-manager/certificate-profiles" with JSON payload
""" """
{ {
"projectId": "{PROJECT_ID}", "projectId": "{PROJECT_ID}",
@@ -25,7 +25,7 @@ Feature: ACME Cert Profile
Scenario: Reveal EAB secret Scenario: Reveal EAB secret
Given I make a random slug as profile_slug Given I make a random slug as profile_slug
And I use AUTH_TOKEN for authentication And I use AUTH_TOKEN for authentication
When I send a "POST" request to "/api/v1/pki/certificate-profiles" with JSON payload When I send a "POST" request to "/api/v1/cert-manager/certificate-profiles" with JSON payload
""" """
{ {
"projectId": "{PROJECT_ID}", "projectId": "{PROJECT_ID}",
@@ -39,11 +39,11 @@ Feature: ACME Cert Profile
""" """
Then the value response.status_code should be equal to 200 Then the value response.status_code should be equal to 200
And I memorize response with jq ".certificateProfile.id" as profile_id And I memorize response with jq ".certificateProfile.id" as profile_id
When I send a "GET" request to "/api/v1/pki/certificate-profiles/{profile_id}/acme/eab-secret/reveal" When I send a "GET" request to "/api/v1/cert-manager/certificate-profiles/{profile_id}/acme/eab-secret/reveal"
Then the value response.status_code should be equal to 200 Then the value response.status_code should be equal to 200
And the value response with jq ".eabKid" should be equal to "{profile_id}" And the value response with jq ".eabKid" should be equal to "{profile_id}"
And the value response with jq ".eabSecret" should be present And the value response with jq ".eabSecret" should be present
And I memorize response with jq ".eabKid" as eab_kid And I memorize response with jq ".eabKid" as eab_kid
And I memorize response with jq ".eabSecret" as eab_secret And I memorize response with jq ".eabSecret" as eab_secret
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{profile_id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{profile_id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{eab_kid}" with secret "{eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{eab_kid}" with secret "{eab_secret}" as acme_account
@@ -2,7 +2,7 @@ Feature: Challenge
Scenario: Validate challenge Scenario: Validate challenge
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
When I create certificate signing request as csr When I create certificate signing request as csr
Then I add names to certificate signing request csr Then I add names to certificate signing request csr
@@ -24,7 +24,7 @@ Feature: Challenge
Scenario: Validate challenges for multiple domains Scenario: Validate challenges for multiple domains
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
When I create certificate signing request as csr When I create certificate signing request as csr
Then I add names to certificate signing request csr Then I add names to certificate signing request csr
@@ -58,7 +58,7 @@ Feature: Challenge
Scenario: Did not finish all challenges Scenario: Did not finish all challenges
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
When I create certificate signing request as csr When I create certificate signing request as csr
Then I add names to certificate signing request csr Then I add names to certificate signing request csr
@@ -153,7 +153,7 @@ Feature: Challenge
Scenario: CSR names mismatch with order identifier Scenario: CSR names mismatch with order identifier
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
When I create certificate signing request as csr When I create certificate signing request as csr
Then I add names to certificate signing request csr Then I add names to certificate signing request csr
@@ -165,13 +165,13 @@ Feature: Challenge
And I create a RSA private key pair as cert_key And I create a RSA private key pair as cert_key
And I sign the certificate signing request csr with private key cert_key and output it as csr_pem in PEM format And I sign the certificate signing request csr with private key cert_key and output it as csr_pem in PEM format
Then I peak and memorize the next nonce as nonce Then I peak and memorize the next nonce as nonce
When I send a raw ACME request to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-order" When I send a raw ACME request to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/new-order"
""" """
{ {
"protected": { "protected": {
"alg": "RS256", "alg": "RS256",
"nonce": "{nonce}", "nonce": "{nonce}",
"url": "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-order", "url": "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/new-order",
"kid": "{acme_account.uri}" "kid": "{acme_account.uri}"
}, },
"payload": { "payload": {
@@ -2,14 +2,14 @@ Feature: Directory
Scenario: Get the directory of ACME service urls Scenario: Get the directory of ACME service urls
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I send a "GET" request to "/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I send a "GET" request to "/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then the response status code should be "200" Then the response status code should be "200"
And the response body should match JSON value And the response body should match JSON value
""" """
{ {
"newNonce": "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-nonce", "newNonce": "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/new-nonce",
"newAccount": "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-account", "newAccount": "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/new-account",
"newOrder": "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-order", "newOrder": "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/new-order",
"meta": { "meta": {
"externalAccountRequired": true "externalAccountRequired": true
} }
@@ -87,7 +87,7 @@ Feature: External CA
""" """
Then I memorize cert_template with jq ".certificateTemplate.id" as cert_template_id Then I memorize cert_template with jq ".certificateTemplate.id" as cert_template_id
Given I create an ACME profile with ca {ext_ca_id} and template {cert_template_id} as "acme_profile" Given I create an ACME profile with ca {ext_ca_id} and template {cert_template_id} as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
When I create certificate signing request as csr When I create certificate signing request as csr
Then I add names to certificate signing request csr Then I add names to certificate signing request csr
@@ -2,7 +2,7 @@ Feature: Internal CA
Scenario: CSR with SANs only Scenario: CSR with SANs only
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
When I create certificate signing request as csr When I create certificate signing request as csr
Then I add names to certificate signing request csr Then I add names to certificate signing request csr
+9 -9
View File
@@ -2,13 +2,13 @@ Feature: Nonce
Scenario: Generate a new nonce Scenario: Generate a new nonce
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I send a "HEAD" request to "/api/v1/pki/acme/profiles/{acme_profile.id}/new-nonce" When I send a "HEAD" request to "/api/v1/cert-manager/acme/profiles/{acme_profile.id}/new-nonce"
Then the response status code should be "200" Then the response status code should be "200"
And the response header "Replay-Nonce" should contains non-empty value And the response header "Replay-Nonce" should contains non-empty value
Scenario Outline: Send a bad nonce to account endpoints Scenario Outline: Send a bad nonce to account endpoints
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
And I memorize acme_account.uri with jq "capture("/(?<id>[^/]+)$") | .id" as account_id And I memorize acme_account.uri with jq "capture("/(?<id>[^/]+)$") | .id" as account_id
When I create certificate signing request as csr When I create certificate signing request as csr
@@ -41,8 +41,8 @@ Feature: Nonce
Examples: Endpoints Examples: Endpoints
| src_var | jq | dest_var | url | | src_var | jq | dest_var | url |
| order | . | not_used | {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/accounts/{account_id}/orders | | order | . | not_used | {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/accounts/{account_id}/orders |
| order | . | not_used | {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-order | | order | . | not_used | {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/new-order |
| order | . | not_used | {order.uri} | | order | . | not_used | {order.uri} |
| order | . | not_used | {order.uri}/finalize | | order | . | not_used | {order.uri}/finalize |
| order | . | not_used | {order.uri}/certificate | | order | . | not_used | {order.uri}/certificate |
@@ -51,7 +51,7 @@ Feature: Nonce
Scenario Outline: Send the same nonce twice Scenario Outline: Send the same nonce twice
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
And I memorize acme_account.uri with jq "capture("/(?<id>[^/]+)$") | .id" as account_id And I memorize acme_account.uri with jq "capture("/(?<id>[^/]+)$") | .id" as account_id
When I create certificate signing request as csr When I create certificate signing request as csr
@@ -65,13 +65,13 @@ Feature: Nonce
And I sign the certificate signing request csr with private key cert_key and output it as csr_pem in PEM format And I sign the certificate signing request csr with private key cert_key and output it as csr_pem in PEM format
And I submit the certificate signing request PEM csr_pem certificate order to the ACME server as order And I submit the certificate signing request PEM csr_pem certificate order to the ACME server as order
And I peak and memorize the next nonce as nonce_value And I peak and memorize the next nonce as nonce_value
When I send a raw ACME request to "/api/v1/pki/acme/profiles/{acme_profile.id}/accounts/{account_id}/orders" When I send a raw ACME request to "/api/v1/cert-manager/acme/profiles/{acme_profile.id}/accounts/{account_id}/orders"
""" """
{ {
"protected": { "protected": {
"alg": "RS256", "alg": "RS256",
"nonce": "{nonce_value}", "nonce": "{nonce_value}",
"url": "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/accounts/{account_id}/orders", "url": "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/accounts/{account_id}/orders",
"kid": "{acme_account.uri}" "kid": "{acme_account.uri}"
}, },
"payload": {} "payload": {}
@@ -98,8 +98,8 @@ Feature: Nonce
Examples: Endpoints Examples: Endpoints
| src_var | jq | dest_var | url | | src_var | jq | dest_var | url |
| order | . | not_used | {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/accounts/{account_id}/orders | | order | . | not_used | {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/accounts/{account_id}/orders |
| order | . | not_used | {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-order | | order | . | not_used | {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/new-order |
| order | . | not_used | {order.uri} | | order | . | not_used | {order.uri} |
| order | . | not_used | {order.uri}/finalize | | order | . | not_used | {order.uri}/finalize |
| order | . | not_used | {order.uri}/certificate | | order | . | not_used | {order.uri}/certificate |
+14 -14
View File
@@ -2,7 +2,7 @@ Feature: Order
Scenario: Create a new order Scenario: Create a new order
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
When I create certificate signing request as csr When I create certificate signing request as csr
Then I add names to certificate signing request csr Then I add names to certificate signing request csr
@@ -14,15 +14,15 @@ Feature: Order
Then I create a RSA private key pair as cert_key Then I create a RSA private key pair as cert_key
And I sign the certificate signing request csr with private key cert_key and output it as csr_pem in PEM format And I sign the certificate signing request csr with private key cert_key and output it as csr_pem in PEM format
And I submit the certificate signing request PEM csr_pem certificate order to the ACME server as order And I submit the certificate signing request PEM csr_pem certificate order to the ACME server as order
And the value order.uri with jq "." should match pattern {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/orders/(.+) And the value order.uri with jq "." should match pattern {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/orders/(.+)
And the value order.body with jq ".status" should be equal to "pending" And the value order.body with jq ".status" should be equal to "pending"
And the value order.body with jq ".identifiers" should be equal to [{"type": "dns", "value": "localhost"}] And the value order.body with jq ".identifiers" should be equal to [{"type": "dns", "value": "localhost"}]
And the value order.body with jq ".finalize" should match pattern {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/orders/(.+)/finalize And the value order.body with jq ".finalize" should match pattern {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/orders/(.+)/finalize
And the value order.body with jq "all(.authorizations[]; startswith("{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/authorizations/"))" should be equal to true And the value order.body with jq "all(.authorizations[]; startswith("{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/authorizations/"))" should be equal to true
Scenario: Create a new order with SANs Scenario: Create a new order with SANs
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
When I create certificate signing request as csr When I create certificate signing request as csr
Then I add names to certificate signing request csr Then I add names to certificate signing request csr
@@ -52,7 +52,7 @@ Feature: Order
Scenario: Fetch an order Scenario: Fetch an order
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
When I create certificate signing request as csr When I create certificate signing request as csr
Then I add names to certificate signing request csr Then I add names to certificate signing request csr
@@ -67,21 +67,21 @@ Feature: Order
And I send an ACME post-as-get to order.uri as fetched_order And I send an ACME post-as-get to order.uri as fetched_order
And the value fetched_order with jq ".status" should be equal to "pending" And the value fetched_order with jq ".status" should be equal to "pending"
And the value fetched_order with jq ".identifiers" should be equal to [{"type": "dns", "value": "localhost"}] And the value fetched_order with jq ".identifiers" should be equal to [{"type": "dns", "value": "localhost"}]
And the value fetched_order with jq ".finalize" should match pattern {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/orders/(.+)/finalize And the value fetched_order with jq ".finalize" should match pattern {BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/orders/(.+)/finalize
And the value fetched_order with jq "all(.authorizations[]; startswith("{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/authorizations/"))" should be equal to true And the value fetched_order with jq "all(.authorizations[]; startswith("{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/authorizations/"))" should be equal to true
Scenario Outline: Create an order with invalid identifier types Scenario Outline: Create an order with invalid identifier types
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
And I peak and memorize the next nonce as nonce And I peak and memorize the next nonce as nonce
When I send a raw ACME request to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-order" When I send a raw ACME request to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/new-order"
""" """
{ {
"protected": { "protected": {
"alg": "RS256", "alg": "RS256",
"nonce": "{nonce}", "nonce": "{nonce}",
"url": "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-order", "url": "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/new-order",
"kid": "{acme_account.uri}" "kid": "{acme_account.uri}"
}, },
"payload": { "payload": {
@@ -105,16 +105,16 @@ Feature: Order
Scenario Outline: Create an order with invalid identifier values Scenario Outline: Create an order with invalid identifier values
Given I have an ACME cert profile as "acme_profile" Given I have an ACME cert profile as "acme_profile"
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory"
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
And I peak and memorize the next nonce as nonce And I peak and memorize the next nonce as nonce
When I send a raw ACME request to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-order" When I send a raw ACME request to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/new-order"
""" """
{ {
"protected": { "protected": {
"alg": "RS256", "alg": "RS256",
"nonce": "{nonce}", "nonce": "{nonce}",
"url": "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/new-order", "url": "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/new-order",
"kid": "{acme_account.uri}" "kid": "{acme_account.uri}"
}, },
"payload": { "payload": {
+8 -8
View File
@@ -56,7 +56,7 @@ def step_impl(context: Context, profile_var: str):
profile_slug = faker.slug() profile_slug = faker.slug()
jwt_token = context.vars["AUTH_TOKEN"] jwt_token = context.vars["AUTH_TOKEN"]
response = context.http_client.post( response = context.http_client.post(
"/api/v1/pki/certificate-profiles", "/api/v1/cert-manager/certificate-profiles",
headers=dict(authorization="Bearer {}".format(jwt_token)), headers=dict(authorization="Bearer {}".format(jwt_token)),
json={ json={
"projectId": context.vars["PROJECT_ID"], "projectId": context.vars["PROJECT_ID"],
@@ -74,7 +74,7 @@ def step_impl(context: Context, profile_var: str):
kid = profile_id kid = profile_id
response = context.http_client.get( response = context.http_client.get(
f"/api/v1/pki/certificate-profiles/{profile_id}/acme/eab-secret/reveal", f"/api/v1/cert-manager/certificate-profiles/{profile_id}/acme/eab-secret/reveal",
headers=dict(authorization="Bearer {}".format(jwt_token)), headers=dict(authorization="Bearer {}".format(jwt_token)),
) )
response.raise_for_status() response.raise_for_status()
@@ -153,7 +153,7 @@ def step_impl(context: Context, var_name: str):
ca_slug = faker.slug() ca_slug = faker.slug()
config = replace_vars(json.loads(context.text), context.vars) config = replace_vars(json.loads(context.text), context.vars)
response = context.http_client.post( response = context.http_client.post(
"/api/v1/pki/ca/acme", "/api/v1/cert-manager/ca/acme",
headers=dict(authorization="Bearer {}".format(jwt_token)), headers=dict(authorization="Bearer {}".format(jwt_token)),
json={ json={
"projectId": context.vars["PROJECT_ID"], "projectId": context.vars["PROJECT_ID"],
@@ -174,7 +174,7 @@ def step_impl(context: Context, var_name: str):
template_slug = faker.slug() template_slug = faker.slug()
config = replace_vars(json.loads(context.text), context.vars) config = replace_vars(json.loads(context.text), context.vars)
response = context.http_client.post( response = context.http_client.post(
"/api/v2/certificate-templates", "/api/v1/cert-manager/certificate-templates",
headers=dict(authorization="Bearer {}".format(jwt_token)), headers=dict(authorization="Bearer {}".format(jwt_token)),
json={ json={
"projectId": context.vars["PROJECT_ID"], "projectId": context.vars["PROJECT_ID"],
@@ -194,7 +194,7 @@ def step_impl(context: Context, ca_id: str, template_id: str, profile_var: str):
profile_slug = faker.slug() profile_slug = faker.slug()
jwt_token = context.vars["AUTH_TOKEN"] jwt_token = context.vars["AUTH_TOKEN"]
response = context.http_client.post( response = context.http_client.post(
"/api/v1/pki/certificate-profiles", "/api/v1/cert-manager/certificate-profiles",
headers=dict(authorization="Bearer {}".format(jwt_token)), headers=dict(authorization="Bearer {}".format(jwt_token)),
json={ json={
"projectId": context.vars["PROJECT_ID"], "projectId": context.vars["PROJECT_ID"],
@@ -212,7 +212,7 @@ def step_impl(context: Context, ca_id: str, template_id: str, profile_var: str):
kid = profile_id kid = profile_id
response = context.http_client.get( response = context.http_client.get(
f"/api/v1/pki/certificate-profiles/{profile_id}/acme/eab-secret/reveal", f"/api/v1/cert-manager/certificate-profiles/{profile_id}/acme/eab-secret/reveal",
headers=dict(authorization="Bearer {}".format(jwt_token)), headers=dict(authorization="Bearer {}".format(jwt_token)),
) )
response.raise_for_status() response.raise_for_status()
@@ -236,7 +236,7 @@ def step_impl(context: Context, profile_var: str):
profile_slug = faker.slug() profile_slug = faker.slug()
jwt_token = context.vars["AUTH_TOKEN"] jwt_token = context.vars["AUTH_TOKEN"]
response = context.http_client.post( response = context.http_client.post(
"/api/v1/pki/certificate-profiles", "/api/v1/cert-manager/certificate-profiles",
headers=dict(authorization="Bearer {}".format(jwt_token)), headers=dict(authorization="Bearer {}".format(jwt_token)),
json={ json={
"projectId": context.vars["PROJECT_ID"], "projectId": context.vars["PROJECT_ID"],
@@ -254,7 +254,7 @@ def step_impl(context: Context, profile_var: str):
kid = profile_id kid = profile_id
response = context.http_client.get( response = context.http_client.get(
f"/api/v1/pki/certificate-profiles/{profile_id}/acme/eab-secret/reveal", f"/api/v1/cert-manager/certificate-profiles/{profile_id}/acme/eab-secret/reveal",
headers=dict(authorization="Bearer {}".format(jwt_token)), headers=dict(authorization="Bearer {}".format(jwt_token)),
) )
response.raise_for_status() response.raise_for_status()
+1 -1
View File
@@ -110,7 +110,7 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
await pkiRouter.register(registerCaCrlRouter, { prefix: "/crl" }); await pkiRouter.register(registerCaCrlRouter, { prefix: "/crl" });
await pkiRouter.register(registerPkiAcmeRouter, { prefix: "/acme" }); await pkiRouter.register(registerPkiAcmeRouter, { prefix: "/acme" });
}, },
{ prefix: "/pki" } { prefix: "/cert-manager" }
); );
await server.register( await server.register(
+12 -11
View File
@@ -77,7 +77,8 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
done(error, undefined); done(error, undefined);
} }
}); });
// GET /api/v1/pki/acme/profiles/<profile_id>/directory
// GET /api/v1/cert-manager/acme/profiles/<profile_id>/directory
// Directory (RFC 8555 Section 7.1.1) // Directory (RFC 8555 Section 7.1.1)
server.route({ server.route({
method: "GET", method: "GET",
@@ -99,7 +100,7 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
handler: async (req) => server.services.pkiAcme.getAcmeDirectory(req.params.profileId) handler: async (req) => server.services.pkiAcme.getAcmeDirectory(req.params.profileId)
}); });
// HEAD /api/v1/pki/acme/profiles/<profile_id>/new-nonce // HEAD /api/v1/cert-manager/acme/profiles/<profile_id>/new-nonce
// New Nonce (RFC 8555 Section 7.2) // New Nonce (RFC 8555 Section 7.2)
server.route({ server.route({
method: "HEAD", method: "HEAD",
@@ -126,7 +127,7 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
} }
}); });
// POST /api/v1/pki/acme/profiles/<profile_id>/new-account // POST /api/v1/cert-manager/acme/profiles/<profile_id>/new-account
// New Account (RFC 8555 Section 7.3) // New Account (RFC 8555 Section 7.3)
server.route({ server.route({
method: "POST", method: "POST",
@@ -163,7 +164,7 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
} }
}); });
// POST /api/v1/pki/acme/profiles/<profile_id>/accounts/<account_id> // POST /api/v1/cert-manager/acme/profiles/<profile_id>/accounts/<account_id>
// Account Deactivation (RFC 8555 Section 7.3.6) // Account Deactivation (RFC 8555 Section 7.3.6)
server.route({ server.route({
method: "POST", method: "POST",
@@ -200,7 +201,7 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
} }
}); });
// POST /api/v1/pki/acme/profiles/<profile_id>/new-order // POST /api/v1/cert-manager/acme/profiles/<profile_id>/new-order
// New Certificate Order (RFC 8555 Section 7.4) // New Certificate Order (RFC 8555 Section 7.4)
server.route({ server.route({
method: "POST", method: "POST",
@@ -235,7 +236,7 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
} }
}); });
// POST /api/v1/pki/acme/profiles/<profile_id>/orders/<order_id> // POST /api/v1/cert-manager/acme/profiles/<profile_id>/orders/<order_id>
// Get Order (RFC 8555 Section 7.1.3) // Get Order (RFC 8555 Section 7.1.3)
server.route({ server.route({
method: "POST", method: "POST",
@@ -271,7 +272,7 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
} }
}); });
// POST /api/v1/pki/acme/profiles/<profile_id>/orders/<order_id>/finalize // POST /api/v1/cert-manager/acme/profiles/<profile_id>/orders/<order_id>/finalize
// Applying for Certificate Issuance (RFC 8555 Section 7.4) // Applying for Certificate Issuance (RFC 8555 Section 7.4)
server.route({ server.route({
method: "POST", method: "POST",
@@ -308,7 +309,7 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
); );
} }
}); });
// POST /api/v1/pki/acme/profiles/<profile_id>/accounts/<account_id>/orders // POST /api/v1/cert-manager/acme/profiles/<profile_id>/accounts/<account_id>/orders
// List Orders (RFC 8555 Section 7.1.2.1) // List Orders (RFC 8555 Section 7.1.2.1)
server.route({ server.route({
method: "POST", method: "POST",
@@ -344,7 +345,7 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
} }
}); });
// POST /api/v1/pki/acme/profiles/<profile_id>/orders/<order_id>/certificate // POST /api/v1/cert-manager/acme/profiles/<profile_id>/orders/<order_id>/certificate
// Download Certificate (RFC 8555 Section 7.4.2) // Download Certificate (RFC 8555 Section 7.4.2)
server.route({ server.route({
method: "POST", method: "POST",
@@ -377,7 +378,7 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
} }
}); });
// POST /api/v1/pki/acme/profiles/<profile_id>/authorizations/<authz_id> // POST /api/v1/cert-manager/acme/profiles/<profile_id>/authorizations/<authz_id>
// Identifier Authorization (RFC 8555 Section 7.5) // Identifier Authorization (RFC 8555 Section 7.5)
server.route({ server.route({
method: "POST", method: "POST",
@@ -411,7 +412,7 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
} }
}); });
// POST /api/v1/pki/acme/profiles/<profile_id>/authorizations/<authz_id>/challenges/<challenge_id> // POST /api/v1/cert-manager/acme/profiles/<profile_id>/authorizations/<authz_id>/challenges/<challenge_id>
// Respond to Challenge (RFC 8555 Section 7.5.1) // Respond to Challenge (RFC 8555 Section 7.5.1)
server.route({ server.route({
method: "POST", method: "POST",
@@ -8,7 +8,7 @@ import { AcmeAccountDoesNotExistError } from "./pki-acme-errors";
export const buildUrl = (profileId: string, path: string): string => { export const buildUrl = (profileId: string, path: string): string => {
const appCfg = getConfig(); const appCfg = getConfig();
const baseUrl = appCfg.SITE_URL ?? ""; const baseUrl = appCfg.SITE_URL ?? "";
return `${baseUrl}/api/v1/pki/acme/profiles/${profileId}${path}`; return `${baseUrl}/api/v1/cert-manager/acme/profiles/${profileId}${path}`;
}; };
export const extractAccountIdFromKid = (kid: string, profileId: string): string => { export const extractAccountIdFromKid = (kid: string, profileId: string): string => {
+4
View File
@@ -1962,9 +1962,11 @@ export const CERTIFICATE_AUTHORITIES = {
export const CERTIFICATES = { export const CERTIFICATES = {
GET: { GET: {
id: "The ID of the certificate to get.",
serialNumber: "The serial number of the certificate to get." serialNumber: "The serial number of the certificate to get."
}, },
REVOKE: { REVOKE: {
id: "The ID of the certificate to revoke.",
serialNumber: serialNumber:
"The serial number of the certificate to revoke. The revoked certificate will be added to the certificate revocation list (CRL) of the CA.", "The serial number of the certificate to revoke. The revoked certificate will be added to the certificate revocation list (CRL) of the CA.",
revocationReason: "The reason for revoking the certificate.", revocationReason: "The reason for revoking the certificate.",
@@ -1972,9 +1974,11 @@ export const CERTIFICATES = {
serialNumberRes: "The serial number of the revoked certificate." serialNumberRes: "The serial number of the revoked certificate."
}, },
DELETE: { DELETE: {
id: "The ID of the certificate to delete.",
serialNumber: "The serial number of the certificate to delete." serialNumber: "The serial number of the certificate to delete."
}, },
GET_CERT: { GET_CERT: {
id: "The ID of the certificate to get the certificate body and certificate chain for.",
serialNumber: "The serial number of the certificate to get the certificate body and certificate chain for.", serialNumber: "The serial number of the certificate to get the certificate body and certificate chain for.",
certificate: "The certificate body of the certificate.", certificate: "The certificate body of the certificate.",
certificateChain: "The certificate chain of the certificate.", certificateChain: "The certificate chain of the certificate.",
@@ -6,7 +6,7 @@ import { DefaultResponseErrorsSchema } from "../routes/sanitizedSchemas";
const isScimRoutes = (pathname: string) => const isScimRoutes = (pathname: string) =>
pathname.startsWith("/api/v1/scim/Users") || pathname.startsWith("/api/v1/scim/Groups"); pathname.startsWith("/api/v1/scim/Users") || pathname.startsWith("/api/v1/scim/Groups");
const isAcmeRoutes = (pathname: string) => pathname.startsWith("/api/v1/pki/acme/"); const isAcmeRoutes = (pathname: string) => pathname.startsWith("/api/v1/cert-manager/acme/");
export const addErrorsToResponseSchemas = fp(async (server) => { export const addErrorsToResponseSchemas = fp(async (server) => {
server.addHook("onRoute", (routeOptions) => { server.addHook("onRoute", (routeOptions) => {
@@ -85,7 +85,6 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
isInternal: false, isInternal: false,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
enableDirectIssuance: !req.body.requireTemplateForIssuance,
...req.body ...req.body
}); });
@@ -220,7 +219,6 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
isInternal: false, isInternal: false,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
enableDirectIssuance: !req.body.requireTemplateForIssuance,
...req.body ...req.body
}); });
@@ -617,6 +615,7 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
} }
}); });
// TODO: DEPRECATE
server.route({ server.route({
method: "POST", method: "POST",
url: "/:caId/issue-certificate", url: "/:caId/issue-certificate",
@@ -625,7 +624,6 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities], tags: [ApiDocsTags.PkiCertificateAuthorities],
description: "Issue certificate from CA", description: "Issue certificate from CA",
params: z.object({ params: z.object({
@@ -711,6 +709,7 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
} }
}); });
// TODO: DEPRECATE
server.route({ server.route({
method: "POST", method: "POST",
url: "/:caId/sign-certificate", url: "/:caId/sign-certificate",
@@ -719,7 +718,6 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities], tags: [ApiDocsTags.PkiCertificateAuthorities],
description: "Sign certificate from CA", description: "Sign certificate from CA",
params: z.object({ params: z.object({
@@ -805,6 +803,7 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
} }
}); });
// TODO: DEPRECATE
server.route({ server.route({
method: "GET", method: "GET",
url: "/:caId/certificate-templates", url: "/:caId/certificate-templates",
@@ -813,7 +812,6 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities], tags: [ApiDocsTags.PkiCertificateAuthorities],
description: "Get list of certificate templates for the CA", description: "Get list of certificate templates for the CA",
params: z.object({ params: z.object({
@@ -854,6 +852,7 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
} }
}); });
// TODO: DEPRECATE
server.route({ server.route({
method: "GET", method: "GET",
url: "/:caId/crls", url: "/:caId/crls",
@@ -862,7 +861,6 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities], tags: [ApiDocsTags.PkiCertificateAuthorities],
description: "Get list of CRLs of the CA", description: "Get list of CRLs of the CA",
params: z.object({ params: z.object({
@@ -28,14 +28,10 @@ export const registerCertificateAuthorityEndpoints = <
projectId: string; projectId: string;
status: CaStatus; status: CaStatus;
configuration: I["configuration"]; configuration: I["configuration"];
enableDirectIssuance: boolean;
}>; }>;
updateSchema: z.ZodType<{ updateSchema: z.ZodType<{
projectId: string;
name?: string;
status?: CaStatus; status?: CaStatus;
configuration?: I["configuration"]; configuration?: I["configuration"];
enableDirectIssuance?: boolean;
}>; }>;
responseSchema: z.ZodTypeAny; responseSchema: z.ZodTypeAny;
}) => { }) => {
@@ -83,7 +79,7 @@ export const registerCertificateAuthorityEndpoints = <
server.route({ server.route({
method: "GET", method: "GET",
url: "/:caName", url: "/:id",
config: { config: {
rateLimit: readLimit rateLimit: readLimit
}, },
@@ -91,10 +87,7 @@ export const registerCertificateAuthorityEndpoints = <
hide: false, hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities], tags: [ApiDocsTags.PkiCertificateAuthorities],
params: z.object({ params: z.object({
caName: z.string() id: z.string()
}),
querystring: z.object({
projectId: z.string().uuid()
}), }),
response: { response: {
200: responseSchema 200: responseSchema
@@ -102,12 +95,10 @@ export const registerCertificateAuthorityEndpoints = <
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const { caName } = req.params; const { id } = req.params;
const { projectId } = req.query;
const certificateAuthority = const certificateAuthority = (await server.services.certificateAuthority.findCertificateAuthorityById(
(await server.services.certificateAuthority.findCertificateAuthorityByNameAndProjectId( { id, type: caType },
{ caName, type: caType, projectId },
req.permission req.permission
)) as T; )) as T;
@@ -166,7 +157,7 @@ export const registerCertificateAuthorityEndpoints = <
server.route({ server.route({
method: "PATCH", method: "PATCH",
url: "/:caName", url: "/:id",
config: { config: {
rateLimit: writeLimit rateLimit: writeLimit
}, },
@@ -174,7 +165,7 @@ export const registerCertificateAuthorityEndpoints = <
hide: false, hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities], tags: [ApiDocsTags.PkiCertificateAuthorities],
params: z.object({ params: z.object({
caName: z.string() id: z.string()
}), }),
body: updateSchema, body: updateSchema,
response: { response: {
@@ -183,13 +174,13 @@ export const registerCertificateAuthorityEndpoints = <
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const { caName } = req.params; const { id } = req.params;
const certificateAuthority = (await server.services.certificateAuthority.updateCertificateAuthority( const certificateAuthority = (await server.services.certificateAuthority.updateCertificateAuthority(
{ {
...req.body, ...req.body,
type: caType, type: caType,
caName id
}, },
req.permission req.permission
)) as T; )) as T;
@@ -213,7 +204,7 @@ export const registerCertificateAuthorityEndpoints = <
server.route({ server.route({
method: "DELETE", method: "DELETE",
url: "/:caName", url: "/:id",
config: { config: {
rateLimit: writeLimit rateLimit: writeLimit
}, },
@@ -221,10 +212,7 @@ export const registerCertificateAuthorityEndpoints = <
hide: false, hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities], tags: [ApiDocsTags.PkiCertificateAuthorities],
params: z.object({ params: z.object({
caName: z.string() id: z.string()
}),
body: z.object({
projectId: z.string().uuid()
}), }),
response: { response: {
200: responseSchema 200: responseSchema
@@ -232,11 +220,10 @@ export const registerCertificateAuthorityEndpoints = <
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const { caName } = req.params; const { id } = req.params;
const { projectId } = req.body;
const certificateAuthority = (await server.services.certificateAuthority.deleteCertificateAuthority( const certificateAuthority = (await server.services.certificateAuthority.deleteCertificateAuthority(
{ caName, type: caType, projectId }, { id, type: caType },
req.permission req.permission
)) as T; )) as T;
@@ -0,0 +1,85 @@
import { z } from "zod";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ApiDocsTags } from "@app/lib/api-docs";
import { readLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { AcmeCertificateAuthoritySchema } from "@app/services/certificate-authority/acme/acme-certificate-authority-schemas";
import { AzureAdCsCertificateAuthoritySchema } from "@app/services/certificate-authority/azure-ad-cs/azure-ad-cs-certificate-authority-schemas";
import { CaType } from "@app/services/certificate-authority/certificate-authority-enums";
import { InternalCertificateAuthoritySchema } from "@app/services/certificate-authority/internal/internal-certificate-authority-schemas";
const CertificateAuthoritySchema = z.discriminatedUnion("type", [
InternalCertificateAuthoritySchema,
AcmeCertificateAuthoritySchema,
AzureAdCsCertificateAuthoritySchema
]);
export const registerGeneralCertificateAuthorityRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities],
description: "Get Certificate Authorities",
querystring: z.object({
projectId: z.string()
}),
response: {
200: z.object({
certificateAuthorities: CertificateAuthoritySchema.array()
})
}
},
handler: async (req) => {
const internalCas = await server.services.certificateAuthority.listCertificateAuthoritiesByProjectId(
{
projectId: req.query.projectId,
type: CaType.INTERNAL
},
req.permission
);
const acmeCas = await server.services.certificateAuthority.listCertificateAuthoritiesByProjectId(
{
projectId: req.query.projectId,
type: CaType.ACME
},
req.permission
);
const azureAdCsCas = await server.services.certificateAuthority.listCertificateAuthoritiesByProjectId(
{
projectId: req.query.projectId,
type: CaType.AZURE_AD_CS
},
req.permission
);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: req.query.projectId,
event: {
type: EventType.GET_CAS,
metadata: {
caIds: [
...(internalCas ?? []).map((ca) => ca.id),
...(acmeCas ?? []).map((ca) => ca.id),
...(azureAdCsCas ?? []).map((ca) => ca.id)
]
}
}
});
return {
certificateAuthorities: [...(internalCas ?? []), ...(acmeCas ?? []), ...(azureAdCsCas ?? [])]
};
}
});
};
@@ -1,4 +1,12 @@
import { CaType } from "@app/services/certificate-authority/certificate-authority-enums"; import { z } from "zod";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ApiDocsTags, CERTIFICATE_AUTHORITIES } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { CaRenewalType, CaType } from "@app/services/certificate-authority/certificate-authority-enums";
import { validateCaDateField } from "@app/services/certificate-authority/certificate-authority-validators";
import { import {
CreateInternalCertificateAuthoritySchema, CreateInternalCertificateAuthoritySchema,
InternalCertificateAuthoritySchema, InternalCertificateAuthoritySchema,
@@ -15,4 +23,406 @@ export const registerInternalCertificateAuthorityRouter = async (server: Fastify
createSchema: CreateInternalCertificateAuthoritySchema, createSchema: CreateInternalCertificateAuthoritySchema,
updateSchema: UpdateInternalCertificateAuthoritySchema updateSchema: UpdateInternalCertificateAuthoritySchema
}); });
server.route({
method: "GET",
url: "/:caId/csr",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities],
description: "Get CA CSR",
params: z.object({
caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.GET_CSR.caId)
}),
response: {
200: z.object({
csr: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CSR.csr)
})
}
},
handler: async (req) => {
const { ca, csr } = await server.services.internalCertificateAuthority.getCaCsr({
caId: req.params.caId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: ca.projectId,
event: {
type: EventType.GET_CA_CSR,
metadata: {
caId: ca.id,
dn: ca.dn
}
}
});
return {
csr
};
}
});
server.route({
method: "POST",
url: "/:caId/renew",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities],
description: "Perform CA certificate renewal",
params: z.object({
caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.caId)
}),
body: z.object({
type: z.nativeEnum(CaRenewalType).describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.type),
notAfter: validateCaDateField.describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.notAfter)
}),
response: {
200: z.object({
certificate: z.string().trim().describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.certificate),
certificateChain: z.string().trim().describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.certificateChain),
serialNumber: z.string().trim().describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.serialNumber)
})
}
},
handler: async (req) => {
const { certificate, certificateChain, serialNumber, ca } =
await server.services.internalCertificateAuthority.renewCaCert({
caId: req.params.caId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: ca.projectId,
event: {
type: EventType.RENEW_CA,
metadata: {
caId: ca.id,
dn: ca.dn
}
}
});
return {
certificate,
certificateChain,
serialNumber
};
}
});
server.route({
method: "GET",
url: "/:caId/ca-certificates",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities],
description: "Get list of past and current CA certificates for a CA",
params: z.object({
caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.GET_CA_CERTS.caId)
}),
response: {
200: z.array(
z.object({
certificate: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CA_CERTS.certificate),
certificateChain: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CA_CERTS.certificateChain),
serialNumber: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CA_CERTS.serialNumber),
version: z.number().describe(CERTIFICATE_AUTHORITIES.GET_CA_CERTS.version)
})
)
}
},
handler: async (req) => {
const { caCerts, ca } = await server.services.internalCertificateAuthority.getCaCerts({
caId: req.params.caId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: ca.projectId,
event: {
type: EventType.GET_CA_CERTS,
metadata: {
caId: ca.id,
dn: ca.dn
}
}
});
return caCerts;
}
});
server.route({
method: "GET",
url: "/:caId/certificate",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities],
description: "Get current CA cert and cert chain of a CA",
params: z.object({
caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.GET_CERT.caId)
}),
response: {
200: z.object({
certificate: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CERT.certificate),
certificateChain: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CERT.certificateChain),
serialNumber: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CERT.serialNumber)
})
}
},
handler: async (req) => {
const { certificate, certificateChain, serialNumber, ca } =
await server.services.internalCertificateAuthority.getCaCert({
caId: req.params.caId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: ca.projectId,
event: {
type: EventType.GET_CA_CERT,
metadata: {
caId: ca.id,
dn: ca.dn
}
}
});
return {
certificate,
certificateChain,
serialNumber
};
}
});
server.route({
method: "POST",
url: "/:caId/sign-intermediate",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities],
description: "Create intermediate CA certificate from parent CA",
params: z.object({
caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.SIGN_INTERMEDIATE.caId)
}),
body: z.object({
csr: z.string().trim().min(1).describe(CERTIFICATE_AUTHORITIES.SIGN_INTERMEDIATE.csr),
notBefore: validateCaDateField.optional().describe(CERTIFICATE_AUTHORITIES.SIGN_INTERMEDIATE.notBefore),
notAfter: validateCaDateField.describe(CERTIFICATE_AUTHORITIES.SIGN_INTERMEDIATE.notAfter),
maxPathLength: z.number().min(-1).default(-1).describe(CERTIFICATE_AUTHORITIES.SIGN_INTERMEDIATE.maxPathLength)
}),
response: {
200: z.object({
certificate: z.string().trim().describe(CERTIFICATE_AUTHORITIES.SIGN_INTERMEDIATE.certificate),
certificateChain: z.string().trim().describe(CERTIFICATE_AUTHORITIES.SIGN_INTERMEDIATE.certificateChain),
issuingCaCertificate: z
.string()
.trim()
.describe(CERTIFICATE_AUTHORITIES.SIGN_INTERMEDIATE.issuingCaCertificate),
serialNumber: z.string().trim().describe(CERTIFICATE_AUTHORITIES.SIGN_INTERMEDIATE.serialNumber)
})
}
},
handler: async (req) => {
const { certificate, certificateChain, issuingCaCertificate, serialNumber, ca } =
await server.services.internalCertificateAuthority.signIntermediate({
caId: req.params.caId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: ca.projectId,
event: {
type: EventType.SIGN_INTERMEDIATE,
metadata: {
caId: ca.id,
dn: ca.dn,
serialNumber
}
}
});
return {
certificate,
certificateChain,
issuingCaCertificate,
serialNumber
};
}
});
server.route({
method: "POST",
url: "/:caId/import-certificate",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities],
description: "Import certificate and chain to CA",
params: z.object({
caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.IMPORT_CERT.caId)
}),
body: z.object({
certificate: z.string().trim().describe(CERTIFICATE_AUTHORITIES.IMPORT_CERT.certificate),
certificateChain: z.string().trim().describe(CERTIFICATE_AUTHORITIES.IMPORT_CERT.certificateChain)
}),
response: {
200: z.object({
message: z.string().trim(),
caId: z.string().trim()
})
}
},
handler: async (req) => {
const { ca } = await server.services.internalCertificateAuthority.importCertToCa({
caId: req.params.caId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: ca.projectId,
event: {
type: EventType.IMPORT_CA_CERT,
metadata: {
caId: ca.id,
dn: ca.dn
}
}
});
return {
message: "Successfully imported certificate to CA",
caId: req.params.caId
};
}
});
server.route({
method: "GET",
url: "/:caId/crls",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities],
description: "Get list of CRLs of the CA",
params: z.object({
caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.GET_CRLS.caId)
}),
response: {
200: z.array(
z.object({
id: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CRLS.id),
crl: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CRLS.crl)
})
)
}
},
handler: async (req) => {
const { ca, crls } = await server.services.certificateAuthorityCrl.getCaCrls({
caId: req.params.caId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: ca.projectId,
event: {
type: EventType.GET_CA_CRLS,
metadata: {
caId: ca.id,
dn: ca.dn
}
}
});
return crls;
}
});
// this endpoint will be used to serve the CA certificate when a client makes a request
// against the Authority Information Access CA Issuer URL
server.route({
method: "GET",
url: "/:caId/certificates/:caCertId/der",
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities],
description: "Get DER-encoded certificate of CA",
params: z.object({
caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.GET_CERT_BY_ID.caId),
caCertId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.GET_CERT_BY_ID.caCertId)
}),
response: {
200: z.instanceof(Buffer)
}
},
handler: async (req, res) => {
const caCert = await server.services.internalCertificateAuthority.getCaCertById(req.params);
void res.header("Content-Type", "application/pkix-cert");
return Buffer.from(caCert.rawData);
}
});
}; };
+517 -261
View File
@@ -4,24 +4,510 @@ import { z } from "zod";
import { CertificatesSchema } from "@app/db/schemas"; import { CertificatesSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ApiDocsTags, CERTIFICATE_AUTHORITIES, CERTIFICATES } from "@app/lib/api-docs"; import { ApiDocsTags, CERTIFICATES } from "@app/lib/api-docs";
import { ms } from "@app/lib/ms"; import { ms } from "@app/lib/ms";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { addNoCacheHeaders } from "@app/server/lib/caching"; import { addNoCacheHeaders } from "@app/server/lib/caching";
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { CertExtendedKeyUsage, CertKeyUsage, CrlReason } from "@app/services/certificate/certificate-types";
import { import {
validateAltNamesField, ACMESANType,
validateCaDateField CertificateOrderStatus,
} from "@app/services/certificate-authority/certificate-authority-validators"; CertKeyAlgorithm,
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; CertSignatureAlgorithm,
CrlReason
} from "@app/services/certificate/certificate-types";
import { validateCaDateField } from "@app/services/certificate-authority/certificate-authority-validators";
import {
CertExtendedKeyUsageType,
CertKeyUsageType,
CertSubjectAlternativeNameType
} from "@app/services/certificate-common/certificate-constants";
import { extractCertificateRequestFromCSR } from "@app/services/certificate-common/certificate-csr-utils";
import { mapEnumsForValidation } from "@app/services/certificate-common/certificate-utils";
import { EnrollmentType } from "@app/services/certificate-profile/certificate-profile-types";
import { validateTemplateRegexField } from "@app/services/certificate-template/certificate-template-validators";
import { booleanSchema } from "../sanitizedSchemas";
interface CertificateRequestForService {
commonName?: string;
keyUsages?: CertKeyUsageType[];
extendedKeyUsages?: CertExtendedKeyUsageType[];
altNames?: Array<{
type: CertSubjectAlternativeNameType;
value: string;
}>;
validity: {
ttl: string;
};
notBefore?: Date;
notAfter?: Date;
signatureAlgorithm?: string;
keyAlgorithm?: string;
}
const validateTtlAndDateFields = (data: { notBefore?: string; notAfter?: string; ttl?: string }) => {
const hasDateFields = data.notBefore || data.notAfter;
const hasTtl = data.ttl;
return !(hasDateFields && hasTtl);
};
const validateDateOrder = (data: { notBefore?: string; notAfter?: string }) => {
if (data.notBefore && data.notAfter) {
const notBefore = new Date(data.notBefore);
const notAfter = new Date(data.notAfter);
return notBefore < notAfter;
}
return true;
};
export const registerCertificateRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/issue-certificate",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificates],
body: z
.object({
profileId: z.string().uuid(),
commonName: validateTemplateRegexField.optional(),
ttl: z
.string()
.trim()
.min(1, "TTL cannot be empty")
.refine((val) => ms(val) > 0, "TTL must be a positive number"),
keyUsages: z.nativeEnum(CertKeyUsageType).array().optional(),
extendedKeyUsages: z.nativeEnum(CertExtendedKeyUsageType).array().optional(),
notBefore: validateCaDateField.optional(),
notAfter: validateCaDateField.optional(),
altNames: z
.array(
z.object({
type: z.nativeEnum(CertSubjectAlternativeNameType),
value: z.string().min(1, "SAN value cannot be empty")
})
)
.optional(),
signatureAlgorithm: z.nativeEnum(CertSignatureAlgorithm),
keyAlgorithm: z.nativeEnum(CertKeyAlgorithm),
removeRootsFromChain: booleanSchema.default(false).optional()
})
.refine(validateTtlAndDateFields, {
message:
"Cannot specify both TTL and notBefore/notAfter. Use either TTL for duration-based validity or notBefore/notAfter for explicit date range."
})
.refine(validateDateOrder, {
message: "notBefore must be earlier than notAfter"
}),
response: {
200: z.object({
certificate: z.string().trim(),
issuingCaCertificate: z.string().trim(),
certificateChain: z.string().trim(),
privateKey: z.string().trim().optional(),
serialNumber: z.string().trim(),
certificateId: z.string()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const certificateRequestForService: CertificateRequestForService = {
commonName: req.body.commonName,
keyUsages: req.body.keyUsages,
extendedKeyUsages: req.body.extendedKeyUsages,
altNames: req.body.altNames,
validity: {
ttl: req.body.ttl
},
notBefore: req.body.notBefore ? new Date(req.body.notBefore) : undefined,
notAfter: req.body.notAfter ? new Date(req.body.notAfter) : undefined,
signatureAlgorithm: req.body.signatureAlgorithm,
keyAlgorithm: req.body.keyAlgorithm
};
const mappedCertificateRequest = mapEnumsForValidation(certificateRequestForService);
const data = await server.services.certificateV3.issueCertificateFromProfile({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
profileId: req.body.profileId,
certificateRequest: mappedCertificateRequest,
removeRootsFromChain: req.body.removeRootsFromChain
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: data.projectId,
event: {
type: EventType.ISSUE_CERTIFICATE_FROM_PROFILE,
metadata: {
certificateProfileId: req.body.profileId,
certificateId: data.certificateId,
commonName: req.body.commonName || "",
profileName: data.profileName
}
}
});
return data;
}
});
server.route({
method: "POST",
url: "/sign-certificate",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificates],
body: z
.object({
profileId: z.string().uuid(),
csr: z.string().trim().min(1, "CSR cannot be empty").max(4096, "CSR cannot exceed 4096 characters"),
ttl: z
.string()
.trim()
.min(1, "TTL cannot be empty")
.refine((val) => ms(val) > 0, "TTL must be a positive number"),
notBefore: validateCaDateField.optional(),
notAfter: validateCaDateField.optional(),
removeRootsFromChain: booleanSchema.default(false).optional()
})
.refine(validateTtlAndDateFields, {
message:
"Cannot specify both TTL and notBefore/notAfter. Use either TTL for duration-based validity or notBefore/notAfter for explicit date range."
})
.refine(validateDateOrder, {
message: "notBefore must be earlier than notAfter"
}),
response: {
200: z.object({
certificate: z.string().trim(),
issuingCaCertificate: z.string().trim(),
certificateChain: z.string().trim(),
serialNumber: z.string().trim(),
certificateId: z.string()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const certificateRequest = extractCertificateRequestFromCSR(req.body.csr);
const data = await server.services.certificateV3.signCertificateFromProfile({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
profileId: req.body.profileId,
csr: req.body.csr,
validity: {
ttl: req.body.ttl
},
notBefore: req.body.notBefore ? new Date(req.body.notBefore) : undefined,
notAfter: req.body.notAfter ? new Date(req.body.notAfter) : undefined,
enrollmentType: EnrollmentType.API,
removeRootsFromChain: req.body.removeRootsFromChain
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: data.projectId,
event: {
type: EventType.SIGN_CERTIFICATE_FROM_PROFILE,
metadata: {
certificateProfileId: req.body.profileId,
certificateId: data.certificateId,
profileName: data.profileName,
commonName: certificateRequest.commonName || ""
}
}
});
return data;
}
});
server.route({
method: "POST",
url: "/order-certificate",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificates],
body: z
.object({
profileId: z.string().uuid(),
subjectAlternativeNames: z
.array(
z.object({
type: z.nativeEnum(ACMESANType),
value: z
.string()
.trim()
.min(1, "SAN value cannot be empty")
.max(255, "SAN value must be less than 255 characters")
})
)
.min(1, "At least one subject alternative name must be provided"),
ttl: z
.string()
.trim()
.min(1, "TTL cannot be empty")
.refine((val) => ms(val) > 0, "TTL must be a positive number"),
keyUsages: z.nativeEnum(CertKeyUsageType).array().optional(),
extendedKeyUsages: z.nativeEnum(CertExtendedKeyUsageType).array().optional(),
notBefore: validateCaDateField.optional(),
notAfter: validateCaDateField.optional(),
commonName: validateTemplateRegexField.optional(),
signatureAlgorithm: z.nativeEnum(CertSignatureAlgorithm),
keyAlgorithm: z.nativeEnum(CertKeyAlgorithm),
removeRootsFromChain: booleanSchema.default(false).optional()
})
.refine(validateTtlAndDateFields, {
message:
"Cannot specify both TTL and notBefore/notAfter. Use either TTL for duration-based validity or notBefore/notAfter for explicit date range."
})
.refine(validateDateOrder, {
message: "notBefore must be earlier than notAfter"
}),
response: {
200: z.object({
orderId: z.string(),
status: z.nativeEnum(CertificateOrderStatus),
subjectAlternativeNames: z.array(
z.object({
type: z.nativeEnum(ACMESANType),
value: z.string(),
status: z.nativeEnum(CertificateOrderStatus)
})
),
authorizations: z.array(
z.object({
identifier: z.object({
type: z.nativeEnum(ACMESANType),
value: z.string()
}),
status: z.nativeEnum(CertificateOrderStatus),
expires: z.string().optional(),
challenges: z.array(
z.object({
type: z.string(),
status: z.nativeEnum(CertificateOrderStatus),
url: z.string(),
token: z.string()
})
)
})
),
finalize: z.string(),
certificate: z.string().optional()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const data = await server.services.certificateV3.orderCertificateFromProfile({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
profileId: req.body.profileId,
certificateOrder: {
altNames: req.body.subjectAlternativeNames,
validity: {
ttl: req.body.ttl
},
commonName: req.body.commonName,
keyUsages: req.body.keyUsages,
extendedKeyUsages: req.body.extendedKeyUsages,
notBefore: req.body.notBefore ? new Date(req.body.notBefore) : undefined,
notAfter: req.body.notAfter ? new Date(req.body.notAfter) : undefined,
signatureAlgorithm: req.body.signatureAlgorithm,
keyAlgorithm: req.body.keyAlgorithm
},
removeRootsFromChain: req.body.removeRootsFromChain
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: data.projectId,
event: {
type: EventType.ORDER_CERTIFICATE_FROM_PROFILE,
metadata: {
certificateProfileId: req.body.profileId,
orderId: data.orderId,
profileName: data.profileName
}
}
});
return data;
}
});
server.route({
method: "POST",
url: "/:certificateId/renew",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificates],
params: z.object({
certificateId: z.string().uuid()
}),
body: z
.object({
removeRootsFromChain: booleanSchema.default(false).optional()
})
.optional(),
response: {
200: z.object({
certificate: z.string().trim(),
issuingCaCertificate: z.string().trim(),
certificateChain: z.string().trim(),
privateKey: z.string().trim().optional(),
serialNumber: z.string().trim(),
certificateId: z.string()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const data = await server.services.certificateV3.renewCertificate({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
certificateId: req.params.certificateId,
removeRootsFromChain: req.body?.removeRootsFromChain
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: data.projectId,
event: {
type: EventType.RENEW_CERTIFICATE,
metadata: {
originalCertificateId: req.params.certificateId,
newCertificateId: data.certificateId,
profileName: data.profileName,
commonName: data.commonName
}
}
});
return data;
}
});
server.route({
method: "PATCH",
url: "/:certificateId/config",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificates],
params: z.object({
certificateId: z.string().uuid()
}),
body: z
.object({
renewBeforeDays: z.number().int().min(1).max(30).optional(),
enableAutoRenewal: z.boolean().optional()
})
.refine((data) => !(data.renewBeforeDays !== undefined && data.enableAutoRenewal === false), {
message: "Cannot specify both renewBeforeDays and enableAutoRenewal=false"
}),
response: {
200: z.object({
message: z.string(),
renewBeforeDays: z.number().optional()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
if (req.body.enableAutoRenewal === false) {
const data = await server.services.certificateV3.disableRenewalConfig({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
certificateId: req.params.certificateId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: data.projectId,
event: {
type: EventType.DISABLE_CERTIFICATE_RENEWAL_CONFIG,
metadata: {
certificateId: req.params.certificateId,
commonName: data.commonName
}
}
});
return {
message: "Auto-renewal disabled successfully"
};
}
if (req.body.renewBeforeDays !== undefined) {
const data = await server.services.certificateV3.updateRenewalConfig({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
certificateId: req.params.certificateId,
renewBeforeDays: req.body.renewBeforeDays
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: data.projectId,
event: {
type: EventType.UPDATE_CERTIFICATE_RENEWAL_CONFIG,
metadata: {
certificateId: req.params.certificateId,
renewBeforeDays: req.body.renewBeforeDays.toString(),
commonName: data.commonName
}
}
});
return {
message: "Certificate configuration updated successfully",
renewBeforeDays: data.renewBeforeDays
};
}
return {
message: "No configuration changes requested"
};
}
});
export const registerCertRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
method: "GET", method: "GET",
url: "/:serialNumber", url: "/:id",
config: { config: {
rateLimit: readLimit rateLimit: readLimit
}, },
@@ -31,7 +517,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
tags: [ApiDocsTags.PkiCertificates], tags: [ApiDocsTags.PkiCertificates],
description: "Get certificate", description: "Get certificate",
params: z.object({ params: z.object({
serialNumber: z.string().trim().describe(CERTIFICATES.GET.serialNumber) id: z.string().trim().describe(CERTIFICATES.GET.id)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -41,7 +527,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
}, },
handler: async (req) => { handler: async (req) => {
const { cert } = await server.services.certificate.getCert({ const { cert } = await server.services.certificate.getCert({
serialNumber: req.params.serialNumber, id: req.params.id,
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
@@ -67,10 +553,9 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
} }
}); });
// TODO: In the future add support for other formats outside of PEM (such as DER). Adding a "format" query param may be best.
server.route({ server.route({
method: "GET", method: "GET",
url: "/:serialNumber/private-key", url: "/:id/private-key",
config: { config: {
rateLimit: readLimit rateLimit: readLimit
}, },
@@ -80,7 +565,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
tags: [ApiDocsTags.PkiCertificates], tags: [ApiDocsTags.PkiCertificates],
description: "Get certificate private key", description: "Get certificate private key",
params: z.object({ params: z.object({
serialNumber: z.string().trim().describe(CERTIFICATES.GET.serialNumber) id: z.string().trim().describe(CERTIFICATES.GET.id)
}), }),
response: { response: {
200: z.string().trim() 200: z.string().trim()
@@ -88,7 +573,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
}, },
handler: async (req, reply) => { handler: async (req, reply) => {
const { cert, certPrivateKey } = await server.services.certificate.getCertPrivateKey({ const { cert, certPrivateKey } = await server.services.certificate.getCertPrivateKey({
serialNumber: req.params.serialNumber, id: req.params.id,
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
@@ -114,10 +599,9 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
} }
}); });
// TODO: In the future add support for other formats outside of PEM (such as DER). Adding a "format" query param may be best.
server.route({ server.route({
method: "GET", method: "GET",
url: "/:serialNumber/bundle", url: "/:id/bundle",
config: { config: {
rateLimit: readLimit rateLimit: readLimit
}, },
@@ -127,7 +611,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
tags: [ApiDocsTags.PkiCertificates], tags: [ApiDocsTags.PkiCertificates],
description: "Get certificate bundle including the certificate, chain, and private key.", description: "Get certificate bundle including the certificate, chain, and private key.",
params: z.object({ params: z.object({
serialNumber: z.string().trim().describe(CERTIFICATES.GET_CERT.serialNumber) id: z.string().trim().describe(CERTIFICATES.GET_CERT.id)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -141,7 +625,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
handler: async (req, reply) => { handler: async (req, reply) => {
const { certificate, certificateChain, serialNumber, cert, privateKey } = const { certificate, certificateChain, serialNumber, cert, privateKey } =
await server.services.certificate.getCertBundle({ await server.services.certificate.getCertBundle({
serialNumber: req.params.serialNumber, id: req.params.id,
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
@@ -172,120 +656,6 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
} }
}); });
server.route({
method: "POST",
url: "/issue-certificate",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificates],
description: "Issue certificate",
body: z
.object({
caId: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.caId),
certificateTemplateId: z
.string()
.trim()
.optional()
.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.certificateTemplateId),
pkiCollectionId: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.pkiCollectionId),
friendlyName: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.friendlyName),
commonName: z.string().trim().min(1).describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.commonName),
altNames: validateAltNamesField.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.altNames),
ttl: z
.string()
.refine((val) => ms(val) > 0, "TTL must be a positive number")
.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.ttl),
notBefore: validateCaDateField.optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.notBefore),
notAfter: validateCaDateField.optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.notAfter),
keyUsages: z
.nativeEnum(CertKeyUsage)
.array()
.optional()
.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.keyUsages),
extendedKeyUsages: z
.nativeEnum(CertExtendedKeyUsage)
.array()
.optional()
.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.extendedKeyUsages)
})
.refine(
(data) => {
const { ttl, notAfter } = data;
return (ttl !== undefined && notAfter === undefined) || (ttl === undefined && notAfter !== undefined);
},
{
message: "Either ttl or notAfter must be present, but not both",
path: ["ttl", "notAfter"]
}
)
.refine(
(data) =>
(data.caId !== undefined && data.certificateTemplateId === undefined) ||
(data.caId === undefined && data.certificateTemplateId !== undefined),
{
message: "Either CA ID or Certificate Template ID must be present, but not both",
path: ["caId", "certificateTemplateId"]
}
),
response: {
200: z.object({
certificate: z.string().trim().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.certificate),
issuingCaCertificate: z.string().trim().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.issuingCaCertificate),
certificateChain: z.string().trim().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.certificateChain),
privateKey: z.string().trim().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.privateKey),
serialNumber: z.string().trim().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.serialNumber)
})
}
},
handler: async (req) => {
const { certificate, certificateChain, issuingCaCertificate, privateKey, serialNumber, ca } =
await server.services.internalCertificateAuthority.issueCertFromCa({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: ca.projectId,
event: {
type: EventType.ISSUE_CERT,
metadata: {
caId: ca.id,
dn: ca.dn,
serialNumber
}
}
});
await server.services.telemetry.sendPostHogEvents({
event: PostHogEventTypes.IssueCert,
distinctId: getTelemetryDistinctId(req),
organizationId: req.permission.orgId,
properties: {
caId: req.body.caId,
certificateTemplateId: req.body.certificateTemplateId,
commonName: req.body.commonName,
...req.auditLogInfo
}
});
return {
certificate,
certificateChain,
issuingCaCertificate,
privateKey,
serialNumber
};
}
});
server.route({ server.route({
method: "POST", method: "POST",
url: "/import-certificate", url: "/import-certificate",
@@ -350,121 +720,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
method: "POST", method: "POST",
url: "/sign-certificate", url: "/:id/revoke",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificates],
description: "Sign certificate",
body: z
.object({
caId: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.caId),
certificateTemplateId: z
.string()
.trim()
.optional()
.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.certificateTemplateId),
pkiCollectionId: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.pkiCollectionId),
csr: z.string().trim().min(1).describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.csr),
friendlyName: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.friendlyName),
commonName: z.string().trim().min(1).optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.commonName),
altNames: validateAltNamesField.describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.altNames),
ttl: z
.string()
.refine((val) => ms(val) > 0, "TTL must be a positive number")
.describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.ttl),
notBefore: validateCaDateField.optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.notBefore),
notAfter: validateCaDateField.optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.notAfter),
keyUsages: z
.nativeEnum(CertKeyUsage)
.array()
.optional()
.describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.keyUsages),
extendedKeyUsages: z
.nativeEnum(CertExtendedKeyUsage)
.array()
.optional()
.describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.extendedKeyUsages)
})
.refine(
(data) => {
const { ttl, notAfter } = data;
return (ttl !== undefined && notAfter === undefined) || (ttl === undefined && notAfter !== undefined);
},
{
message: "Either ttl or notAfter must be present, but not both",
path: ["ttl", "notAfter"]
}
)
.refine(
(data) =>
(data.caId !== undefined && data.certificateTemplateId === undefined) ||
(data.caId === undefined && data.certificateTemplateId !== undefined),
{
message: "Either CA ID or Certificate Template ID must be present, but not both",
path: ["caId", "certificateTemplateId"]
}
),
response: {
200: z.object({
certificate: z.string().trim().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.certificate),
issuingCaCertificate: z.string().trim().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.issuingCaCertificate),
certificateChain: z.string().trim().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.certificateChain),
serialNumber: z.string().trim().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.serialNumber)
})
}
},
handler: async (req) => {
const { certificate, certificateChain, issuingCaCertificate, serialNumber, ca, commonName } =
await server.services.internalCertificateAuthority.signCertFromCa({
isInternal: false,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: ca.projectId,
event: {
type: EventType.SIGN_CERT,
metadata: {
caId: ca.id,
dn: ca.dn,
serialNumber
}
}
});
await server.services.telemetry.sendPostHogEvents({
event: PostHogEventTypes.SignCert,
distinctId: getTelemetryDistinctId(req),
organizationId: req.permission.orgId,
properties: {
caId: req.body.caId,
certificateTemplateId: req.body.certificateTemplateId,
commonName,
...req.auditLogInfo
}
});
return {
certificate: certificate.toString("pem"),
certificateChain,
issuingCaCertificate,
serialNumber
};
}
});
server.route({
method: "POST",
url: "/:serialNumber/revoke",
config: { config: {
rateLimit: writeLimit rateLimit: writeLimit
}, },
@@ -474,7 +730,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
tags: [ApiDocsTags.PkiCertificates], tags: [ApiDocsTags.PkiCertificates],
description: "Revoke", description: "Revoke",
params: z.object({ params: z.object({
serialNumber: z.string().trim().describe(CERTIFICATES.REVOKE.serialNumber) id: z.string().trim().describe(CERTIFICATES.REVOKE.id)
}), }),
body: z.object({ body: z.object({
revocationReason: z.nativeEnum(CrlReason).describe(CERTIFICATES.REVOKE.revocationReason) revocationReason: z.nativeEnum(CrlReason).describe(CERTIFICATES.REVOKE.revocationReason)
@@ -489,7 +745,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
}, },
handler: async (req) => { handler: async (req) => {
const { revokedAt, cert, ca } = await server.services.certificate.revokeCert({ const { revokedAt, cert, ca } = await server.services.certificate.revokeCert({
serialNumber: req.params.serialNumber, id: req.params.id,
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
@@ -512,7 +768,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
return { return {
message: "Successfully revoked certificate", message: "Successfully revoked certificate",
serialNumber: req.params.serialNumber, serialNumber: cert.serialNumber,
revokedAt revokedAt
}; };
} }
@@ -520,7 +776,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
method: "DELETE", method: "DELETE",
url: "/:serialNumber", url: "/:id",
config: { config: {
rateLimit: writeLimit rateLimit: writeLimit
}, },
@@ -530,7 +786,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
tags: [ApiDocsTags.PkiCertificates], tags: [ApiDocsTags.PkiCertificates],
description: "Delete certificate", description: "Delete certificate",
params: z.object({ params: z.object({
serialNumber: z.string().trim().describe(CERTIFICATES.DELETE.serialNumber) id: z.string().trim().describe(CERTIFICATES.DELETE.id)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -540,7 +796,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
}, },
handler: async (req) => { handler: async (req) => {
const { deletedCert } = await server.services.certificate.deleteCert({ const { deletedCert } = await server.services.certificate.deleteCert({
serialNumber: req.params.serialNumber, id: req.params.id,
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
@@ -568,7 +824,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
method: "GET", method: "GET",
url: "/:serialNumber/certificate", url: "/:id/certificate",
config: { config: {
rateLimit: readLimit rateLimit: readLimit
}, },
@@ -578,7 +834,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
tags: [ApiDocsTags.PkiCertificates], tags: [ApiDocsTags.PkiCertificates],
description: "Get certificate body of certificate", description: "Get certificate body of certificate",
params: z.object({ params: z.object({
serialNumber: z.string().trim().describe(CERTIFICATES.GET_CERT.serialNumber) id: z.string().trim().describe(CERTIFICATES.GET_CERT.id)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -590,7 +846,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
}, },
handler: async (req) => { handler: async (req) => {
const { certificate, certificateChain, serialNumber, cert } = await server.services.certificate.getCertBody({ const { certificate, certificateChain, serialNumber, cert } = await server.services.certificate.getCertBody({
serialNumber: req.params.serialNumber, id: req.params.id,
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
@@ -620,7 +876,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
method: "POST", method: "POST",
url: "/:serialNumber/pkcs12", url: "/:id/pkcs12",
config: { config: {
rateLimit: writeLimit rateLimit: writeLimit
}, },
@@ -630,7 +886,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
tags: [ApiDocsTags.PkiCertificates], tags: [ApiDocsTags.PkiCertificates],
description: "Download certificate in PKCS12 format", description: "Download certificate in PKCS12 format",
params: z.object({ params: z.object({
serialNumber: z.string().trim().describe(CERTIFICATES.GET.serialNumber) id: z.string().trim().describe(CERTIFICATES.GET.id)
}), }),
body: z.object({ body: z.object({
password: z password: z
@@ -645,7 +901,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
}, },
handler: async (req, reply) => { handler: async (req, reply) => {
const { pkcs12Data, cert } = await server.services.certificate.getCertPkcs12({ const { pkcs12Data, cert } = await server.services.certificate.getCertPkcs12({
serialNumber: req.params.serialNumber, id: req.params.id,
password: req.body.password, password: req.body.password,
alias: req.body.alias, alias: req.body.alias,
actor: req.permission.type, actor: req.permission.type,
@@ -671,7 +927,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
reply.header("Content-Type", "application/octet-stream"); reply.header("Content-Type", "application/octet-stream");
reply.header( reply.header(
"Content-Disposition", "Content-Disposition",
`attachment; filename="certificate-${req.params.serialNumber.replace(new RE2("[^\\w.-]", "g"), "_")}.p12"` `attachment; filename="certificate-${cert.serialNumber?.replace(new RE2("[^\\w.-]", "g"), "_")}.p12"`
); );
return pkcs12Data; return pkcs12Data;
@@ -1,28 +1,239 @@
import RE2 from "re2";
import { z } from "zod"; import { z } from "zod";
import { CertificateTemplateEstConfigsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ApiDocsTags, CERTIFICATE_TEMPLATES } from "@app/lib/api-docs"; import { ApiDocsTags } from "@app/lib/api-docs";
import { ms } from "@app/lib/ms";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { slugSchema } from "@app/server/lib/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { CertExtendedKeyUsage, CertKeyUsage } from "@app/services/certificate/certificate-types"; import {
import { sanitizedCertificateTemplate } from "@app/services/certificate-template/certificate-template-schema"; CertExtendedKeyUsageType,
import { validateTemplateRegexField } from "@app/services/certificate-template/certificate-template-validators"; CertKeyUsageType,
CertSubjectAlternativeNameType,
CertSubjectAttributeType
} from "@app/services/certificate-common/certificate-constants";
import { certificateTemplateV2ResponseSchema } from "@app/services/certificate-template-v2/certificate-template-v2-schemas";
const sanitizedEstConfig = CertificateTemplateEstConfigsSchema.pick({ const attributeTypeSchema = z.nativeEnum(CertSubjectAttributeType);
id: true, const sanTypeSchema = z.nativeEnum(CertSubjectAlternativeNameType);
certificateTemplateId: true,
isEnabled: true, const templateV2SubjectSchema = z
disableBootstrapCertValidation: true .object({
type: attributeTypeSchema,
allowed: z.array(z.string()).optional(),
required: z.array(z.string()).optional(),
denied: z.array(z.string()).optional()
})
.refine(
(data) => {
if (!data.allowed && !data.required && !data.denied) {
return false;
}
return true;
},
{
message: "Subject attribute must have at least one allowed, required, or denied value"
}
);
const templateV2KeyUsagesSchema = z
.object({
allowed: z.array(z.nativeEnum(CertKeyUsageType)).optional(),
required: z.array(z.nativeEnum(CertKeyUsageType)).optional(),
denied: z.array(z.nativeEnum(CertKeyUsageType)).optional()
})
.refine(
(data) => {
if (!data.allowed && !data.required && !data.denied) {
return false;
}
return true;
},
{
message: "Key usages must have at least one allowed, required, or denied value"
}
);
const templateV2ExtendedKeyUsagesSchema = z
.object({
allowed: z.array(z.nativeEnum(CertExtendedKeyUsageType)).optional(),
required: z.array(z.nativeEnum(CertExtendedKeyUsageType)).optional(),
denied: z.array(z.nativeEnum(CertExtendedKeyUsageType)).optional()
})
.refine(
(data) => {
if (!data.allowed && !data.required && !data.denied) {
return false;
}
return true;
},
{
message: "Extended key usages must have at least one allowed, required, or denied value"
}
);
const templateV2SanSchema = z
.object({
type: sanTypeSchema,
allowed: z.array(z.string()).optional(),
required: z.array(z.string()).optional(),
denied: z.array(z.string()).optional()
})
.refine(
(data) => {
if (!data.allowed && !data.required && !data.denied) {
return false;
}
return true;
},
{
message: "SAN must have at least one allowed, required, or denied value"
}
);
const templateV2ValiditySchema = z.object({
max: z
.string()
.refine(
(val) => {
if (!val) return true;
if (val.length < 2) return false;
const unit = val.slice(-1);
const number = val.slice(0, -1);
const digitRegex = new RE2("^\\d+$");
return ["d", "h", "m", "y"].includes(unit) && digitRegex.test(number);
},
{
message: "Max validity must be in format like '365d', '12m', '1y', or '24h'"
}
)
.optional()
});
const templateV2AlgorithmsSchema = z.object({
signature: z.array(z.string()).min(1, "At least one signature algorithm must be provided").optional(),
keyAlgorithm: z.array(z.string()).min(1, "At least one key algorithm must be provided").optional()
});
const createCertificateTemplateV2Schema = z.object({
projectId: z.string().min(1),
name: z.string().min(1).max(255, "Name must be between 1 and 255 characters"),
description: z.string().max(1000).optional(),
subject: z.array(templateV2SubjectSchema).optional(),
sans: z.array(templateV2SanSchema).optional(),
keyUsages: templateV2KeyUsagesSchema.optional(),
extendedKeyUsages: templateV2ExtendedKeyUsagesSchema.optional(),
algorithms: templateV2AlgorithmsSchema.optional(),
validity: templateV2ValiditySchema.optional()
});
const updateCertificateTemplateV2Schema = z.object({
name: z.string().min(1).max(255, "Name must be between 1 and 255 characters").optional(),
description: z.string().max(1000).optional(),
subject: z.array(templateV2SubjectSchema).optional(),
sans: z.array(templateV2SanSchema).optional(),
keyUsages: templateV2KeyUsagesSchema.optional(),
extendedKeyUsages: templateV2ExtendedKeyUsagesSchema.optional(),
algorithms: templateV2AlgorithmsSchema.optional(),
validity: templateV2ValiditySchema.optional()
}); });
export const registerCertificateTemplateRouter = async (server: FastifyZodProvider) => { export const registerCertificateTemplateRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateTemplates],
body: createCertificateTemplateV2Schema,
response: {
200: z.object({
certificateTemplate: certificateTemplateV2ResponseSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { projectId, ...data } = req.body;
const certificateTemplate = await server.services.certificateTemplateV2.createTemplateV2({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod!,
actorOrgId: req.permission.orgId,
projectId,
data
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.CREATE_CERTIFICATE_TEMPLATE,
metadata: {
certificateTemplateId: certificateTemplate.id,
name: certificateTemplate.name,
projectId: certificateTemplate.projectId
}
}
});
return { certificateTemplate };
}
});
server.route({ server.route({
method: "GET", method: "GET",
url: "/:certificateTemplateId", url: "/",
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateTemplates],
querystring: z.object({
projectId: z.string().min(1),
offset: z.coerce.number().min(0).default(0),
limit: z.coerce.number().min(1).max(100).default(20),
search: z.string().optional()
}),
response: {
200: z.object({
certificateTemplates: certificateTemplateV2ResponseSchema.array(),
totalCount: z.number()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { templates, totalCount } = await server.services.certificateTemplateV2.listTemplatesV2({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod!,
actorOrgId: req.permission.orgId,
...req.query
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: req.query.projectId,
event: {
type: EventType.LIST_CERTIFICATE_TEMPLATES,
metadata: {
projectId: req.query.projectId
}
}
});
return { certificateTemplates: templates, totalCount };
}
});
server.route({
method: "GET",
url: "/:id",
config: { config: {
rateLimit: readLimit rateLimit: readLimit
}, },
@@ -30,20 +241,22 @@ export const registerCertificateTemplateRouter = async (server: FastifyZodProvid
hide: false, hide: false,
tags: [ApiDocsTags.PkiCertificateTemplates], tags: [ApiDocsTags.PkiCertificateTemplates],
params: z.object({ params: z.object({
certificateTemplateId: z.string().describe(CERTIFICATE_TEMPLATES.GET.certificateTemplateId) id: z.string().uuid()
}), }),
response: { response: {
200: sanitizedCertificateTemplate 200: z.object({
certificateTemplate: certificateTemplateV2ResponseSchema
})
} }
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const certificateTemplate = await server.services.certificateTemplate.getCertTemplate({ const certificateTemplate = await server.services.certificateTemplateV2.getTemplateV2ById({
id: req.params.certificateTemplateId,
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod!,
actorOrgId: req.permission.orgId actorOrgId: req.permission.orgId,
templateId: req.params.id
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
@@ -58,125 +271,38 @@ export const registerCertificateTemplateRouter = async (server: FastifyZodProvid
} }
}); });
return certificateTemplate; return { certificateTemplate };
}
});
server.route({
method: "POST",
url: "/",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateTemplates],
body: z.object({
caId: z.string().describe(CERTIFICATE_TEMPLATES.CREATE.caId),
pkiCollectionId: z.string().optional().describe(CERTIFICATE_TEMPLATES.CREATE.pkiCollectionId),
name: slugSchema().describe(CERTIFICATE_TEMPLATES.CREATE.name),
commonName: validateTemplateRegexField.describe(CERTIFICATE_TEMPLATES.CREATE.commonName),
subjectAlternativeName: validateTemplateRegexField.describe(
CERTIFICATE_TEMPLATES.CREATE.subjectAlternativeName
),
ttl: z
.string()
.refine((val) => ms(val) > 0, "TTL must be a positive number")
.describe(CERTIFICATE_TEMPLATES.CREATE.ttl),
keyUsages: z
.nativeEnum(CertKeyUsage)
.array()
.optional()
.default([CertKeyUsage.DIGITAL_SIGNATURE, CertKeyUsage.KEY_ENCIPHERMENT])
.describe(CERTIFICATE_TEMPLATES.CREATE.keyUsages),
extendedKeyUsages: z
.nativeEnum(CertExtendedKeyUsage)
.array()
.optional()
.default([])
.describe(CERTIFICATE_TEMPLATES.CREATE.extendedKeyUsages)
}),
response: {
200: sanitizedCertificateTemplate
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const certificateTemplate = await server.services.certificateTemplate.createCertTemplate({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: certificateTemplate.projectId,
event: {
type: EventType.CREATE_CERTIFICATE_TEMPLATE,
metadata: {
certificateTemplateId: certificateTemplate.id,
caId: certificateTemplate.caId,
pkiCollectionId: certificateTemplate.pkiCollectionId as string,
name: certificateTemplate.name,
commonName: certificateTemplate.commonName,
subjectAlternativeName: certificateTemplate.subjectAlternativeName,
ttl: certificateTemplate.ttl,
projectId: certificateTemplate.projectId
}
}
});
return certificateTemplate;
} }
}); });
server.route({ server.route({
method: "PATCH", method: "PATCH",
url: "/:certificateTemplateId", url: "/:id",
config: { config: {
rateLimit: writeLimit rateLimit: writeLimit
}, },
schema: { schema: {
hide: false, hide: false,
tags: [ApiDocsTags.PkiCertificateTemplates], tags: [ApiDocsTags.PkiCertificateTemplates],
body: z.object({
caId: z.string().optional().describe(CERTIFICATE_TEMPLATES.UPDATE.caId),
pkiCollectionId: z.string().optional().describe(CERTIFICATE_TEMPLATES.UPDATE.pkiCollectionId),
name: slugSchema().optional().describe(CERTIFICATE_TEMPLATES.UPDATE.name),
commonName: validateTemplateRegexField.optional().describe(CERTIFICATE_TEMPLATES.UPDATE.commonName),
subjectAlternativeName: validateTemplateRegexField
.optional()
.describe(CERTIFICATE_TEMPLATES.UPDATE.subjectAlternativeName),
ttl: z
.string()
.refine((val) => ms(val) > 0, "TTL must be a positive number")
.optional()
.describe(CERTIFICATE_TEMPLATES.UPDATE.ttl),
keyUsages: z.nativeEnum(CertKeyUsage).array().optional().describe(CERTIFICATE_TEMPLATES.UPDATE.keyUsages),
extendedKeyUsages: z
.nativeEnum(CertExtendedKeyUsage)
.array()
.optional()
.describe(CERTIFICATE_TEMPLATES.UPDATE.extendedKeyUsages)
}),
params: z.object({ params: z.object({
certificateTemplateId: z.string().describe(CERTIFICATE_TEMPLATES.UPDATE.certificateTemplateId) id: z.string().uuid()
}), }),
body: updateCertificateTemplateV2Schema,
response: { response: {
200: sanitizedCertificateTemplate 200: z.object({
certificateTemplate: certificateTemplateV2ResponseSchema
})
} }
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const certificateTemplate = await server.services.certificateTemplate.updateCertTemplate({ const certificateTemplate = await server.services.certificateTemplateV2.updateTemplateV2({
...req.body,
id: req.params.certificateTemplateId,
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod!,
actorOrgId: req.permission.orgId actorOrgId: req.permission.orgId,
templateId: req.params.id,
data: req.body
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
@@ -186,23 +312,18 @@ export const registerCertificateTemplateRouter = async (server: FastifyZodProvid
type: EventType.UPDATE_CERTIFICATE_TEMPLATE, type: EventType.UPDATE_CERTIFICATE_TEMPLATE,
metadata: { metadata: {
certificateTemplateId: certificateTemplate.id, certificateTemplateId: certificateTemplate.id,
name: certificateTemplate.name, name: certificateTemplate.name
caId: certificateTemplate.caId,
pkiCollectionId: certificateTemplate.pkiCollectionId as string,
commonName: certificateTemplate.commonName,
subjectAlternativeName: certificateTemplate.subjectAlternativeName,
ttl: certificateTemplate.ttl
} }
} }
}); });
return certificateTemplate; return { certificateTemplate };
} }
}); });
server.route({ server.route({
method: "DELETE", method: "DELETE",
url: "/:certificateTemplateId", url: "/:id",
config: { config: {
rateLimit: writeLimit rateLimit: writeLimit
}, },
@@ -210,20 +331,22 @@ export const registerCertificateTemplateRouter = async (server: FastifyZodProvid
hide: false, hide: false,
tags: [ApiDocsTags.PkiCertificateTemplates], tags: [ApiDocsTags.PkiCertificateTemplates],
params: z.object({ params: z.object({
certificateTemplateId: z.string().describe(CERTIFICATE_TEMPLATES.DELETE.certificateTemplateId) id: z.string().uuid()
}), }),
response: { response: {
200: sanitizedCertificateTemplate 200: z.object({
certificateTemplate: certificateTemplateV2ResponseSchema
})
} }
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const certificateTemplate = await server.services.certificateTemplate.deleteCertTemplate({ const certificateTemplate = await server.services.certificateTemplateV2.deleteTemplateV2({
id: req.params.certificateTemplateId,
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod!,
actorOrgId: req.permission.orgId actorOrgId: req.permission.orgId,
templateId: req.params.id
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
@@ -238,158 +361,7 @@ export const registerCertificateTemplateRouter = async (server: FastifyZodProvid
} }
}); });
return certificateTemplate; return { certificateTemplate };
}
});
server.route({
method: "POST",
url: "/:certificateTemplateId/est-config",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateTemplates],
description: "Create Certificate Template EST configuration",
params: z.object({
certificateTemplateId: z.string().trim()
}),
body: z
.object({
caChain: z.string().trim().optional(),
passphrase: z.string().min(1),
isEnabled: z.boolean().default(true),
disableBootstrapCertValidation: z.boolean().default(false)
})
.refine(
({ caChain, disableBootstrapCertValidation }) =>
disableBootstrapCertValidation || (!disableBootstrapCertValidation && caChain),
"CA chain is required"
),
response: {
200: sanitizedEstConfig
}
},
handler: async (req) => {
const estConfig = await server.services.certificateTemplate.createEstConfiguration({
certificateTemplateId: req.params.certificateTemplateId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: estConfig.projectId,
event: {
type: EventType.CREATE_CERTIFICATE_TEMPLATE_EST_CONFIG,
metadata: {
certificateTemplateId: estConfig.certificateTemplateId,
isEnabled: estConfig.isEnabled as boolean
}
}
});
return estConfig;
}
});
server.route({
method: "PATCH",
url: "/:certificateTemplateId/est-config",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateTemplates],
description: "Update Certificate Template EST configuration",
params: z.object({
certificateTemplateId: z.string().trim()
}),
body: z.object({
caChain: z.string().trim().optional(),
passphrase: z.string().min(1).optional(),
disableBootstrapCertValidation: z.boolean().optional(),
isEnabled: z.boolean().optional()
}),
response: {
200: sanitizedEstConfig
}
},
handler: async (req) => {
const estConfig = await server.services.certificateTemplate.updateEstConfiguration({
certificateTemplateId: req.params.certificateTemplateId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: estConfig.projectId,
event: {
type: EventType.UPDATE_CERTIFICATE_TEMPLATE_EST_CONFIG,
metadata: {
certificateTemplateId: estConfig.certificateTemplateId,
isEnabled: estConfig.isEnabled as boolean
}
}
});
return estConfig;
}
});
server.route({
method: "GET",
url: "/:certificateTemplateId/est-config",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateTemplates],
description: "Get Certificate Template EST configuration",
params: z.object({
certificateTemplateId: z.string().trim()
}),
response: {
200: sanitizedEstConfig.extend({
caChain: z.string()
})
}
},
handler: async (req) => {
const estConfig = await server.services.certificateTemplate.getEstConfiguration({
isInternal: false,
certificateTemplateId: req.params.certificateTemplateId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: estConfig.projectId,
event: {
type: EventType.GET_CERTIFICATE_TEMPLATE_EST_CONFIG,
metadata: {
certificateTemplateId: estConfig.certificateTemplateId
}
}
});
return estConfig;
} }
}); });
}; };
@@ -0,0 +1,680 @@
/* eslint-disable @typescript-eslint/no-floating-promises */
import RE2 from "re2";
import { z } from "zod";
import { CertificatesSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ApiDocsTags, CERTIFICATE_AUTHORITIES, CERTIFICATES } from "@app/lib/api-docs";
import { ms } from "@app/lib/ms";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { addNoCacheHeaders } from "@app/server/lib/caching";
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { CertExtendedKeyUsage, CertKeyUsage, CrlReason } from "@app/services/certificate/certificate-types";
import {
validateAltNamesField,
validateCaDateField
} from "@app/services/certificate-authority/certificate-authority-validators";
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
export const registerDeprecatedCertRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:serialNumber",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificates],
description: "Get certificate",
params: z.object({
serialNumber: z.string().trim().describe(CERTIFICATES.GET.serialNumber)
}),
response: {
200: z.object({
certificate: CertificatesSchema
})
}
},
handler: async (req) => {
const { cert } = await server.services.certificate.getCert({
serialNumber: req.params.serialNumber,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: cert.projectId,
event: {
type: EventType.GET_CERT,
metadata: {
certId: cert.id,
cn: cert.commonName,
serialNumber: cert.serialNumber
}
}
});
return {
certificate: cert
};
}
});
// TODO: In the future add support for other formats outside of PEM (such as DER). Adding a "format" query param may be best.
server.route({
method: "GET",
url: "/:serialNumber/private-key",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificates],
description: "Get certificate private key",
params: z.object({
serialNumber: z.string().trim().describe(CERTIFICATES.GET.serialNumber)
}),
response: {
200: z.string().trim()
}
},
handler: async (req, reply) => {
const { cert, certPrivateKey } = await server.services.certificate.getCertPrivateKey({
serialNumber: req.params.serialNumber,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: cert.projectId,
event: {
type: EventType.GET_CERT_PRIVATE_KEY,
metadata: {
certId: cert.id,
cn: cert.commonName,
serialNumber: cert.serialNumber
}
}
});
addNoCacheHeaders(reply);
return certPrivateKey;
}
});
// TODO: In the future add support for other formats outside of PEM (such as DER). Adding a "format" query param may be best.
server.route({
method: "GET",
url: "/:serialNumber/bundle",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificates],
description: "Get certificate bundle including the certificate, chain, and private key.",
params: z.object({
serialNumber: z.string().trim().describe(CERTIFICATES.GET_CERT.serialNumber)
}),
response: {
200: z.object({
certificate: z.string().trim().describe(CERTIFICATES.GET_CERT.certificate),
certificateChain: z.string().trim().nullable().describe(CERTIFICATES.GET_CERT.certificateChain),
privateKey: z.string().trim().nullable().describe(CERTIFICATES.GET_CERT.privateKey),
serialNumber: z.string().trim().describe(CERTIFICATES.GET_CERT.serialNumberRes)
})
}
},
handler: async (req, reply) => {
const { certificate, certificateChain, serialNumber, cert, privateKey } =
await server.services.certificate.getCertBundle({
serialNumber: req.params.serialNumber,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: cert.projectId,
event: {
type: EventType.GET_CERT_BUNDLE,
metadata: {
certId: cert.id,
cn: cert.commonName,
serialNumber: cert.serialNumber
}
}
});
addNoCacheHeaders(reply);
return {
certificate,
certificateChain,
serialNumber,
privateKey
};
}
});
server.route({
method: "POST",
url: "/issue-certificate",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificates],
description: "Issue certificate",
body: z
.object({
caId: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.caId),
certificateTemplateId: z
.string()
.trim()
.optional()
.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.certificateTemplateId),
pkiCollectionId: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.pkiCollectionId),
friendlyName: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.friendlyName),
commonName: z.string().trim().min(1).describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.commonName),
altNames: validateAltNamesField.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.altNames),
ttl: z
.string()
.refine((val) => ms(val) > 0, "TTL must be a positive number")
.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.ttl),
notBefore: validateCaDateField.optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.notBefore),
notAfter: validateCaDateField.optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.notAfter),
keyUsages: z
.nativeEnum(CertKeyUsage)
.array()
.optional()
.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.keyUsages),
extendedKeyUsages: z
.nativeEnum(CertExtendedKeyUsage)
.array()
.optional()
.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.extendedKeyUsages)
})
.refine(
(data) => {
const { ttl, notAfter } = data;
return (ttl !== undefined && notAfter === undefined) || (ttl === undefined && notAfter !== undefined);
},
{
message: "Either ttl or notAfter must be present, but not both",
path: ["ttl", "notAfter"]
}
)
.refine(
(data) =>
(data.caId !== undefined && data.certificateTemplateId === undefined) ||
(data.caId === undefined && data.certificateTemplateId !== undefined),
{
message: "Either CA ID or Certificate Template ID must be present, but not both",
path: ["caId", "certificateTemplateId"]
}
),
response: {
200: z.object({
certificate: z.string().trim().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.certificate),
issuingCaCertificate: z.string().trim().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.issuingCaCertificate),
certificateChain: z.string().trim().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.certificateChain),
privateKey: z.string().trim().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.privateKey),
serialNumber: z.string().trim().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.serialNumber)
})
}
},
handler: async (req) => {
const { certificate, certificateChain, issuingCaCertificate, privateKey, serialNumber, ca } =
await server.services.internalCertificateAuthority.issueCertFromCa({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: ca.projectId,
event: {
type: EventType.ISSUE_CERT,
metadata: {
caId: ca.id,
dn: ca.dn,
serialNumber
}
}
});
await server.services.telemetry.sendPostHogEvents({
event: PostHogEventTypes.IssueCert,
distinctId: getTelemetryDistinctId(req),
organizationId: req.permission.orgId,
properties: {
caId: req.body.caId,
certificateTemplateId: req.body.certificateTemplateId,
commonName: req.body.commonName,
...req.auditLogInfo
}
});
return {
certificate,
certificateChain,
issuingCaCertificate,
privateKey,
serialNumber
};
}
});
server.route({
method: "POST",
url: "/import-certificate",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificates],
description: "Import certificate",
body: z.object({
projectSlug: z.string().trim().min(1).describe(CERTIFICATES.IMPORT.projectSlug),
certificatePem: z.string().trim().min(1).describe(CERTIFICATES.IMPORT.certificatePem),
privateKeyPem: z.string().trim().min(1).describe(CERTIFICATES.IMPORT.privateKeyPem),
chainPem: z.string().trim().min(1).describe(CERTIFICATES.IMPORT.chainPem),
friendlyName: z.string().trim().optional().describe(CERTIFICATES.IMPORT.friendlyName),
pkiCollectionId: z.string().trim().optional().describe(CERTIFICATES.IMPORT.pkiCollectionId)
}),
response: {
200: z.object({
certificate: z.string().trim().describe(CERTIFICATES.IMPORT.certificate),
certificateChain: z.string().trim().describe(CERTIFICATES.IMPORT.certificateChain),
privateKey: z.string().trim().describe(CERTIFICATES.IMPORT.privateKey),
serialNumber: z.string().trim().describe(CERTIFICATES.IMPORT.serialNumber)
})
}
},
handler: async (req) => {
const { certificate, certificateChain, privateKey, serialNumber, cert } =
await server.services.certificate.importCert({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: cert.projectId,
event: {
type: EventType.IMPORT_CERT,
metadata: {
certId: cert.id,
cn: cert.commonName,
serialNumber
}
}
});
return {
certificate,
certificateChain,
privateKey,
serialNumber
};
}
});
server.route({
method: "POST",
url: "/sign-certificate",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificates],
description: "Sign certificate",
body: z
.object({
caId: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.caId),
certificateTemplateId: z
.string()
.trim()
.optional()
.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.certificateTemplateId),
pkiCollectionId: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.pkiCollectionId),
csr: z.string().trim().min(1).describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.csr),
friendlyName: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.friendlyName),
commonName: z.string().trim().min(1).optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.commonName),
altNames: validateAltNamesField.describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.altNames),
ttl: z
.string()
.refine((val) => ms(val) > 0, "TTL must be a positive number")
.describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.ttl),
notBefore: validateCaDateField.optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.notBefore),
notAfter: validateCaDateField.optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.notAfter),
keyUsages: z
.nativeEnum(CertKeyUsage)
.array()
.optional()
.describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.keyUsages),
extendedKeyUsages: z
.nativeEnum(CertExtendedKeyUsage)
.array()
.optional()
.describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.extendedKeyUsages)
})
.refine(
(data) => {
const { ttl, notAfter } = data;
return (ttl !== undefined && notAfter === undefined) || (ttl === undefined && notAfter !== undefined);
},
{
message: "Either ttl or notAfter must be present, but not both",
path: ["ttl", "notAfter"]
}
)
.refine(
(data) =>
(data.caId !== undefined && data.certificateTemplateId === undefined) ||
(data.caId === undefined && data.certificateTemplateId !== undefined),
{
message: "Either CA ID or Certificate Template ID must be present, but not both",
path: ["caId", "certificateTemplateId"]
}
),
response: {
200: z.object({
certificate: z.string().trim().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.certificate),
issuingCaCertificate: z.string().trim().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.issuingCaCertificate),
certificateChain: z.string().trim().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.certificateChain),
serialNumber: z.string().trim().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.serialNumber)
})
}
},
handler: async (req) => {
const { certificate, certificateChain, issuingCaCertificate, serialNumber, ca, commonName } =
await server.services.internalCertificateAuthority.signCertFromCa({
isInternal: false,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: ca.projectId,
event: {
type: EventType.SIGN_CERT,
metadata: {
caId: ca.id,
dn: ca.dn,
serialNumber
}
}
});
await server.services.telemetry.sendPostHogEvents({
event: PostHogEventTypes.SignCert,
distinctId: getTelemetryDistinctId(req),
organizationId: req.permission.orgId,
properties: {
caId: req.body.caId,
certificateTemplateId: req.body.certificateTemplateId,
commonName,
...req.auditLogInfo
}
});
return {
certificate: certificate.toString("pem"),
certificateChain,
issuingCaCertificate,
serialNumber
};
}
});
server.route({
method: "POST",
url: "/:serialNumber/revoke",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificates],
description: "Revoke",
params: z.object({
serialNumber: z.string().trim().describe(CERTIFICATES.REVOKE.serialNumber)
}),
body: z.object({
revocationReason: z.nativeEnum(CrlReason).describe(CERTIFICATES.REVOKE.revocationReason)
}),
response: {
200: z.object({
message: z.string().trim(),
serialNumber: z.string().trim().describe(CERTIFICATES.REVOKE.serialNumberRes),
revokedAt: z.date().describe(CERTIFICATES.REVOKE.revokedAt)
})
}
},
handler: async (req) => {
const { revokedAt, cert, ca } = await server.services.certificate.revokeCert({
serialNumber: req.params.serialNumber,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: ca.projectId,
event: {
type: EventType.REVOKE_CERT,
metadata: {
certId: cert.id,
cn: cert.commonName,
serialNumber: cert.serialNumber
}
}
});
return {
message: "Successfully revoked certificate",
serialNumber: req.params.serialNumber,
revokedAt
};
}
});
server.route({
method: "DELETE",
url: "/:serialNumber",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificates],
description: "Delete certificate",
params: z.object({
serialNumber: z.string().trim().describe(CERTIFICATES.DELETE.serialNumber)
}),
response: {
200: z.object({
certificate: CertificatesSchema
})
}
},
handler: async (req) => {
const { deletedCert } = await server.services.certificate.deleteCert({
serialNumber: req.params.serialNumber,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: deletedCert.projectId,
event: {
type: EventType.DELETE_CERT,
metadata: {
certId: deletedCert.id,
cn: deletedCert.commonName,
serialNumber: deletedCert.serialNumber
}
}
});
return {
certificate: deletedCert
};
}
});
server.route({
method: "GET",
url: "/:serialNumber/certificate",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificates],
description: "Get certificate body of certificate",
params: z.object({
serialNumber: z.string().trim().describe(CERTIFICATES.GET_CERT.serialNumber)
}),
response: {
200: z.object({
certificate: z.string().trim().describe(CERTIFICATES.GET_CERT.certificate),
certificateChain: z.string().trim().nullable().describe(CERTIFICATES.GET_CERT.certificateChain),
serialNumber: z.string().trim().describe(CERTIFICATES.GET_CERT.serialNumberRes)
})
}
},
handler: async (req) => {
const { certificate, certificateChain, serialNumber, cert } = await server.services.certificate.getCertBody({
serialNumber: req.params.serialNumber,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: cert.projectId,
event: {
type: EventType.GET_CERT_BODY,
metadata: {
certId: cert.id,
cn: cert.commonName,
serialNumber: cert.serialNumber
}
}
});
return {
certificate,
certificateChain,
serialNumber
};
}
});
server.route({
method: "POST",
url: "/:serialNumber/pkcs12",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
hide: true,
tags: [ApiDocsTags.PkiCertificates],
description: "Download certificate in PKCS12 format",
params: z.object({
serialNumber: z.string().trim().describe(CERTIFICATES.GET.serialNumber)
}),
body: z.object({
password: z
.string()
.min(6, "Password must be at least 6 characters long")
.describe("Password for the keystore (minimum 6 characters)"),
alias: z.string().min(1, "Alias is required").describe("Alias for the certificate in the keystore")
}),
response: {
200: z.any().describe("PKCS12 keystore as binary data")
}
},
handler: async (req, reply) => {
const { pkcs12Data, cert } = await server.services.certificate.getCertPkcs12({
serialNumber: req.params.serialNumber,
password: req.body.password,
alias: req.body.alias,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: cert.projectId,
event: {
type: EventType.EXPORT_CERT_PKCS12,
metadata: {
certId: cert.id,
cn: cert.commonName,
serialNumber: cert.serialNumber
}
}
});
addNoCacheHeaders(reply);
reply.header("Content-Type", "application/octet-stream");
reply.header(
"Content-Disposition",
`attachment; filename="certificate-${req.params.serialNumber.replace(new RE2("[^\\w.-]", "g"), "_")}.p12"`
);
return pkcs12Data;
}
});
};
@@ -0,0 +1,395 @@
import { z } from "zod";
import { CertificateTemplateEstConfigsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ApiDocsTags, CERTIFICATE_TEMPLATES } from "@app/lib/api-docs";
import { ms } from "@app/lib/ms";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { slugSchema } from "@app/server/lib/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { CertExtendedKeyUsage, CertKeyUsage } from "@app/services/certificate/certificate-types";
import { sanitizedCertificateTemplate } from "@app/services/certificate-template/certificate-template-schema";
import { validateTemplateRegexField } from "@app/services/certificate-template/certificate-template-validators";
const sanitizedEstConfig = CertificateTemplateEstConfigsSchema.pick({
id: true,
certificateTemplateId: true,
isEnabled: true,
disableBootstrapCertValidation: true
});
export const registerDeprecatedCertificateTemplateRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:certificateTemplateId",
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateTemplates],
params: z.object({
certificateTemplateId: z.string().describe(CERTIFICATE_TEMPLATES.GET.certificateTemplateId)
}),
response: {
200: sanitizedCertificateTemplate
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const certificateTemplate = await server.services.certificateTemplate.getCertTemplate({
id: req.params.certificateTemplateId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: certificateTemplate.projectId,
event: {
type: EventType.GET_CERTIFICATE_TEMPLATE,
metadata: {
certificateTemplateId: certificateTemplate.id,
name: certificateTemplate.name
}
}
});
return certificateTemplate;
}
});
server.route({
method: "POST",
url: "/",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateTemplates],
body: z.object({
caId: z.string().describe(CERTIFICATE_TEMPLATES.CREATE.caId),
pkiCollectionId: z.string().optional().describe(CERTIFICATE_TEMPLATES.CREATE.pkiCollectionId),
name: slugSchema().describe(CERTIFICATE_TEMPLATES.CREATE.name),
commonName: validateTemplateRegexField.describe(CERTIFICATE_TEMPLATES.CREATE.commonName),
subjectAlternativeName: validateTemplateRegexField.describe(
CERTIFICATE_TEMPLATES.CREATE.subjectAlternativeName
),
ttl: z
.string()
.refine((val) => ms(val) > 0, "TTL must be a positive number")
.describe(CERTIFICATE_TEMPLATES.CREATE.ttl),
keyUsages: z
.nativeEnum(CertKeyUsage)
.array()
.optional()
.default([CertKeyUsage.DIGITAL_SIGNATURE, CertKeyUsage.KEY_ENCIPHERMENT])
.describe(CERTIFICATE_TEMPLATES.CREATE.keyUsages),
extendedKeyUsages: z
.nativeEnum(CertExtendedKeyUsage)
.array()
.optional()
.default([])
.describe(CERTIFICATE_TEMPLATES.CREATE.extendedKeyUsages)
}),
response: {
200: sanitizedCertificateTemplate
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const certificateTemplate = await server.services.certificateTemplate.createCertTemplate({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: certificateTemplate.projectId,
event: {
type: EventType.CREATE_CERTIFICATE_TEMPLATE,
metadata: {
certificateTemplateId: certificateTemplate.id,
caId: certificateTemplate.caId,
pkiCollectionId: certificateTemplate.pkiCollectionId as string,
name: certificateTemplate.name,
commonName: certificateTemplate.commonName,
subjectAlternativeName: certificateTemplate.subjectAlternativeName,
ttl: certificateTemplate.ttl,
projectId: certificateTemplate.projectId
}
}
});
return certificateTemplate;
}
});
server.route({
method: "PATCH",
url: "/:certificateTemplateId",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateTemplates],
body: z.object({
caId: z.string().optional().describe(CERTIFICATE_TEMPLATES.UPDATE.caId),
pkiCollectionId: z.string().optional().describe(CERTIFICATE_TEMPLATES.UPDATE.pkiCollectionId),
name: slugSchema().optional().describe(CERTIFICATE_TEMPLATES.UPDATE.name),
commonName: validateTemplateRegexField.optional().describe(CERTIFICATE_TEMPLATES.UPDATE.commonName),
subjectAlternativeName: validateTemplateRegexField
.optional()
.describe(CERTIFICATE_TEMPLATES.UPDATE.subjectAlternativeName),
ttl: z
.string()
.refine((val) => ms(val) > 0, "TTL must be a positive number")
.optional()
.describe(CERTIFICATE_TEMPLATES.UPDATE.ttl),
keyUsages: z.nativeEnum(CertKeyUsage).array().optional().describe(CERTIFICATE_TEMPLATES.UPDATE.keyUsages),
extendedKeyUsages: z
.nativeEnum(CertExtendedKeyUsage)
.array()
.optional()
.describe(CERTIFICATE_TEMPLATES.UPDATE.extendedKeyUsages)
}),
params: z.object({
certificateTemplateId: z.string().describe(CERTIFICATE_TEMPLATES.UPDATE.certificateTemplateId)
}),
response: {
200: sanitizedCertificateTemplate
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const certificateTemplate = await server.services.certificateTemplate.updateCertTemplate({
...req.body,
id: req.params.certificateTemplateId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: certificateTemplate.projectId,
event: {
type: EventType.UPDATE_CERTIFICATE_TEMPLATE,
metadata: {
certificateTemplateId: certificateTemplate.id,
name: certificateTemplate.name,
caId: certificateTemplate.caId,
pkiCollectionId: certificateTemplate.pkiCollectionId as string,
commonName: certificateTemplate.commonName,
subjectAlternativeName: certificateTemplate.subjectAlternativeName,
ttl: certificateTemplate.ttl
}
}
});
return certificateTemplate;
}
});
server.route({
method: "DELETE",
url: "/:certificateTemplateId",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateTemplates],
params: z.object({
certificateTemplateId: z.string().describe(CERTIFICATE_TEMPLATES.DELETE.certificateTemplateId)
}),
response: {
200: sanitizedCertificateTemplate
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const certificateTemplate = await server.services.certificateTemplate.deleteCertTemplate({
id: req.params.certificateTemplateId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: certificateTemplate.projectId,
event: {
type: EventType.DELETE_CERTIFICATE_TEMPLATE,
metadata: {
certificateTemplateId: certificateTemplate.id,
name: certificateTemplate.name
}
}
});
return certificateTemplate;
}
});
server.route({
method: "POST",
url: "/:certificateTemplateId/est-config",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateTemplates],
description: "Create Certificate Template EST configuration",
params: z.object({
certificateTemplateId: z.string().trim()
}),
body: z
.object({
caChain: z.string().trim().optional(),
passphrase: z.string().min(1),
isEnabled: z.boolean().default(true),
disableBootstrapCertValidation: z.boolean().default(false)
})
.refine(
({ caChain, disableBootstrapCertValidation }) =>
disableBootstrapCertValidation || (!disableBootstrapCertValidation && caChain),
"CA chain is required"
),
response: {
200: sanitizedEstConfig
}
},
handler: async (req) => {
const estConfig = await server.services.certificateTemplate.createEstConfiguration({
certificateTemplateId: req.params.certificateTemplateId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: estConfig.projectId,
event: {
type: EventType.CREATE_CERTIFICATE_TEMPLATE_EST_CONFIG,
metadata: {
certificateTemplateId: estConfig.certificateTemplateId,
isEnabled: estConfig.isEnabled as boolean
}
}
});
return estConfig;
}
});
server.route({
method: "PATCH",
url: "/:certificateTemplateId/est-config",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateTemplates],
description: "Update Certificate Template EST configuration",
params: z.object({
certificateTemplateId: z.string().trim()
}),
body: z.object({
caChain: z.string().trim().optional(),
passphrase: z.string().min(1).optional(),
disableBootstrapCertValidation: z.boolean().optional(),
isEnabled: z.boolean().optional()
}),
response: {
200: sanitizedEstConfig
}
},
handler: async (req) => {
const estConfig = await server.services.certificateTemplate.updateEstConfiguration({
certificateTemplateId: req.params.certificateTemplateId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: estConfig.projectId,
event: {
type: EventType.UPDATE_CERTIFICATE_TEMPLATE_EST_CONFIG,
metadata: {
certificateTemplateId: estConfig.certificateTemplateId,
isEnabled: estConfig.isEnabled as boolean
}
}
});
return estConfig;
}
});
server.route({
method: "GET",
url: "/:certificateTemplateId/est-config",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiCertificateTemplates],
description: "Get Certificate Template EST configuration",
params: z.object({
certificateTemplateId: z.string().trim()
}),
response: {
200: sanitizedEstConfig.extend({
caChain: z.string()
})
}
},
handler: async (req) => {
const estConfig = await server.services.certificateTemplate.getEstConfiguration({
isInternal: false,
certificateTemplateId: req.params.certificateTemplateId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: estConfig.projectId,
event: {
type: EventType.GET_CERTIFICATE_TEMPLATE_EST_CONFIG,
metadata: {
certificateTemplateId: estConfig.certificateTemplateId
}
}
});
return estConfig;
}
});
};
@@ -0,0 +1,205 @@
import { z } from "zod";
import { PkiAlertsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ALERTS, ApiDocsTags } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { PkiAlertEventType } from "@app/services/pki-alert-v2/pki-alert-v2-types";
export const registerDeprecatedPkiAlertRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
tags: [ApiDocsTags.PkiAlerting],
description: "Create PKI alert",
body: z.object({
projectId: z.string().trim().describe(ALERTS.CREATE.projectId),
pkiCollectionId: z.string().trim().describe(ALERTS.CREATE.pkiCollectionId),
name: z.string().trim().describe(ALERTS.CREATE.name),
alertBeforeDays: z.number().describe(ALERTS.CREATE.alertBeforeDays),
emails: z
.array(z.string().trim().email({ message: "Invalid email address" }))
.min(1, { message: "You must specify at least 1 email" })
.max(5, { message: "You can specify a maximum of 5 emails" })
.describe(ALERTS.CREATE.emails)
}),
response: {
200: PkiAlertsSchema
}
},
handler: async (req) => {
const alert = await server.services.pkiAlert.createPkiAlert({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: alert.projectId,
event: {
type: EventType.CREATE_PKI_ALERT,
metadata: {
pkiAlertId: alert.id,
pkiCollectionId: alert.pkiCollectionId,
name: alert.name,
alertBefore: alert.alertBeforeDays.toString(),
eventType: PkiAlertEventType.EXPIRATION,
recipientEmails: alert.recipientEmails
}
}
});
return alert;
}
});
server.route({
method: "GET",
url: "/:alertId",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
tags: [ApiDocsTags.PkiAlerting],
description: "Get PKI alert",
params: z.object({
alertId: z.string().trim().describe(ALERTS.GET.alertId)
}),
response: {
200: PkiAlertsSchema
}
},
handler: async (req) => {
const alert = await server.services.pkiAlert.getPkiAlertById({
alertId: req.params.alertId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: alert.projectId,
event: {
type: EventType.GET_PKI_ALERT,
metadata: {
pkiAlertId: alert.id
}
}
});
return alert;
}
});
server.route({
method: "PATCH",
url: "/:alertId",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
tags: [ApiDocsTags.PkiAlerting],
description: "Update PKI alert",
params: z.object({
alertId: z.string().trim().describe(ALERTS.UPDATE.alertId)
}),
body: z.object({
name: z.string().trim().optional().describe(ALERTS.UPDATE.name),
alertBeforeDays: z.number().optional().describe(ALERTS.UPDATE.alertBeforeDays),
pkiCollectionId: z.string().trim().optional().describe(ALERTS.UPDATE.pkiCollectionId),
emails: z
.array(z.string().trim().email({ message: "Invalid email address" }))
.min(1, { message: "You must specify at least 1 email" })
.max(5, { message: "You can specify a maximum of 5 emails" })
.optional()
.describe(ALERTS.UPDATE.emails)
}),
response: {
200: PkiAlertsSchema
}
},
handler: async (req) => {
const alert = await server.services.pkiAlert.updatePkiAlert({
alertId: req.params.alertId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: alert.projectId,
event: {
type: EventType.UPDATE_PKI_ALERT,
metadata: {
pkiAlertId: alert.id,
pkiCollectionId: alert.pkiCollectionId,
name: alert.name,
alertBefore: alert.alertBeforeDays.toString(),
eventType: PkiAlertEventType.EXPIRATION,
recipientEmails: alert.recipientEmails
}
}
});
return alert;
}
});
server.route({
method: "DELETE",
url: "/:alertId",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
tags: [ApiDocsTags.PkiAlerting],
description: "Delete PKI alert",
params: z.object({
alertId: z.string().trim().describe(ALERTS.DELETE.alertId)
}),
response: {
200: PkiAlertsSchema
}
},
handler: async (req) => {
const alert = await server.services.pkiAlert.deletePkiAlert({
alertId: req.params.alertId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: alert.projectId,
event: {
type: EventType.DELETE_PKI_ALERT,
metadata: {
pkiAlertId: alert.id
}
}
});
return alert;
}
});
};
+41 -4
View File
@@ -11,10 +11,14 @@ import { registerAuthRoutes } from "./auth-router";
import { registerProjectBotRouter } from "./bot-router"; import { registerProjectBotRouter } from "./bot-router";
import { registerCaRouter } from "./certificate-authority-router"; import { registerCaRouter } from "./certificate-authority-router";
import { CERTIFICATE_AUTHORITY_REGISTER_ROUTER_MAP } from "./certificate-authority-routers"; import { CERTIFICATE_AUTHORITY_REGISTER_ROUTER_MAP } from "./certificate-authority-routers";
import { registerGeneralCertificateAuthorityRouter } from "./certificate-authority-routers/general-certificate-authority-router";
import { registerCertificateProfilesRouter } from "./certificate-profiles-router"; import { registerCertificateProfilesRouter } from "./certificate-profiles-router";
import { registerCertRouter } from "./certificate-router"; import { registerCertificateRouter } from "./certificate-router";
import { registerCertificateTemplateRouter } from "./certificate-template-router"; import { registerCertificateTemplateRouter } from "./certificate-template-router";
import { registerDeprecatedCertRouter } from "./deprecated-certificate-router";
import { registerDeprecatedCertificateTemplateRouter } from "./deprecated-certificate-template-router";
import { registerDeprecatedIdentityProjectMembershipRouter } from "./deprecated-identity-project-membership-router"; import { registerDeprecatedIdentityProjectMembershipRouter } from "./deprecated-identity-project-membership-router";
import { registerDeprecatedPkiAlertRouter } from "./deprecated-pki-alert-router";
import { registerDeprecatedProjectEnvRouter } from "./deprecated-project-env-router"; import { registerDeprecatedProjectEnvRouter } from "./deprecated-project-env-router";
import { registerDeprecatedProjectMembershipRouter } from "./deprecated-project-membership-router"; import { registerDeprecatedProjectMembershipRouter } from "./deprecated-project-membership-router";
import { registerDeprecatedProjectRouter } from "./deprecated-project-router"; import { registerDeprecatedProjectRouter } from "./deprecated-project-router";
@@ -148,6 +152,39 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
prefix: "/projects/:projectId/memberships" prefix: "/projects/:projectId/memberships"
}); });
await server.register(
async (pkiRouter) => {
await pkiRouter.register(
async (caRouter) => {
for await (const [caType, router] of Object.entries(CERTIFICATE_AUTHORITY_REGISTER_ROUTER_MAP)) {
await caRouter.register(router, { prefix: `/${caType}` });
}
await caRouter.register(registerGeneralCertificateAuthorityRouter);
},
{
prefix: "/ca"
}
);
await pkiRouter.register(registerCertificateRouter, { prefix: "/certificates" });
await pkiRouter.register(registerCertificateTemplateRouter, { prefix: "/certificate-templates" });
await pkiRouter.register(registerCertificateProfilesRouter, { prefix: "/certificate-profiles" });
await pkiRouter.register(registerPkiAlertRouter, { prefix: "/alerts" });
await pkiRouter.register(
async (pkiSyncRouter) => {
await pkiSyncRouter.register(registerPkiSyncRouter);
for await (const [destination, router] of Object.entries(PKI_SYNC_REGISTER_ROUTER_MAP)) {
await pkiSyncRouter.register(router, { prefix: `/${destination}` });
}
},
{ prefix: "/syncs" }
);
},
{ prefix: "/cert-manager" }
);
// NOTE: THESE /pki/* ENDPOINTS ARE TO BE DEPRECATED IN FAVOR OF /cert-manager/*
// DO NOT EXTEND THEM ANYMORE!!!
await server.register( await server.register(
async (pkiRouter) => { async (pkiRouter) => {
await pkiRouter.register(registerCaRouter, { prefix: "/ca" }); await pkiRouter.register(registerCaRouter, { prefix: "/ca" });
@@ -161,10 +198,10 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
prefix: "/ca" prefix: "/ca"
} }
); );
await pkiRouter.register(registerCertRouter, { prefix: "/certificates" }); await pkiRouter.register(registerDeprecatedCertRouter, { prefix: "/certificates" });
await pkiRouter.register(registerCertificateTemplateRouter, { prefix: "/certificate-templates" }); await pkiRouter.register(registerDeprecatedCertificateTemplateRouter, { prefix: "/certificate-templates" });
await pkiRouter.register(registerCertificateProfilesRouter, { prefix: "/certificate-profiles" }); await pkiRouter.register(registerCertificateProfilesRouter, { prefix: "/certificate-profiles" });
await pkiRouter.register(registerPkiAlertRouter, { prefix: "/alerts" }); await pkiRouter.register(registerDeprecatedPkiAlertRouter, { prefix: "/alerts" });
await pkiRouter.register(registerPkiCollectionRouter, { prefix: "/collections" }); await pkiRouter.register(registerPkiCollectionRouter, { prefix: "/collections" });
await pkiRouter.register(registerPkiSubscriberRouter, { prefix: "/subscribers" }); await pkiRouter.register(registerPkiSubscriberRouter, { prefix: "/subscribers" });
await pkiRouter.register( await pkiRouter.register(
+291 -53
View File
@@ -1,12 +1,18 @@
import { z } from "zod"; import { z } from "zod";
import { PkiAlertsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ALERTS, ApiDocsTags } from "@app/lib/api-docs"; import { ApiDocsTags } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { PkiAlertEventType } from "@app/services/pki-alert-v2/pki-alert-v2-types"; import {
CreatePkiAlertV2Schema,
createSecureAlertBeforeValidator,
PkiAlertChannelType,
PkiAlertEventType,
PkiFilterRuleSchema,
UpdatePkiAlertV2Schema
} from "@app/services/pki-alert-v2/pki-alert-v2-types";
export const registerPkiAlertRouter = async (server: FastifyZodProvider) => { export const registerPkiAlertRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
@@ -17,25 +23,40 @@ export const registerPkiAlertRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Create a new PKI alert",
tags: [ApiDocsTags.PkiAlerting], tags: [ApiDocsTags.PkiAlerting],
description: "Create PKI alert", body: CreatePkiAlertV2Schema.extend({
body: z.object({ projectId: z.string().uuid().describe("Project ID")
projectId: z.string().trim().describe(ALERTS.CREATE.projectId),
pkiCollectionId: z.string().trim().describe(ALERTS.CREATE.pkiCollectionId),
name: z.string().trim().describe(ALERTS.CREATE.name),
alertBeforeDays: z.number().describe(ALERTS.CREATE.alertBeforeDays),
emails: z
.array(z.string().trim().email({ message: "Invalid email address" }))
.min(1, { message: "You must specify at least 1 email" })
.max(5, { message: "You can specify a maximum of 5 emails" })
.describe(ALERTS.CREATE.emails)
}), }),
response: { response: {
200: PkiAlertsSchema 200: z.object({
alert: z.object({
id: z.string().uuid(),
name: z.string(),
description: z.string().nullable(),
eventType: z.nativeEnum(PkiAlertEventType),
alertBefore: z.string(),
filters: z.array(PkiFilterRuleSchema),
enabled: z.boolean(),
projectId: z.string().uuid(),
channels: z.array(
z.object({
id: z.string().uuid(),
channelType: z.nativeEnum(PkiAlertChannelType),
config: z.record(z.any()),
enabled: z.boolean(),
createdAt: z.date(),
updatedAt: z.date()
})
),
createdAt: z.date(),
updatedAt: z.date()
})
})
} }
}, },
handler: async (req) => { handler: async (req) => {
const alert = await server.services.pkiAlert.createPkiAlert({ const alert = await server.services.pkiAlertV2.createAlert({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
@@ -45,21 +66,79 @@ export const registerPkiAlertRouter = async (server: FastifyZodProvider) => {
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
projectId: alert.projectId, projectId: req.body.projectId,
event: { event: {
type: EventType.CREATE_PKI_ALERT, type: EventType.CREATE_PKI_ALERT,
metadata: { metadata: {
pkiAlertId: alert.id, pkiAlertId: alert.id,
pkiCollectionId: alert.pkiCollectionId,
name: alert.name, name: alert.name,
alertBefore: alert.alertBeforeDays.toString(), eventType: alert.eventType,
eventType: PkiAlertEventType.EXPIRATION, alertBefore: alert.alertBefore
recipientEmails: alert.recipientEmails
} }
} }
}); });
return alert; return { alert };
}
});
server.route({
method: "GET",
url: "/",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "List PKI alerts for a project",
tags: [ApiDocsTags.PkiAlerting],
querystring: z.object({
projectId: z.string().uuid(),
search: z.string().optional(),
eventType: z.nativeEnum(PkiAlertEventType).optional(),
enabled: z.coerce.boolean().optional(),
limit: z.coerce.number().min(1).max(100).default(20),
offset: z.coerce.number().min(0).default(0)
}),
response: {
200: z.object({
alerts: z.array(
z.object({
id: z.string().uuid(),
name: z.string(),
description: z.string().nullable(),
eventType: z.nativeEnum(PkiAlertEventType),
alertBefore: z.string(),
filters: z.array(PkiFilterRuleSchema),
enabled: z.boolean(),
channels: z.array(
z.object({
id: z.string().uuid(),
channelType: z.nativeEnum(PkiAlertChannelType),
config: z.record(z.any()),
enabled: z.boolean(),
createdAt: z.date(),
updatedAt: z.date()
})
),
createdAt: z.date(),
updatedAt: z.date()
})
),
total: z.number()
})
}
},
handler: async (req) => {
const alerts = await server.services.pkiAlertV2.listAlerts({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.query
});
return alerts;
} }
}); });
@@ -71,17 +150,40 @@ export const registerPkiAlertRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Get a PKI alert by ID",
tags: [ApiDocsTags.PkiAlerting], tags: [ApiDocsTags.PkiAlerting],
description: "Get PKI alert",
params: z.object({ params: z.object({
alertId: z.string().trim().describe(ALERTS.GET.alertId) alertId: z.string().uuid().describe("Alert ID")
}), }),
response: { response: {
200: PkiAlertsSchema 200: z.object({
alert: z.object({
id: z.string().uuid(),
name: z.string(),
description: z.string().nullable(),
eventType: z.nativeEnum(PkiAlertEventType),
alertBefore: z.string(),
filters: z.array(PkiFilterRuleSchema),
enabled: z.boolean(),
projectId: z.string().uuid(),
channels: z.array(
z.object({
id: z.string().uuid(),
channelType: z.nativeEnum(PkiAlertChannelType),
config: z.record(z.any()),
enabled: z.boolean(),
createdAt: z.date(),
updatedAt: z.date()
})
),
createdAt: z.date(),
updatedAt: z.date()
})
})
} }
}, },
handler: async (req) => { handler: async (req) => {
const alert = await server.services.pkiAlert.getPkiAlertById({ const alert = await server.services.pkiAlertV2.getAlertById({
alertId: req.params.alertId, alertId: req.params.alertId,
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
@@ -100,7 +202,7 @@ export const registerPkiAlertRouter = async (server: FastifyZodProvider) => {
} }
}); });
return alert; return { alert };
} }
}); });
@@ -108,32 +210,45 @@ export const registerPkiAlertRouter = async (server: FastifyZodProvider) => {
method: "PATCH", method: "PATCH",
url: "/:alertId", url: "/:alertId",
config: { config: {
rateLimit: readLimit rateLimit: writeLimit
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Update a PKI alert",
tags: [ApiDocsTags.PkiAlerting], tags: [ApiDocsTags.PkiAlerting],
description: "Update PKI alert",
params: z.object({ params: z.object({
alertId: z.string().trim().describe(ALERTS.UPDATE.alertId) alertId: z.string().uuid().describe("Alert ID")
}),
body: z.object({
name: z.string().trim().optional().describe(ALERTS.UPDATE.name),
alertBeforeDays: z.number().optional().describe(ALERTS.UPDATE.alertBeforeDays),
pkiCollectionId: z.string().trim().optional().describe(ALERTS.UPDATE.pkiCollectionId),
emails: z
.array(z.string().trim().email({ message: "Invalid email address" }))
.min(1, { message: "You must specify at least 1 email" })
.max(5, { message: "You can specify a maximum of 5 emails" })
.optional()
.describe(ALERTS.UPDATE.emails)
}), }),
body: UpdatePkiAlertV2Schema,
response: { response: {
200: PkiAlertsSchema 200: z.object({
alert: z.object({
id: z.string().uuid(),
name: z.string(),
description: z.string().nullable(),
eventType: z.nativeEnum(PkiAlertEventType),
alertBefore: z.string(),
filters: z.array(PkiFilterRuleSchema),
enabled: z.boolean(),
projectId: z.string().uuid(),
channels: z.array(
z.object({
id: z.string().uuid(),
channelType: z.nativeEnum(PkiAlertChannelType),
config: z.record(z.any()),
enabled: z.boolean(),
createdAt: z.date(),
updatedAt: z.date()
})
),
createdAt: z.date(),
updatedAt: z.date()
})
})
} }
}, },
handler: async (req) => { handler: async (req) => {
const alert = await server.services.pkiAlert.updatePkiAlert({ const alert = await server.services.pkiAlertV2.updateAlert({
alertId: req.params.alertId, alertId: req.params.alertId,
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
@@ -149,16 +264,14 @@ export const registerPkiAlertRouter = async (server: FastifyZodProvider) => {
type: EventType.UPDATE_PKI_ALERT, type: EventType.UPDATE_PKI_ALERT,
metadata: { metadata: {
pkiAlertId: alert.id, pkiAlertId: alert.id,
pkiCollectionId: alert.pkiCollectionId,
name: alert.name, name: alert.name,
alertBefore: alert.alertBeforeDays.toString(), eventType: alert.eventType,
eventType: PkiAlertEventType.EXPIRATION, alertBefore: alert.alertBefore
recipientEmails: alert.recipientEmails
} }
} }
}); });
return alert; return { alert };
} }
}); });
@@ -170,17 +283,40 @@ export const registerPkiAlertRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Delete a PKI alert",
tags: [ApiDocsTags.PkiAlerting], tags: [ApiDocsTags.PkiAlerting],
description: "Delete PKI alert",
params: z.object({ params: z.object({
alertId: z.string().trim().describe(ALERTS.DELETE.alertId) alertId: z.string().uuid().describe("Alert ID")
}), }),
response: { response: {
200: PkiAlertsSchema 200: z.object({
alert: z.object({
id: z.string().uuid(),
name: z.string(),
description: z.string().nullable(),
eventType: z.nativeEnum(PkiAlertEventType),
alertBefore: z.string(),
filters: z.array(PkiFilterRuleSchema),
enabled: z.boolean(),
projectId: z.string().uuid(),
channels: z.array(
z.object({
id: z.string().uuid(),
channelType: z.nativeEnum(PkiAlertChannelType),
config: z.record(z.any()),
enabled: z.boolean(),
createdAt: z.date(),
updatedAt: z.date()
})
),
createdAt: z.date(),
updatedAt: z.date()
})
})
} }
}, },
handler: async (req) => { handler: async (req) => {
const alert = await server.services.pkiAlert.deletePkiAlert({ const alert = await server.services.pkiAlertV2.deleteAlert({
alertId: req.params.alertId, alertId: req.params.alertId,
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
@@ -199,7 +335,109 @@ export const registerPkiAlertRouter = async (server: FastifyZodProvider) => {
} }
}); });
return alert; return { alert };
}
});
server.route({
method: "GET",
url: "/:alertId/certificates",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "List certificates that match an alert's filter rules",
tags: [ApiDocsTags.PkiAlerting],
params: z.object({
alertId: z.string().uuid().describe("Alert ID")
}),
querystring: z.object({
limit: z.coerce.number().min(1).max(100).default(20),
offset: z.coerce.number().min(0).default(0)
}),
response: {
200: z.object({
certificates: z.array(
z.object({
id: z.string().uuid(),
serialNumber: z.string(),
commonName: z.string(),
san: z.array(z.string()),
profileName: z.string().nullable(),
enrollmentType: z.string().nullable(),
notBefore: z.date(),
notAfter: z.date(),
status: z.string()
})
),
total: z.number()
})
}
},
handler: async (req) => {
const result = await server.services.pkiAlertV2.listMatchingCertificates({
alertId: req.params.alertId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.query
});
return result;
}
});
server.route({
method: "POST",
url: "/preview/certificates",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Preview certificates that would match the given filter rules",
tags: [ApiDocsTags.PkiAlerting],
body: z.object({
projectId: z.string().uuid().describe("Project ID"),
filters: z.array(PkiFilterRuleSchema),
alertBefore: z
.string()
.refine(createSecureAlertBeforeValidator(), "Must be in format like '30d', '1w', '3m', '1y'")
.describe("Alert timing (e.g., '30d', '1w')"),
limit: z.coerce.number().min(1).max(100).default(20),
offset: z.coerce.number().min(0).default(0)
}),
response: {
200: z.object({
certificates: z.array(
z.object({
id: z.string().uuid(),
serialNumber: z.string(),
commonName: z.string(),
san: z.array(z.string()),
profileName: z.string().nullable(),
enrollmentType: z.string().nullable(),
notBefore: z.date(),
notAfter: z.date(),
status: z.string()
})
),
total: z.number()
})
}
},
handler: async (req) => {
const result = await server.services.pkiAlertV2.listCurrentMatchingCertificates({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
return result;
} }
}); });
}; };
+1 -1
View File
@@ -1,5 +1,5 @@
import { registerCaRouter } from "./certificate-authority-router"; import { registerCaRouter } from "./certificate-authority-router";
import { registerCertificateTemplatesV2Router } from "./certificate-templates-v2-router"; import { registerCertificateTemplatesV2Router } from "./deprecated-certificate-templates-v2-router";
import { registerDeprecatedGroupProjectRouter } from "./deprecated-group-project-router"; import { registerDeprecatedGroupProjectRouter } from "./deprecated-group-project-router";
import { registerDeprecatedIdentityProjectRouter } from "./deprecated-identity-project-router"; import { registerDeprecatedIdentityProjectRouter } from "./deprecated-identity-project-router";
import { registerDeprecatedProjectMembershipRouter } from "./deprecated-project-membership-router"; import { registerDeprecatedProjectMembershipRouter } from "./deprecated-project-membership-router";
+1 -1
View File
@@ -1,4 +1,4 @@
import { registerCertificatesRouter } from "./certificates-router"; import { registerCertificatesRouter } from "./deprecated-certificates-router";
import { registerDeprecatedSecretRouter } from "./deprecated-secret-router"; import { registerDeprecatedSecretRouter } from "./deprecated-secret-router";
import { registerExternalMigrationRouter } from "./external-migration-router"; import { registerExternalMigrationRouter } from "./external-migration-router";
import { registerLoginRouter } from "./login-router"; import { registerLoginRouter } from "./login-router";
@@ -377,7 +377,6 @@ export const AcmeCertificateAuthorityFns = ({
name, name,
projectId, projectId,
configuration, configuration,
enableDirectIssuance,
actor, actor,
status status
}: { }: {
@@ -385,7 +384,6 @@ export const AcmeCertificateAuthorityFns = ({
name: string; name: string;
projectId: string; projectId: string;
configuration: TCreateAcmeCertificateAuthorityDTO["configuration"]; configuration: TCreateAcmeCertificateAuthorityDTO["configuration"];
enableDirectIssuance: boolean;
actor: OrgServiceActor; actor: OrgServiceActor;
}) => { }) => {
if (crypto.isFipsModeEnabled()) { if (crypto.isFipsModeEnabled()) {
@@ -425,7 +423,7 @@ export const AcmeCertificateAuthorityFns = ({
const ca = await certificateAuthorityDAL.create( const ca = await certificateAuthorityDAL.create(
{ {
projectId, projectId,
enableDirectIssuance, enableDirectIssuance: false,
name, name,
status status
}, },
@@ -473,14 +471,12 @@ export const AcmeCertificateAuthorityFns = ({
id, id,
status, status,
configuration, configuration,
enableDirectIssuance,
actor, actor,
name name
}: { }: {
id: string; id: string;
status?: CaStatus; status?: CaStatus;
configuration: TUpdateAcmeCertificateAuthorityDTO["configuration"]; configuration: TUpdateAcmeCertificateAuthorityDTO["configuration"];
enableDirectIssuance?: boolean;
actor: OrgServiceActor; actor: OrgServiceActor;
name?: string; name?: string;
}) => { }) => {
@@ -541,13 +537,12 @@ export const AcmeCertificateAuthorityFns = ({
); );
} }
if (name || status || enableDirectIssuance) { if (name || status) {
await certificateAuthorityDAL.updateById( await certificateAuthorityDAL.updateById(
id, id,
{ {
name, name,
status, status
enableDirectIssuance
}, },
tx tx
); );
@@ -597,7 +597,6 @@ export const AzureAdCsCertificateAuthorityFns = ({
name, name,
projectId, projectId,
configuration, configuration,
enableDirectIssuance,
actor, actor,
status status
}: { }: {
@@ -605,16 +604,8 @@ export const AzureAdCsCertificateAuthorityFns = ({
name: string; name: string;
projectId: string; projectId: string;
configuration: TCreateAzureAdCsCertificateAuthorityDTO["configuration"]; configuration: TCreateAzureAdCsCertificateAuthorityDTO["configuration"];
enableDirectIssuance: boolean;
actor: OrgServiceActor; actor: OrgServiceActor;
}) => { }) => {
// Azure ADCS does not support direct issuance - enforce this restriction
if (enableDirectIssuance) {
throw new BadRequestError({
message: "Azure ADCS Certificate Authorities do not support direct issuance"
});
}
const { azureAdcsConnectionId } = configuration; const { azureAdcsConnectionId } = configuration;
const appConnection = await appConnectionDAL.findById(azureAdcsConnectionId); const appConnection = await appConnectionDAL.findById(azureAdcsConnectionId);
@@ -679,24 +670,15 @@ export const AzureAdCsCertificateAuthorityFns = ({
id, id,
status, status,
configuration, configuration,
enableDirectIssuance,
actor, actor,
name name
}: { }: {
id: string; id: string;
status?: CaStatus; status?: CaStatus;
configuration: TUpdateAzureAdCsCertificateAuthorityDTO["configuration"]; configuration: TUpdateAzureAdCsCertificateAuthorityDTO["configuration"];
enableDirectIssuance?: boolean;
actor: OrgServiceActor; actor: OrgServiceActor;
name?: string; name?: string;
}) => { }) => {
// Azure ADCS does not support direct issuance - enforce this restriction
if (enableDirectIssuance) {
throw new BadRequestError({
message: "Azure ADCS Certificate Authorities do not support direct issuance"
});
}
const updatedCa = await certificateAuthorityDAL.transaction(async (tx) => { const updatedCa = await certificateAuthorityDAL.transaction(async (tx) => {
if (configuration) { if (configuration) {
const { azureAdcsConnectionId } = configuration; const { azureAdcsConnectionId } = configuration;
@@ -737,13 +719,12 @@ export const AzureAdCsCertificateAuthorityFns = ({
); );
} }
if (name || status || enableDirectIssuance !== undefined) { if (name || status) {
await certificateAuthorityDAL.updateById( await certificateAuthorityDAL.updateById(
id, id,
{ {
name, name,
status, status
enableDirectIssuance: false // Always false for Azure ADCS CAs
}, },
tx tx
); );
@@ -19,14 +19,10 @@ export const GenericCreateCertificateAuthorityFieldsSchema = (type: CaType) =>
z.object({ z.object({
name: slugSchema({ field: "name" }).describe(CertificateAuthorities.CREATE(type).name), name: slugSchema({ field: "name" }).describe(CertificateAuthorities.CREATE(type).name),
projectId: z.string().uuid("Project ID must be valid").describe(CertificateAuthorities.CREATE(type).projectId), projectId: z.string().uuid("Project ID must be valid").describe(CertificateAuthorities.CREATE(type).projectId),
enableDirectIssuance: z.boolean().describe(CertificateAuthorities.CREATE(type).enableDirectIssuance),
status: z.nativeEnum(CaStatus).describe(CertificateAuthorities.CREATE(type).status) status: z.nativeEnum(CaStatus).describe(CertificateAuthorities.CREATE(type).status)
}); });
export const GenericUpdateCertificateAuthorityFieldsSchema = (type: CaType) => export const GenericUpdateCertificateAuthorityFieldsSchema = (type: CaType) =>
z.object({ z.object({
name: slugSchema({ field: "name" }).optional().describe(CertificateAuthorities.UPDATE(type).name),
projectId: z.string().uuid("Project ID must be valid").describe(CertificateAuthorities.UPDATE(type).projectId),
enableDirectIssuance: z.boolean().optional().describe(CertificateAuthorities.UPDATE(type).enableDirectIssuance),
status: z.nativeEnum(CaStatus).optional().describe(CertificateAuthorities.UPDATE(type).status) status: z.nativeEnum(CaStatus).optional().describe(CertificateAuthorities.UPDATE(type).status)
}); });
@@ -123,7 +123,7 @@ export const certificateAuthorityServiceFactory = ({
}); });
const createCertificateAuthority = async ( const createCertificateAuthority = async (
{ type, projectId, name, enableDirectIssuance, configuration, status }: TCreateCertificateAuthorityDTO, { type, projectId, name, configuration, status }: TCreateCertificateAuthorityDTO,
actor: OrgServiceActor actor: OrgServiceActor
) => { ) => {
const { permission } = await permissionService.getProjectPermission({ const { permission } = await permissionService.getProjectPermission({
@@ -145,7 +145,6 @@ export const certificateAuthorityServiceFactory = ({
...(configuration as TCreateInternalCertificateAuthorityDTO["configuration"]), ...(configuration as TCreateInternalCertificateAuthorityDTO["configuration"]),
isInternal: true, isInternal: true,
projectId, projectId,
enableDirectIssuance,
name name
}); });
@@ -171,7 +170,6 @@ export const certificateAuthorityServiceFactory = ({
name, name,
projectId, projectId,
configuration: configuration as TCreateAcmeCertificateAuthorityDTO["configuration"], configuration: configuration as TCreateAcmeCertificateAuthorityDTO["configuration"],
enableDirectIssuance,
status, status,
actor actor
}); });
@@ -182,7 +180,6 @@ export const certificateAuthorityServiceFactory = ({
name, name,
projectId, projectId,
configuration: configuration as TCreateAzureAdCsCertificateAuthorityDTO["configuration"], configuration: configuration as TCreateAzureAdCsCertificateAuthorityDTO["configuration"],
enableDirectIssuance,
status, status,
actor actor
}); });
@@ -191,18 +188,12 @@ export const certificateAuthorityServiceFactory = ({
throw new BadRequestError({ message: "Invalid certificate authority type" }); throw new BadRequestError({ message: "Invalid certificate authority type" });
}; };
const findCertificateAuthorityByNameAndProjectId = async ( const findCertificateAuthorityById = async ({ id, type }: { id: string; type: CaType }, actor: OrgServiceActor) => {
{ caName, type, projectId }: { caName: string; type: CaType; projectId: string }, const certificateAuthority = await certificateAuthorityDAL.findByIdWithAssociatedCa(id);
actor: OrgServiceActor
) => {
const certificateAuthority = await certificateAuthorityDAL.findByNameAndProjectIdWithAssociatedCa(
caName,
projectId
);
if (!certificateAuthority) if (!certificateAuthority)
throw new NotFoundError({ throw new NotFoundError({
message: `Could not find certificate authority with name "${caName}" in project "${projectId}"` message: `Could not find certificate authority with id "${id}"`
}); });
const { permission } = await permissionService.getProjectPermission({ const { permission } = await permissionService.getProjectPermission({
@@ -222,7 +213,7 @@ export const certificateAuthorityServiceFactory = ({
if (type === CaType.INTERNAL) { if (type === CaType.INTERNAL) {
if (!certificateAuthority.internalCa?.id) { if (!certificateAuthority.internalCa?.id) {
throw new NotFoundError({ throw new NotFoundError({
message: `Internal certificate authority with name "${caName}" in project "${projectId}" not found` message: `Internal certificate authority with id "${id}" not found`
}); });
} }
@@ -239,7 +230,7 @@ export const certificateAuthorityServiceFactory = ({
if (certificateAuthority.externalCa?.type !== type) { if (certificateAuthority.externalCa?.type !== type) {
throw new NotFoundError({ throw new NotFoundError({
message: `Could not find external certificate authority with name "${caName}" in project "${projectId}" and type "${type}"` message: `Could not find external certificate authority with id ${id} and type "${type}"`
}); });
} }
@@ -303,17 +294,14 @@ export const certificateAuthorityServiceFactory = ({
}; };
const updateCertificateAuthority = async ( const updateCertificateAuthority = async (
{ caName, type, configuration, enableDirectIssuance, status, name, projectId }: TUpdateCertificateAuthorityDTO, { id, type, configuration, status, name }: TUpdateCertificateAuthorityDTO,
actor: OrgServiceActor actor: OrgServiceActor
) => { ) => {
const certificateAuthority = await certificateAuthorityDAL.findByNameAndProjectIdWithAssociatedCa( const certificateAuthority = await certificateAuthorityDAL.findByIdWithAssociatedCa(id);
caName,
projectId
);
if (!certificateAuthority) if (!certificateAuthority)
throw new NotFoundError({ throw new NotFoundError({
message: `Could not find certificate authority with name "${caName}" in project "${projectId}"` message: `Could not find certificate authority with id "${id}"`
}); });
const { permission } = await permissionService.getProjectPermission({ const { permission } = await permissionService.getProjectPermission({
@@ -333,13 +321,12 @@ export const certificateAuthorityServiceFactory = ({
if (type === CaType.INTERNAL) { if (type === CaType.INTERNAL) {
if (!certificateAuthority.internalCa?.id) { if (!certificateAuthority.internalCa?.id) {
throw new NotFoundError({ throw new NotFoundError({
message: `Internal certificate authority with name "${caName}" in project "${projectId}" not found` message: `Internal certificate authority with id "${id}" not found`
}); });
} }
const updatedCa = await internalCertificateAuthorityService.updateCaById({ const updatedCa = await internalCertificateAuthorityService.updateCaById({
isInternal: true, isInternal: true,
enableDirectIssuance,
caId: certificateAuthority.id, caId: certificateAuthority.id,
status, status,
name name
@@ -366,7 +353,6 @@ export const certificateAuthorityServiceFactory = ({
return acmeFns.updateCertificateAuthority({ return acmeFns.updateCertificateAuthority({
id: certificateAuthority.id, id: certificateAuthority.id,
configuration: configuration as TUpdateAcmeCertificateAuthorityDTO["configuration"], configuration: configuration as TUpdateAcmeCertificateAuthorityDTO["configuration"],
enableDirectIssuance,
actor, actor,
status, status,
name name
@@ -377,7 +363,6 @@ export const certificateAuthorityServiceFactory = ({
return azureAdCsFns.updateCertificateAuthority({ return azureAdCsFns.updateCertificateAuthority({
id: certificateAuthority.id, id: certificateAuthority.id,
configuration: configuration as TUpdateAzureAdCsCertificateAuthorityDTO["configuration"], configuration: configuration as TUpdateAzureAdCsCertificateAuthorityDTO["configuration"],
enableDirectIssuance,
actor, actor,
status, status,
name name
@@ -387,18 +372,12 @@ export const certificateAuthorityServiceFactory = ({
throw new BadRequestError({ message: "Invalid certificate authority type" }); throw new BadRequestError({ message: "Invalid certificate authority type" });
}; };
const deleteCertificateAuthority = async ( const deleteCertificateAuthority = async ({ id, type }: { id: string; type: CaType }, actor: OrgServiceActor) => {
{ caName, type, projectId }: { caName: string; type: CaType; projectId: string }, const certificateAuthority = await certificateAuthorityDAL.findByIdWithAssociatedCa(id);
actor: OrgServiceActor
) => {
const certificateAuthority = await certificateAuthorityDAL.findByNameAndProjectIdWithAssociatedCa(
caName,
projectId
);
if (!certificateAuthority) if (!certificateAuthority)
throw new NotFoundError({ throw new NotFoundError({
message: `Could not find certificate authority with name "${caName}" in project "${projectId}"` message: `Could not find certificate authority with id "${id}"`
}); });
const { permission } = await permissionService.getProjectPermission({ const { permission } = await permissionService.getProjectPermission({
@@ -489,7 +468,7 @@ export const certificateAuthorityServiceFactory = ({
return { return {
createCertificateAuthority, createCertificateAuthority,
findCertificateAuthorityByNameAndProjectId, findCertificateAuthorityById,
listCertificateAuthoritiesByProjectId, listCertificateAuthoritiesByProjectId,
updateCertificateAuthority, updateCertificateAuthority,
deleteCertificateAuthority, deleteCertificateAuthority,
@@ -19,10 +19,9 @@ export type TCertificateAuthorityInput =
| TAcmeCertificateAuthorityInput | TAcmeCertificateAuthorityInput
| TCreateAzureAdCsCertificateAuthorityDTO; | TCreateAzureAdCsCertificateAuthorityDTO;
export type TCreateCertificateAuthorityDTO = Omit<TCertificateAuthority, "id">; export type TCreateCertificateAuthorityDTO = Omit<TCertificateAuthority, "id" | "enableDirectIssuance">;
export type TUpdateCertificateAuthorityDTO = Partial<Omit<TCreateCertificateAuthorityDTO, "projectId">> & { export type TUpdateCertificateAuthorityDTO = Partial<Omit<TCreateCertificateAuthorityDTO, "projectId">> & {
type: CaType; type: CaType;
caName: string; id: string;
projectId: string;
}; };
@@ -136,8 +136,8 @@ export const InternalCertificateAuthorityFns = ({
const caCrl = await certificateAuthorityCrlDAL.findOne({ caSecretId: caSecret.id }); const caCrl = await certificateAuthorityCrlDAL.findOne({ caSecretId: caSecret.id });
const appCfg = getConfig(); const appCfg = getConfig();
const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/pki/crl/${caCrl.id}/der`; const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/cert-manager/crl/${caCrl.id}/der`;
const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/pki/ca/${ca.id}/certificates/${caCert.id}/der`; const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/cert-manager/ca/internal/${ca.id}/certificates/${caCert.id}/der`;
const extensions: x509.Extension[] = [ const extensions: x509.Extension[] = [
new x509.BasicConstraintsExtension(false), new x509.BasicConstraintsExtension(false),
@@ -366,8 +366,8 @@ export const InternalCertificateAuthorityFns = ({
const caCrl = await certificateAuthorityCrlDAL.findOne({ caSecretId: caSecret.id }); const caCrl = await certificateAuthorityCrlDAL.findOne({ caSecretId: caSecret.id });
const appCfg = getConfig(); const appCfg = getConfig();
const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/pki/crl/${caCrl.id}/der`; const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/cert-manager/crl/${caCrl.id}/der`;
const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/pki/ca/${ca.id}/certificates/${caCert.id}/der`; const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/cert-manager/ca/internal/${ca.id}/certificates/${caCert.id}/der`;
const extensions: x509.Extension[] = [ const extensions: x509.Extension[] = [
new x509.BasicConstraintsExtension(false), new x509.BasicConstraintsExtension(false),
@@ -140,7 +140,6 @@ export const internalCertificateAuthorityServiceFactory = ({
notAfter, notAfter,
maxPathLength, maxPathLength,
keyAlgorithm, keyAlgorithm,
enableDirectIssuance,
name, name,
...dto ...dto
}: TCreateCaDTO) => { }: TCreateCaDTO) => {
@@ -192,9 +191,9 @@ export const internalCertificateAuthorityServiceFactory = ({
const ca = await certificateAuthorityDAL.create( const ca = await certificateAuthorityDAL.create(
{ {
projectId, projectId,
enableDirectIssuance,
name: name || slugify(`${(friendlyName || dn).slice(0, 16)}-${alphaNumericNanoId(8)}`), name: name || slugify(`${(friendlyName || dn).slice(0, 16)}-${alphaNumericNanoId(8)}`),
status: type === InternalCaType.ROOT ? CaStatus.ACTIVE : CaStatus.PENDING_CERTIFICATE status: type === InternalCaType.ROOT ? CaStatus.ACTIVE : CaStatus.PENDING_CERTIFICATE,
enableDirectIssuance: false
}, },
tx tx
); );
@@ -354,7 +353,7 @@ export const internalCertificateAuthorityServiceFactory = ({
* Update CA with id [caId]. * Update CA with id [caId].
* Note: Used to enable/disable CA * Note: Used to enable/disable CA
*/ */
const updateCaById = async ({ caId, status, enableDirectIssuance, name, ...dto }: TUpdateCaDTO) => { const updateCaById = async ({ caId, status, name, ...dto }: TUpdateCaDTO) => {
const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(caId); const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(caId);
if (!ca.internalCa) throw new NotFoundError({ message: `CA with ID '${caId}' not found` }); if (!ca.internalCa) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
@@ -375,8 +374,8 @@ export const internalCertificateAuthorityServiceFactory = ({
} }
const updatedCa = await certificateAuthorityDAL.transaction(async (tx) => { const updatedCa = await certificateAuthorityDAL.transaction(async (tx) => {
if (enableDirectIssuance !== undefined || status !== undefined || name !== undefined) { if (status !== undefined || name !== undefined) {
await certificateAuthorityDAL.updateById(ca.id, { enableDirectIssuance, status, name }, tx); await certificateAuthorityDAL.updateById(ca.id, { status, name }, tx);
} }
return certificateAuthorityDAL.findByIdWithAssociatedCa(caId, tx); return certificateAuthorityDAL.findByIdWithAssociatedCa(caId, tx);
@@ -971,9 +970,9 @@ export const internalCertificateAuthorityServiceFactory = ({
const serialNumber = createSerialNumber(); const serialNumber = createSerialNumber();
const caCrl = await certificateAuthorityCrlDAL.findOne({ caSecretId: caSecret.id }); const caCrl = await certificateAuthorityCrlDAL.findOne({ caSecretId: caSecret.id });
const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/pki/crl/${caCrl.id}/der`; const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/cert-manager/crl/${caCrl.id}/der`;
const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/pki/ca/${ca.id}/certificates/${caCert.id}/der`; const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/cert-manager/ca/internal/${ca.id}/certificates/${caCert.id}/der`;
const intermediateCert = await x509.X509CertificateGenerator.create({ const intermediateCert = await x509.X509CertificateGenerator.create({
serialNumber, serialNumber,
subject: csrObj.subject, subject: csrObj.subject,
@@ -1352,8 +1351,8 @@ export const internalCertificateAuthorityServiceFactory = ({
const caCrl = await certificateAuthorityCrlDAL.findOne({ caSecretId: caSecret.id }); const caCrl = await certificateAuthorityCrlDAL.findOne({ caSecretId: caSecret.id });
const appCfg = getConfig(); const appCfg = getConfig();
const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/pki/crl/${caCrl.id}/der`; const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/cert-manager/crl/${caCrl.id}/der`;
const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/pki/ca/${ca.id}/certificates/${caCert.id}/der`; const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/cert-manager/ca/internal/${ca.id}/certificates/${caCert.id}/der`;
const extensions: x509.Extension[] = [ const extensions: x509.Extension[] = [
new x509.BasicConstraintsExtension(false), new x509.BasicConstraintsExtension(false),
@@ -1728,9 +1727,9 @@ export const internalCertificateAuthorityServiceFactory = ({
}); });
const caCrl = await certificateAuthorityCrlDAL.findOne({ caSecretId: caSecret.id }); const caCrl = await certificateAuthorityCrlDAL.findOne({ caSecretId: caSecret.id });
const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/pki/crl/${caCrl.id}/der`; const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/cert-manager/crl/${caCrl.id}/der`;
const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/pki/ca/${ca.id}/certificates/${caCert.id}/der`; const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/cert-manager/ca/internal/${ca.id}/certificates/${caCert.id}/der`;
const extensions: x509.Extension[] = [ const extensions: x509.Extension[] = [
new x509.BasicConstraintsExtension(false), new x509.BasicConstraintsExtension(false),
await x509.AuthorityKeyIdentifierExtension.create(caCertObj, false), await x509.AuthorityKeyIdentifierExtension.create(caCertObj, false),
@@ -48,7 +48,6 @@ export type TCreateCaDTO =
notAfter?: string; notAfter?: string;
maxPathLength?: number | null; maxPathLength?: number | null;
keyAlgorithm: CertKeyAlgorithm; keyAlgorithm: CertKeyAlgorithm;
enableDirectIssuance: boolean;
} }
| ({ | ({
isInternal: false; isInternal: false;
@@ -66,7 +65,6 @@ export type TCreateCaDTO =
notAfter?: string; notAfter?: string;
maxPathLength?: number | null; maxPathLength?: number | null;
keyAlgorithm: CertKeyAlgorithm; keyAlgorithm: CertKeyAlgorithm;
enableDirectIssuance: boolean;
} & Omit<TProjectPermission, "projectId">); } & Omit<TProjectPermission, "projectId">);
export type TGetCaDTO = { export type TGetCaDTO = {
@@ -79,14 +77,12 @@ export type TUpdateCaDTO =
caId: string; caId: string;
name?: string; name?: string;
status?: CaStatus; status?: CaStatus;
enableDirectIssuance?: boolean;
} }
| ({ | ({
isInternal: false; isInternal: false;
caId: string; caId: string;
name?: string; name?: string;
status?: CaStatus; status?: CaStatus;
enableDirectIssuance?: boolean;
} & Omit<TProjectPermission, "projectId">); } & Omit<TProjectPermission, "projectId">);
export type TDeleteCaDTO = { export type TDeleteCaDTO = {
@@ -1190,7 +1190,7 @@ export const certificateV3ServiceFactory = ({
status: CertificateOrderStatus.VALID status: CertificateOrderStatus.VALID
})), })),
authorizations: [], authorizations: [],
finalize: `/api/v3/pki/certificates/orders/${orderId}/completed`, finalize: `/api/v1/cert-manager/certificates/orders/${orderId}/completed`,
certificate: certificateResult.certificate, certificate: certificateResult.certificate,
projectId: certificateResult.projectId, projectId: certificateResult.projectId,
profileName: certificateResult.profileName profileName: certificateResult.profileName
@@ -52,7 +52,10 @@ import {
} from "./certificate-types"; } from "./certificate-types";
type TCertificateServiceFactoryDep = { type TCertificateServiceFactoryDep = {
certificateDAL: Pick<TCertificateDALFactory, "findOne" | "deleteById" | "update" | "find" | "transaction" | "create">; certificateDAL: Pick<
TCertificateDALFactory,
"findOne" | "deleteById" | "update" | "find" | "transaction" | "create" | "findById"
>;
certificateSecretDAL: Pick<TCertificateSecretDALFactory, "findOne" | "create">; certificateSecretDAL: Pick<TCertificateSecretDALFactory, "findOne" | "create">;
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "findOne" | "create">; certificateBodyDAL: Pick<TCertificateBodyDALFactory, "findOne" | "create">;
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById" | "findByIdWithAssociatedCa">; certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById" | "findByIdWithAssociatedCa">;
@@ -91,8 +94,8 @@ export const certificateServiceFactory = ({
/** /**
* Return details for certificate with serial number [serialNumber] * Return details for certificate with serial number [serialNumber]
*/ */
const getCert = async ({ serialNumber, actorId, actorAuthMethod, actor, actorOrgId }: TGetCertDTO) => { const getCert = async ({ id, serialNumber, actorId, actorAuthMethod, actor, actorOrgId }: TGetCertDTO) => {
const cert = await certificateDAL.findOne({ serialNumber }); const cert = id ? await certificateDAL.findById(id) : await certificateDAL.findOne({ serialNumber });
const { permission } = await permissionService.getProjectPermission({ const { permission } = await permissionService.getProjectPermission({
actor, actor,
@@ -117,13 +120,14 @@ export const certificateServiceFactory = ({
* Get certificate private key. * Get certificate private key.
*/ */
const getCertPrivateKey = async ({ const getCertPrivateKey = async ({
id,
serialNumber, serialNumber,
actorId, actorId,
actorAuthMethod, actorAuthMethod,
actor, actor,
actorOrgId actorOrgId
}: TGetCertPrivateKeyDTO) => { }: TGetCertPrivateKeyDTO) => {
const cert = await certificateDAL.findOne({ serialNumber }); const cert = id ? await certificateDAL.findById(id) : await certificateDAL.findOne({ serialNumber });
const { permission } = await permissionService.getProjectPermission({ const { permission } = await permissionService.getProjectPermission({
actor, actor,
@@ -156,8 +160,8 @@ export const certificateServiceFactory = ({
/** /**
* Delete certificate with serial number [serialNumber] * Delete certificate with serial number [serialNumber]
*/ */
const deleteCert = async ({ serialNumber, actorId, actorAuthMethod, actor, actorOrgId }: TDeleteCertDTO) => { const deleteCert = async ({ id, serialNumber, actorId, actorAuthMethod, actor, actorOrgId }: TDeleteCertDTO) => {
const cert = await certificateDAL.findOne({ serialNumber }); const cert = id ? await certificateDAL.findById(id) : await certificateDAL.findOne({ serialNumber });
const { permission } = await permissionService.getProjectPermission({ const { permission } = await permissionService.getProjectPermission({
actor, actor,
@@ -193,6 +197,7 @@ export const certificateServiceFactory = ({
* of its issuing CA * of its issuing CA
*/ */
const revokeCert = async ({ const revokeCert = async ({
id,
serialNumber, serialNumber,
revocationReason, revocationReason,
actorId, actorId,
@@ -200,7 +205,7 @@ export const certificateServiceFactory = ({
actor, actor,
actorOrgId actorOrgId
}: TRevokeCertDTO) => { }: TRevokeCertDTO) => {
const cert = await certificateDAL.findOne({ serialNumber }); const cert = id ? await certificateDAL.findById(id) : await certificateDAL.findOne({ serialNumber });
if (!cert.caId) { if (!cert.caId) {
throw new BadRequestError({ throw new BadRequestError({
@@ -290,8 +295,8 @@ export const certificateServiceFactory = ({
* Return certificate body and certificate chain for certificate with * Return certificate body and certificate chain for certificate with
* serial number [serialNumber] * serial number [serialNumber]
*/ */
const getCertBody = async ({ serialNumber, actorId, actorAuthMethod, actor, actorOrgId }: TGetCertBodyDTO) => { const getCertBody = async ({ id, serialNumber, actorId, actorAuthMethod, actor, actorOrgId }: TGetCertBodyDTO) => {
const cert = await certificateDAL.findOne({ serialNumber }); const cert = id ? await certificateDAL.findById(id) : await certificateDAL.findOne({ serialNumber });
const { permission } = await permissionService.getProjectPermission({ const { permission } = await permissionService.getProjectPermission({
actor, actor,
@@ -584,8 +589,15 @@ export const certificateServiceFactory = ({
* Return certificate body and certificate chain for certificate with * Return certificate body and certificate chain for certificate with
* serial number [serialNumber] * serial number [serialNumber]
*/ */
const getCertBundle = async ({ serialNumber, actorId, actorAuthMethod, actor, actorOrgId }: TGetCertBundleDTO) => { const getCertBundle = async ({
const cert = await certificateDAL.findOne({ serialNumber }); id,
serialNumber,
actorId,
actorAuthMethod,
actor,
actorOrgId
}: TGetCertBundleDTO) => {
const cert = id ? await certificateDAL.findById(id) : await certificateDAL.findOne({ serialNumber });
const { permission } = await permissionService.getProjectPermission({ const { permission } = await permissionService.getProjectPermission({
actor, actor,
@@ -673,12 +685,13 @@ export const certificateServiceFactory = ({
certificate, certificate,
certificateChain, certificateChain,
privateKey, privateKey,
serialNumber, serialNumber: cert.serialNumber,
cert cert
}; };
}; };
const getCertPkcs12 = async ({ const getCertPkcs12 = async ({
id,
serialNumber, serialNumber,
password, password,
alias, alias,
@@ -700,7 +713,7 @@ export const certificateServiceFactory = ({
if (!alias || alias.trim() === "") { if (!alias || alias.trim() === "") {
throw new BadRequestError({ message: "Alias is required for PKCS12 keystore generation" }); throw new BadRequestError({ message: "Alias is required for PKCS12 keystore generation" });
} }
const cert = await certificateDAL.findOne({ serialNumber }); const cert = id ? await certificateDAL.findById(id) : await certificateDAL.findOne({ serialNumber });
const { permission } = await permissionService.getProjectPermission({ const { permission } = await permissionService.getProjectPermission({
actor, actor,
@@ -84,20 +84,24 @@ export enum CrlReason {
} }
export type TGetCertDTO = { export type TGetCertDTO = {
serialNumber: string; id?: string;
serialNumber?: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TDeleteCertDTO = { export type TDeleteCertDTO = {
serialNumber: string; id?: string;
serialNumber?: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TRevokeCertDTO = { export type TRevokeCertDTO = {
serialNumber: string; id?: string;
serialNumber?: string;
revocationReason: CrlReason; revocationReason: CrlReason;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TGetCertBodyDTO = { export type TGetCertBodyDTO = {
serialNumber: string; id?: string;
serialNumber?: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TImportCertDTO = { export type TImportCertDTO = {
@@ -112,15 +116,18 @@ export type TImportCertDTO = {
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TGetCertPrivateKeyDTO = { export type TGetCertPrivateKeyDTO = {
serialNumber: string; id?: string;
serialNumber?: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TGetCertBundleDTO = { export type TGetCertBundleDTO = {
serialNumber: string; id?: string;
serialNumber?: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TGetCertPkcs12DTO = { export type TGetCertPkcs12DTO = {
serialNumber: string; id?: string;
serialNumber?: string;
password: string; password: string;
alias: string; alias: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
@@ -524,8 +524,8 @@ export const pkiSubscriberServiceFactory = ({
}); });
const caCrl = await certificateAuthorityCrlDAL.findOne({ caSecretId: caSecret.id }); const caCrl = await certificateAuthorityCrlDAL.findOne({ caSecretId: caSecret.id });
const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/pki/crl/${caCrl.id}/der`; const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/cert-manager/crl/${caCrl.id}/der`;
const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/pki/ca/${ca.id}/certificates/${caCert.id}/der`; const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/cert-manager/ca/internal/${ca.id}/certificates/${caCert.id}/der`;
const extensions: x509.Extension[] = [ const extensions: x509.Extension[] = [
new x509.BasicConstraintsExtension(false), new x509.BasicConstraintsExtension(false),
@@ -466,8 +466,8 @@ export const pkiTemplatesServiceFactory = ({
}); });
const caCrl = await certificateAuthorityCrlDAL.findOne({ caSecretId: caSecret.id }); const caCrl = await certificateAuthorityCrlDAL.findOne({ caSecretId: caSecret.id });
const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/pki/crl/${caCrl.id}/der`; const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/cert-manager/crl/${caCrl.id}/der`;
const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/pki/ca/${ca.id}/certificates/${caCert.id}/der`; const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/cert-manager/ca/internal/${ca.id}/certificates/${caCert.id}/der`;
const extensions: x509.Extension[] = [ const extensions: x509.Extension[] = [
new x509.BasicConstraintsExtension(false), new x509.BasicConstraintsExtension(false),
+2 -2
View File
@@ -294,8 +294,8 @@ export const ROUTE_PATHS = Object.freeze({
}, },
CertManager: { CertManager: {
CertAuthDetailsByIDPage: setRoute( CertAuthDetailsByIDPage: setRoute(
"/organizations/$orgId/projects/cert-management/$projectId/ca/$caName", "/organizations/$orgId/projects/cert-management/$projectId/ca/$caId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caName" "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caId"
), ),
SubscribersPage: setRoute( SubscribersPage: setRoute(
"/organizations/$orgId/projects/cert-management/$projectId/subscribers", "/organizations/$orgId/projects/cert-management/$projectId/subscribers",
+1 -1
View File
@@ -13,12 +13,12 @@ export {
export { export {
useGetAzureAdcsTemplates, useGetAzureAdcsTemplates,
useGetCa, useGetCa,
useGetCaById,
useGetCaCert, useGetCaCert,
useGetCaCerts, useGetCaCerts,
useGetCaCertTemplates, useGetCaCertTemplates,
useGetCaCrls, useGetCaCrls,
useGetCaCsr, useGetCaCsr,
useGetInternalCaById,
useListCasByProjectId, useListCasByProjectId,
useListCasByTypeAndProjectId, useListCasByTypeAndProjectId,
useListExternalCasByProjectId useListExternalCasByProjectId
+14 -19
View File
@@ -27,21 +27,20 @@ import {
export const useUpdateCa = () => { export const useUpdateCa = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<TUnifiedCertificateAuthority, object, TUpdateCertificateAuthorityDTO>({ return useMutation<TUnifiedCertificateAuthority, object, TUpdateCertificateAuthorityDTO>({
mutationFn: async ({ caName, ...body }) => { mutationFn: async ({ id, ...body }) => {
const { data } = await apiRequest.patch<TUnifiedCertificateAuthority>( const { data } = await apiRequest.patch<TUnifiedCertificateAuthority>(
`/api/v1/pki/ca/${body.type}/${caName}`, `/api/v1/cert-manager/ca/${body.type}/${id}`,
body body
); );
return data; return data;
}, },
onSuccess: ({ projectId, type }, { caName }) => { onSuccess: ({ projectId, type }, { id }) => {
caKeys.getCaByNameAndProjectId(caName, projectId);
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: caKeys.listCasByTypeAndProjectId(type, projectId) queryKey: caKeys.listCasByTypeAndProjectId(type, projectId)
}); });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: caKeys.getCaByNameAndProjectId(caName, projectId) queryKey: caKeys.getCaById(id)
}); });
// Invalidate external CAs list // Invalidate external CAs list
queryClient.invalidateQueries({ queryClient.invalidateQueries({
@@ -56,7 +55,7 @@ export const useCreateCa = () => {
return useMutation<TUnifiedCertificateAuthority, object, TCreateCertificateAuthorityDTO>({ return useMutation<TUnifiedCertificateAuthority, object, TCreateCertificateAuthorityDTO>({
mutationFn: async (body) => { mutationFn: async (body) => {
const { data } = await apiRequest.post<TUnifiedCertificateAuthority>( const { data } = await apiRequest.post<TUnifiedCertificateAuthority>(
`/api/v1/pki/ca/${body.type}`, `/api/v1/cert-manager/ca/${body.type}`,
body body
); );
return data; return data;
@@ -76,14 +75,9 @@ export const useCreateCa = () => {
export const useDeleteCa = () => { export const useDeleteCa = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<TUnifiedCertificateAuthority, object, TDeleteCertificateAuthorityDTO>({ return useMutation<TUnifiedCertificateAuthority, object, TDeleteCertificateAuthorityDTO>({
mutationFn: async ({ caName, type, projectId }) => { mutationFn: async ({ id, type }) => {
const { data } = await apiRequest.delete<TUnifiedCertificateAuthority>( const { data } = await apiRequest.delete<TUnifiedCertificateAuthority>(
`/api/v1/pki/ca/${type}/${caName}`, `/api/v1/cert-manager/ca/${type}/${id}`
{
data: {
projectId
}
}
); );
return data; return data;
}, },
@@ -104,7 +98,7 @@ export const useSignIntermediate = () => {
return useMutation<TSignIntermediateResponse, object, TSignIntermediateDTO>({ return useMutation<TSignIntermediateResponse, object, TSignIntermediateDTO>({
mutationFn: async (body) => { mutationFn: async (body) => {
const { data } = await apiRequest.post<TSignIntermediateResponse>( const { data } = await apiRequest.post<TSignIntermediateResponse>(
`/api/v1/pki/ca/${body.caId}/sign-intermediate`, `/api/v1/cert-manager/ca/internal/${body.caId}/sign-intermediate`,
body body
); );
return data; return data;
@@ -117,13 +111,14 @@ export const useImportCaCertificate = (projectId: string) => {
return useMutation<TImportCaCertificateResponse, object, TImportCaCertificateDTO>({ return useMutation<TImportCaCertificateResponse, object, TImportCaCertificateDTO>({
mutationFn: async ({ caId, ...body }) => { mutationFn: async ({ caId, ...body }) => {
const { data } = await apiRequest.post<TImportCaCertificateResponse>( const { data } = await apiRequest.post<TImportCaCertificateResponse>(
`/api/v1/pki/ca/${caId}/import-certificate`, `/api/v1/cert-manager/ca/internal/${caId}/import-certificate`,
body body
); );
return data; return data;
}, },
onSuccess: (_, { caId }) => { onSuccess: (_, { caId }) => {
queryClient.invalidateQueries({ queryKey: projectKeys.getProjectCas({ projectId }) }); queryClient.invalidateQueries({ queryKey: projectKeys.getProjectCas({ projectId }) });
queryClient.invalidateQueries({ queryKey: caKeys.getCaById(caId) });
queryClient.invalidateQueries({ queryKey: caKeys.getCaCerts(caId) }); queryClient.invalidateQueries({ queryKey: caKeys.getCaCerts(caId) });
queryClient.invalidateQueries({ queryKey: caKeys.getCaCert(caId) }); queryClient.invalidateQueries({ queryKey: caKeys.getCaCert(caId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
@@ -133,7 +128,7 @@ export const useImportCaCertificate = (projectId: string) => {
}); });
}; };
// consider rename to issue certificate // TODO: DEPRECATE
export const useCreateCertificate = () => { export const useCreateCertificate = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<TCreateCertificateResponse, object, TCreateCertificateDTO>({ return useMutation<TCreateCertificateResponse, object, TCreateCertificateDTO>({
@@ -157,7 +152,7 @@ export const useCreateCertificateV3 = (options?: { projectId?: string }) => {
return useMutation<TCreateCertificateV3Response, object, TCreateCertificateV3DTO>({ return useMutation<TCreateCertificateV3Response, object, TCreateCertificateV3DTO>({
mutationFn: async (body) => { mutationFn: async (body) => {
const { data } = await apiRequest.post<TCreateCertificateV3Response>( const { data } = await apiRequest.post<TCreateCertificateV3Response>(
"/api/v3/pki/certificates/issue-certificate", "/api/v1/cert-manager/certificates/issue-certificate",
body body
); );
return data; return data;
@@ -185,7 +180,7 @@ export const useOrderCertificateWithProfile = () => {
return useMutation<TOrderCertificateResponse, object, TOrderCertificateDTO>({ return useMutation<TOrderCertificateResponse, object, TOrderCertificateDTO>({
mutationFn: async (body) => { mutationFn: async (body) => {
const { data } = await apiRequest.post<TOrderCertificateResponse>( const { data } = await apiRequest.post<TOrderCertificateResponse>(
"/api/v3/pki/certificates/order-certificate", "/api/v1/cert-manager/certificates/order-certificate",
body body
); );
return data; return data;
@@ -203,7 +198,7 @@ export const useRenewCa = () => {
return useMutation<TRenewCaResponse, object, TRenewCaDTO>({ return useMutation<TRenewCaResponse, object, TRenewCaDTO>({
mutationFn: async (body) => { mutationFn: async (body) => {
const { data } = await apiRequest.post<TRenewCaResponse>( const { data } = await apiRequest.post<TRenewCaResponse>(
`/api/v1/pki/ca/${body.caId}/renew`, `/api/v1/cert-manager/ca/internal/${body.caId}/renew`,
body body
); );
return data; return data;
+24 -27
View File
@@ -4,7 +4,11 @@ import { apiRequest } from "@app/config/request";
import { TCertificateTemplate } from "../certificateTemplates/types"; import { TCertificateTemplate } from "../certificateTemplates/types";
import { CaType } from "./enums"; import { CaType } from "./enums";
import { TAzureAdCsTemplate, TCertificateAuthority, TUnifiedCertificateAuthority } from "./types"; import {
TAzureAdCsTemplate,
TInternalCertificateAuthority,
TUnifiedCertificateAuthority
} from "./types";
export const caKeys = { export const caKeys = {
getCaById: (caId: string) => [{ caId }, "ca"], getCaById: (caId: string) => [{ caId }, "ca"],
@@ -25,24 +29,16 @@ export const caKeys = {
] ]
}; };
export const useGetCa = ({ export const useGetCa = ({ caId, type }: { caId: string; type: CaType }) => {
caName,
projectId,
type
}: {
caName: string;
projectId: string;
type: CaType;
}) => {
return useQuery({ return useQuery({
queryKey: caKeys.getCaByNameAndProjectId(caName, projectId), queryKey: caKeys.getCaById(caId),
queryFn: async () => { queryFn: async () => {
const { data } = await apiRequest.get<TUnifiedCertificateAuthority>( const { data } = await apiRequest.get<TUnifiedCertificateAuthority>(
`/api/v1/pki/ca/${type}/${caName}?projectId=${projectId}` `/api/v1/cert-manager/ca/${type}/${caId}`
); );
return data; return data;
}, },
enabled: Boolean(caName && projectId && type) enabled: Boolean(caId && type)
}); });
}; };
@@ -51,7 +47,7 @@ export const useListCasByTypeAndProjectId = (type: CaType, projectId: string) =>
queryKey: caKeys.listCasByTypeAndProjectId(type, projectId), queryKey: caKeys.listCasByTypeAndProjectId(type, projectId),
queryFn: async () => { queryFn: async () => {
const { data } = await apiRequest.get<TUnifiedCertificateAuthority[]>( const { data } = await apiRequest.get<TUnifiedCertificateAuthority[]>(
`/api/v1/pki/ca/${type}?projectId=${projectId}` `/api/v1/cert-manager/ca/${type}?projectId=${projectId}`
); );
return data; return data;
@@ -65,7 +61,7 @@ export const useListCasByProjectId = (projectId: string) => {
queryFn: async () => { queryFn: async () => {
const { data } = await apiRequest.get<{ const { data } = await apiRequest.get<{
certificateAuthorities: TUnifiedCertificateAuthority[]; certificateAuthorities: TUnifiedCertificateAuthority[];
}>(`/api/v2/pki/ca?projectId=${projectId}`); }>(`/api/v1/cert-manager/ca?projectId=${projectId}`);
return data.certificateAuthorities; return data.certificateAuthorities;
} }
@@ -78,10 +74,10 @@ export const useListExternalCasByProjectId = (projectId: string) => {
queryFn: async () => { queryFn: async () => {
const [acmeResponse, azureAdCsResponse] = await Promise.allSettled([ const [acmeResponse, azureAdCsResponse] = await Promise.allSettled([
apiRequest.get<TUnifiedCertificateAuthority[]>( apiRequest.get<TUnifiedCertificateAuthority[]>(
`/api/v1/pki/ca/${CaType.ACME}?projectId=${projectId}` `/api/v1/cert-manager/ca/${CaType.ACME}?projectId=${projectId}`
), ),
apiRequest.get<TUnifiedCertificateAuthority[]>( apiRequest.get<TUnifiedCertificateAuthority[]>(
`/api/v1/pki/ca/${CaType.AZURE_AD_CS}?projectId=${projectId}` `/api/v1/cert-manager/ca/${CaType.AZURE_AD_CS}?projectId=${projectId}`
) )
]); ]);
@@ -100,14 +96,14 @@ export const useListExternalCasByProjectId = (projectId: string) => {
}); });
}; };
export const useGetCaById = (caId: string) => { export const useGetInternalCaById = (caId: string) => {
return useQuery({ return useQuery({
queryKey: caKeys.getCaById(caId), queryKey: caKeys.getCaById(caId),
queryFn: async () => { queryFn: async () => {
const { const { data } = await apiRequest.get<TInternalCertificateAuthority>(
data: { ca } `/api/v1/cert-manager/ca/internal/${caId}`
} = await apiRequest.get<{ ca: TCertificateAuthority }>(`/api/v1/pki/ca/${caId}`); );
return ca; return data;
}, },
enabled: Boolean(caId) enabled: Boolean(caId)
}); });
@@ -124,7 +120,7 @@ export const useGetCaCerts = (caId: string) => {
serialNumber: string; serialNumber: string;
version: number; version: number;
}[] }[]
>(`/api/v1/pki/ca/${caId}/ca-certificates`); // TODO: consider updating endpoint structure >(`/api/v1/cert-manager/ca/internal/${caId}/ca-certificates`);
return data; return data;
}, },
enabled: Boolean(caId) enabled: Boolean(caId)
@@ -139,7 +135,7 @@ export const useGetCaCert = (caId: string) => {
certificate: string; certificate: string;
certificateChain: string; certificateChain: string;
serialNumber: string; serialNumber: string;
}>(`/api/v1/pki/ca/${caId}/certificate`); // TODO: consider updating endpoint structure }>(`/api/v1/cert-manager/ca/internal/${caId}/certificate`);
return data; return data;
}, },
enabled: Boolean(caId) enabled: Boolean(caId)
@@ -154,7 +150,7 @@ export const useGetCaCsr = (caId: string) => {
data: { csr } data: { csr }
} = await apiRequest.get<{ } = await apiRequest.get<{
csr: string; csr: string;
}>(`/api/v1/pki/ca/${caId}/csr`); }>(`/api/v1/cert-manager/ca/internal/${caId}/csr`);
return csr; return csr;
}, },
enabled: Boolean(caId) enabled: Boolean(caId)
@@ -170,13 +166,14 @@ export const useGetCaCrls = (caId: string) => {
id: string; id: string;
crl: string; crl: string;
}[] }[]
>(`/api/v1/pki/ca/${caId}/crls`); >(`/api/v1/cert-manager/ca/internal/${caId}/crls`);
return data; return data;
}, },
enabled: Boolean(caId) enabled: Boolean(caId)
}); });
}; };
// TODO: DEPRECATE
export const useGetCaCertTemplates = (caId: string) => { export const useGetCaCertTemplates = (caId: string) => {
return useQuery({ return useQuery({
queryKey: caKeys.getCaCertTemplates(caId), queryKey: caKeys.getCaCertTemplates(caId),
@@ -202,7 +199,7 @@ export const useGetAzureAdcsTemplates = ({
queryFn: async () => { queryFn: async () => {
const { data } = await apiRequest.get<{ const { data } = await apiRequest.get<{
templates: TAzureAdCsTemplate[]; templates: TAzureAdCsTemplate[];
}>(`/api/v1/pki/ca/azure-ad-cs/${caId}/templates?projectId=${projectId}`); }>(`/api/v1/cert-manager/ca/azure-ad-cs/${caId}/templates?projectId=${projectId}`);
return data; return data;
}, },
enabled: Boolean(caId && projectId) enabled: Boolean(caId && projectId)
+3 -4
View File
@@ -68,15 +68,14 @@ export type TUnifiedCertificateAuthority =
export type TCreateCertificateAuthorityDTO = Omit<TUnifiedCertificateAuthority, "id">; export type TCreateCertificateAuthorityDTO = Omit<TUnifiedCertificateAuthority, "id">;
export type TUpdateCertificateAuthorityDTO = Partial<TUnifiedCertificateAuthority> & { export type TUpdateCertificateAuthorityDTO = Partial<TUnifiedCertificateAuthority> & {
caName: string; id: string;
projectId: string;
type: CaType; type: CaType;
}; };
export type TDeleteCertificateAuthorityDTO = { export type TDeleteCertificateAuthorityDTO = {
caName: string; id: string;
type: CaType;
projectId: string; projectId: string;
type: CaType;
}; };
export type TCertificateAuthority = { export type TCertificateAuthority = {
@@ -17,7 +17,7 @@ export const useCreateCertificateProfile = () => {
mutationFn: async (data) => { mutationFn: async (data) => {
const { data: response } = await apiRequest.post<{ const { data: response } = await apiRequest.post<{
certificateProfile: TCertificateProfile; certificateProfile: TCertificateProfile;
}>("/api/v1/pki/certificate-profiles", data); }>("/api/v1/cert-manager/certificate-profiles", data);
return response.certificateProfile; return response.certificateProfile;
}, },
onSuccess: (_, { projectId }) => { onSuccess: (_, { projectId }) => {
@@ -35,7 +35,7 @@ export const useUpdateCertificateProfile = () => {
mutationFn: async ({ profileId, ...data }) => { mutationFn: async ({ profileId, ...data }) => {
const { data: response } = await apiRequest.patch<{ const { data: response } = await apiRequest.patch<{
certificateProfile: TCertificateProfile; certificateProfile: TCertificateProfile;
}>(`/api/v1/pki/certificate-profiles/${profileId}`, data); }>(`/api/v1/cert-manager/certificate-profiles/${profileId}`, data);
return response.certificateProfile; return response.certificateProfile;
}, },
onSuccess: (profile, { profileId }) => { onSuccess: (profile, { profileId }) => {
@@ -56,7 +56,7 @@ export const useDeleteCertificateProfile = () => {
mutationFn: async ({ profileId }) => { mutationFn: async ({ profileId }) => {
const { data: response } = await apiRequest.delete<{ const { data: response } = await apiRequest.delete<{
certificateProfile: TCertificateProfile; certificateProfile: TCertificateProfile;
}>(`/api/v1/pki/certificate-profiles/${profileId}`); }>(`/api/v1/cert-manager/certificate-profiles/${profileId}`);
return response.certificateProfile; return response.certificateProfile;
}, },
onSuccess: (profile, { profileId }) => { onSuccess: (profile, { profileId }) => {
@@ -71,7 +71,7 @@ export const useListCertificateProfiles = ({
const { data } = await apiRequest.get<{ const { data } = await apiRequest.get<{
certificateProfiles: TCertificateProfile[]; certificateProfiles: TCertificateProfile[];
totalCount: number; totalCount: number;
}>("/api/v1/pki/certificate-profiles", { }>("/api/v1/cert-manager/certificate-profiles", {
params: { params: {
projectId, projectId,
limit, limit,
@@ -93,7 +93,7 @@ export const useGetCertificateProfileById = ({ profileId }: TGetCertificateProfi
queryFn: async () => { queryFn: async () => {
const { data } = await apiRequest.get<{ const { data } = await apiRequest.get<{
certificateProfile: TCertificateProfileWithDetails; certificateProfile: TCertificateProfileWithDetails;
}>(`/api/v1/pki/certificate-profiles/${profileId}`); }>(`/api/v1/cert-manager/certificate-profiles/${profileId}`);
return data.certificateProfile; return data.certificateProfile;
}, },
enabled: Boolean(profileId) enabled: Boolean(profileId)
@@ -109,7 +109,7 @@ export const useGetCertificateProfileBySlug = ({
queryFn: async () => { queryFn: async () => {
const { data } = await apiRequest.get<{ const { data } = await apiRequest.get<{
certificateProfile: TCertificateProfile; certificateProfile: TCertificateProfile;
}>(`/api/v1/pki/certificate-profiles/slug/${slug}`, { }>(`/api/v1/cert-manager/certificate-profiles/slug/${slug}`, {
params: { projectId } params: { projectId }
}); });
return data.certificateProfile; return data.certificateProfile;
@@ -125,7 +125,7 @@ export const useRevealAcmeEabSecret = ({ profileId }: TRevealAcmeEabSecretDTO) =
const { data } = await apiRequest.get<{ const { data } = await apiRequest.get<{
eabKid: string; eabKid: string;
eabSecret: string; eabSecret: string;
}>(`/api/v1/pki/certificate-profiles/${profileId}/acme/eab-secret/reveal`); }>(`/api/v1/cert-manager/certificate-profiles/${profileId}/acme/eab-secret/reveal`);
return data; return data;
}, },
enabled: Boolean(profileId) enabled: Boolean(profileId)
@@ -144,7 +144,7 @@ export const useGetProfileCertificates = ({
queryFn: async () => { queryFn: async () => {
const { data } = await apiRequest.get<{ const { data } = await apiRequest.get<{
certificates: TProfileCertificate[]; certificates: TProfileCertificate[];
}>(`/api/v1/pki/certificate-profiles/${profileId}/certificates`, { }>(`/api/v1/cert-manager/certificate-profiles/${profileId}/certificates`, {
params: { params: {
offset, offset,
limit, limit,
@@ -21,6 +21,7 @@ import {
TUpdateEstConfigDTO TUpdateEstConfigDTO
} from "./types"; } from "./types";
// TODO: DEPRECATE
export const useCreateCertTemplate = () => { export const useCreateCertTemplate = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<TCertificateTemplate, object, TCreateCertificateTemplateDTO>({ return useMutation<TCertificateTemplate, object, TCreateCertificateTemplateDTO>({
@@ -40,6 +41,7 @@ export const useCreateCertTemplate = () => {
}); });
}; };
// TODO: DEPRECATE
export const useUpdateCertTemplate = () => { export const useUpdateCertTemplate = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<TCertificateTemplate, object, TUpdateCertificateTemplateDTO>({ return useMutation<TCertificateTemplate, object, TUpdateCertificateTemplateDTO>({
@@ -61,6 +63,7 @@ export const useUpdateCertTemplate = () => {
}); });
}; };
// TODO: DEPRECATE
export const useDeleteCertTemplate = () => { export const useDeleteCertTemplate = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<TCertificateTemplate, object, TDeleteCertificateTemplateDTO>({ return useMutation<TCertificateTemplate, object, TDeleteCertificateTemplateDTO>({
@@ -86,7 +89,7 @@ export const useCreateCertTemplateV2 = () => {
mutationFn: async (dto) => { mutationFn: async (dto) => {
const { data } = await apiRequest.post<{ const { data } = await apiRequest.post<{
certificateTemplate: TCertificateTemplate; certificateTemplate: TCertificateTemplate;
}>("/api/v2/pki/certificate-templates", dto); }>("/api/v1/cert-manager/certificate-templates", dto);
return data.certificateTemplate; return data.certificateTemplate;
}, },
onSuccess: (_, { projectId }) => { onSuccess: (_, { projectId }) => {
@@ -105,7 +108,7 @@ export const useUpdateCertTemplateV2 = () => {
return useMutation<TCertificateTemplate, object, TUpdateCertificateTemplateV2DTO>({ return useMutation<TCertificateTemplate, object, TUpdateCertificateTemplateV2DTO>({
mutationFn: async (dto) => { mutationFn: async (dto) => {
const { data } = await apiRequest.patch<{ certificateTemplate: TCertificateTemplate }>( const { data } = await apiRequest.patch<{ certificateTemplate: TCertificateTemplate }>(
`/api/v2/pki/certificate-templates/${dto.templateName}`, `/api/v1/cert-manager/certificate-templates/${dto.templateName}`,
dto dto
); );
@@ -127,7 +130,7 @@ export const useDeleteCertTemplateV2 = () => {
return useMutation<TCertificateTemplate, object, TDeleteCertificateTemplateV2DTO>({ return useMutation<TCertificateTemplate, object, TDeleteCertificateTemplateV2DTO>({
mutationFn: async (dto) => { mutationFn: async (dto) => {
const { data } = await apiRequest.delete<{ certificateTemplate: TCertificateTemplate }>( const { data } = await apiRequest.delete<{ certificateTemplate: TCertificateTemplate }>(
`/api/v2/pki/certificate-templates/${dto.templateName}`, `/api/v1/cert-manager/certificate-templates/${dto.templateName}`,
{ {
data: { data: {
projectId: dto.projectId projectId: dto.projectId
@@ -147,6 +150,7 @@ export const useDeleteCertTemplateV2 = () => {
}); });
}; };
// TODO: DEPRECATE
export const useCreateEstConfig = () => { export const useCreateEstConfig = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<object, object, TCreateEstConfigDTO>({ return useMutation<object, object, TCreateEstConfigDTO>({
@@ -165,6 +169,7 @@ export const useCreateEstConfig = () => {
}); });
}; };
// TODO: DEPRECATE
export const useUpdateEstConfig = () => { export const useUpdateEstConfig = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<object, object, TUpdateEstConfigDTO>({ return useMutation<object, object, TUpdateEstConfigDTO>({
@@ -193,7 +198,7 @@ export const useCreateCertificateTemplateV2WithPolicies = () => {
mutationFn: async (data) => { mutationFn: async (data) => {
const { data: response } = await apiRequest.post<{ const { data: response } = await apiRequest.post<{
certificateTemplate: TCertificateTemplateV2WithPolicies; certificateTemplate: TCertificateTemplateV2WithPolicies;
}>("/api/v2/certificate-templates", data); }>("/api/v1/cert-manager/certificate-templates", data);
return response.certificateTemplate; return response.certificateTemplate;
}, },
onSuccess: (_, { projectId }) => { onSuccess: (_, { projectId }) => {
@@ -214,7 +219,7 @@ export const useUpdateCertificateTemplateV2WithPolicies = () => {
mutationFn: async ({ templateId, ...data }) => { mutationFn: async ({ templateId, ...data }) => {
const { data: response } = await apiRequest.patch<{ const { data: response } = await apiRequest.patch<{
certificateTemplate: TCertificateTemplateV2WithPolicies; certificateTemplate: TCertificateTemplateV2WithPolicies;
}>(`/api/v2/certificate-templates/${templateId}`, data); }>(`/api/v1/cert-manager/certificate-templates/${templateId}`, data);
return response.certificateTemplate; return response.certificateTemplate;
}, },
onSuccess: (template, { templateId }) => { onSuccess: (template, { templateId }) => {
@@ -238,7 +243,7 @@ export const useDeleteCertificateTemplateV2WithPolicies = () => {
mutationFn: async ({ templateId }) => { mutationFn: async ({ templateId }) => {
const { data: response } = await apiRequest.delete<{ const { data: response } = await apiRequest.delete<{
certificateTemplate: TCertificateTemplateV2WithPolicies; certificateTemplate: TCertificateTemplateV2WithPolicies;
}>(`/api/v2/certificate-templates/${templateId}`); }>(`/api/v1/cert-manager/certificate-templates/${templateId}`);
return response.certificateTemplate; return response.certificateTemplate;
}, },
onSuccess: (template, { templateId }) => { onSuccess: (template, { templateId }) => {
@@ -31,6 +31,7 @@ export const certTemplateKeys = {
getTemplateV2ById: (id: string) => ["cert-template-v2", id] getTemplateV2ById: (id: string) => ["cert-template-v2", id]
}; };
// TODO: DEPRECATE
export const useGetCertTemplate = (id: string) => { export const useGetCertTemplate = (id: string) => {
return useQuery({ return useQuery({
queryKey: certTemplateKeys.getCertTemplateById(id), queryKey: certTemplateKeys.getCertTemplateById(id),
@@ -44,6 +45,7 @@ export const useGetCertTemplate = (id: string) => {
}); });
}; };
// TODO: DEPRECATE
export const useListCertificateTemplates = ({ export const useListCertificateTemplates = ({
limit = 100, limit = 100,
offset = 0, offset = 0,
@@ -55,7 +57,7 @@ export const useListCertificateTemplates = ({
const { data } = await apiRequest.get<{ const { data } = await apiRequest.get<{
certificateTemplates: TCertificateTemplateV2[]; certificateTemplates: TCertificateTemplateV2[];
totalCount?: number; totalCount?: number;
}>("/api/v2/pki/certificate-templates", { }>("/api/v1/pki/certificate-templates", {
params: { params: {
limit, limit,
offset, offset,
@@ -67,6 +69,7 @@ export const useListCertificateTemplates = ({
}); });
}; };
// TODO: DEPRECATE
export const useGetEstConfig = (certificateTemplateId: string) => { export const useGetEstConfig = (certificateTemplateId: string) => {
return useQuery({ return useQuery({
queryKey: certTemplateKeys.getEstConfig(certificateTemplateId), queryKey: certTemplateKeys.getEstConfig(certificateTemplateId),
@@ -92,7 +95,7 @@ export const useListCertificateTemplatesV2 = ({
const { data } = await apiRequest.get<{ const { data } = await apiRequest.get<{
certificateTemplates: TCertificateTemplateV2WithPolicies[]; certificateTemplates: TCertificateTemplateV2WithPolicies[];
totalCount: number; totalCount: number;
}>("/api/v2/certificate-templates", { }>("/api/v1/cert-manager/certificate-templates", {
params: { params: {
projectId, projectId,
limit, limit,
@@ -113,7 +116,7 @@ export const useGetCertificateTemplateV2ById = ({
queryFn: async () => { queryFn: async () => {
const { data } = await apiRequest.get<{ const { data } = await apiRequest.get<{
certificateTemplate: TCertificateTemplateV2WithPolicies; certificateTemplate: TCertificateTemplateV2WithPolicies;
}>(`/api/v2/certificate-templates/${templateId}`); }>(`/api/v1/cert-manager/certificate-templates/${templateId}`);
return data.certificateTemplate; return data.certificateTemplate;
}, },
enabled: Boolean(templateId) enabled: Boolean(templateId)
@@ -19,11 +19,11 @@ import {
export const useDeleteCert = () => { export const useDeleteCert = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<TCertificate, object, TDeleteCertDTO>({ return useMutation<TCertificate, object, TDeleteCertDTO>({
mutationFn: async ({ serialNumber }) => { mutationFn: async ({ id }) => {
const { const {
data: { certificate } data: { certificate }
} = await apiRequest.delete<{ certificate: TCertificate }>( } = await apiRequest.delete<{ certificate: TCertificate }>(
`/api/v1/pki/certificates/${serialNumber}` `/api/v1/cert-manager/certificates/${id}`
); );
return certificate; return certificate;
}, },
@@ -47,11 +47,11 @@ export const useDeleteCert = () => {
export const useRevokeCert = () => { export const useRevokeCert = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<TCertificate, object, TRevokeCertDTO>({ return useMutation<TCertificate, object, TRevokeCertDTO>({
mutationFn: async ({ serialNumber, revocationReason }) => { mutationFn: async ({ id, revocationReason }) => {
const { const {
data: { certificate } data: { certificate }
} = await apiRequest.post<{ certificate: TCertificate }>( } = await apiRequest.post<{ certificate: TCertificate }>(
`/api/v1/pki/certificates/${serialNumber}/revoke`, `/api/v1/cert-manager/certificates/${id}/revoke`,
{ {
revocationReason revocationReason
} }
@@ -80,7 +80,7 @@ export const useImportCertificate = () => {
return useMutation<TImportCertificateResponse, object, TImportCertificateDTO>({ return useMutation<TImportCertificateResponse, object, TImportCertificateDTO>({
mutationFn: async (body) => { mutationFn: async (body) => {
const { data } = await apiRequest.post<TImportCertificateResponse>( const { data } = await apiRequest.post<TImportCertificateResponse>(
"/api/v1/pki/certificates/import-certificate", "/api/v1/cert-manager/certificates/import-certificate",
body body
); );
return data; return data;
@@ -98,7 +98,7 @@ export const useRenewCertificate = () => {
return useMutation<TRenewCertificateResponse, object, TRenewCertificateDTO>({ return useMutation<TRenewCertificateResponse, object, TRenewCertificateDTO>({
mutationFn: async ({ certificateId }) => { mutationFn: async ({ certificateId }) => {
const { data } = await apiRequest.post<TRenewCertificateResponse>( const { data } = await apiRequest.post<TRenewCertificateResponse>(
`/api/v3/pki/certificates/${certificateId}/renew`, `/api/v1/cert-manager/certificates/${certificateId}/renew`,
{} {}
); );
return data; return data;
@@ -131,7 +131,7 @@ export const useUpdateRenewalConfig = () => {
>({ >({
mutationFn: async ({ certificateId, renewBeforeDays, enableAutoRenewal }) => { mutationFn: async ({ certificateId, renewBeforeDays, enableAutoRenewal }) => {
const { data } = await apiRequest.patch<{ message: string; renewBeforeDays?: number }>( const { data } = await apiRequest.patch<{ message: string; renewBeforeDays?: number }>(
`/api/v3/pki/certificates/${certificateId}/config`, `/api/v1/cert-manager/certificates/${certificateId}/config`,
{ renewBeforeDays, enableAutoRenewal } { renewBeforeDays, enableAutoRenewal }
); );
return data; return data;
@@ -152,7 +152,7 @@ export const useDownloadCertPkcs12 = () => {
mutationFn: async ({ serialNumber, projectSlug, password, alias }) => { mutationFn: async ({ serialNumber, projectSlug, password, alias }) => {
try { try {
const response = await apiRequest.post( const response = await apiRequest.post(
`/api/v1/pki/certificates/${serialNumber}/pkcs12`, `/api/v1/cert-manager/certificates/${serialNumber}/pkcs12`,
{ {
password, password,
alias alias
+2 -2
View File
@@ -24,13 +24,13 @@ export type TCertificate = {
}; };
export type TDeleteCertDTO = { export type TDeleteCertDTO = {
id: string;
projectId: string; projectId: string;
serialNumber: string;
}; };
export type TRevokeCertDTO = { export type TRevokeCertDTO = {
projectId: string; projectId: string;
serialNumber: string; id: string;
revocationReason: string; revocationReason: string;
}; };
@@ -6,6 +6,7 @@ import { projectKeys } from "../projects";
import { pkiAlertKeys } from "./queries"; import { pkiAlertKeys } from "./queries";
import { TCreatePkiAlertDTO, TDeletePkiAlertDTO, TPkiAlert, TUpdatePkiAlertDTO } from "./types"; import { TCreatePkiAlertDTO, TDeletePkiAlertDTO, TPkiAlert, TUpdatePkiAlertDTO } from "./types";
// TODO: DEPRECATE
export const useCreatePkiAlert = () => { export const useCreatePkiAlert = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<TPkiAlert, object, TCreatePkiAlertDTO>({ return useMutation<TPkiAlert, object, TCreatePkiAlertDTO>({
@@ -19,6 +20,7 @@ export const useCreatePkiAlert = () => {
}); });
}; };
// TODO: DEPRECATE
export const useUpdatePkiAlert = () => { export const useUpdatePkiAlert = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<TPkiAlert, object, TUpdatePkiAlertDTO>({ return useMutation<TPkiAlert, object, TUpdatePkiAlertDTO>({
@@ -36,6 +38,7 @@ export const useUpdatePkiAlert = () => {
}); });
}; };
// TODO: DEPRECATE
export const useDeletePkiAlert = () => { export const useDeletePkiAlert = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<TPkiAlert, object, TDeletePkiAlertDTO>({ return useMutation<TPkiAlert, object, TDeletePkiAlertDTO>({
@@ -8,6 +8,7 @@ export const pkiAlertKeys = {
getPkiAlertById: (alertId: string) => [{ alertId }, "alert"] getPkiAlertById: (alertId: string) => [{ alertId }, "alert"]
}; };
// TODO: DEPRECATE
export const useGetPkiAlertById = (alertId: string) => { export const useGetPkiAlertById = (alertId: string) => {
return useQuery({ return useQuery({
queryKey: pkiAlertKeys.getPkiAlertById(alertId), queryKey: pkiAlertKeys.getPkiAlertById(alertId),
@@ -11,7 +11,7 @@ export const useCreatePkiAlertV2 = () => {
return useMutation<TPkiAlertV2, unknown, TCreatePkiAlertV2>({ return useMutation<TPkiAlertV2, unknown, TCreatePkiAlertV2>({
mutationFn: async (data) => { mutationFn: async (data) => {
const { data: response } = await apiRequest.post<{ alert: TPkiAlertV2 }>( const { data: response } = await apiRequest.post<{ alert: TPkiAlertV2 }>(
"/api/v2/pki/alerts", "/api/v1/cert-manager/alerts",
data data
); );
return response.alert; return response.alert;
@@ -30,7 +30,7 @@ export const useUpdatePkiAlertV2 = () => {
return useMutation<TPkiAlertV2, unknown, TUpdatePkiAlertV2>({ return useMutation<TPkiAlertV2, unknown, TUpdatePkiAlertV2>({
mutationFn: async ({ alertId, ...data }) => { mutationFn: async ({ alertId, ...data }) => {
const { data: response } = await apiRequest.patch<{ alert: TPkiAlertV2 }>( const { data: response } = await apiRequest.patch<{ alert: TPkiAlertV2 }>(
`/api/v2/pki/alerts/${alertId}`, `/api/v1/cert-manager/alerts/${alertId}`,
data data
); );
return response.alert; return response.alert;
@@ -52,7 +52,7 @@ export const useDeletePkiAlertV2 = () => {
return useMutation<TPkiAlertV2, unknown, TDeletePkiAlertV2>({ return useMutation<TPkiAlertV2, unknown, TDeletePkiAlertV2>({
mutationFn: async ({ alertId }) => { mutationFn: async ({ alertId }) => {
const { data } = await apiRequest.delete<{ alert: TPkiAlertV2 }>( const { data } = await apiRequest.delete<{ alert: TPkiAlertV2 }>(
`/api/v2/pki/alerts/${alertId}` `/api/v1/cert-manager/alerts/${alertId}`
); );
return data.alert; return data.alert;
}, },
@@ -24,14 +24,16 @@ export const pkiAlertsV2Keys = {
}; };
const fetchPkiAlertsV2 = async (params: TGetPkiAlertsV2): Promise<TGetPkiAlertsV2Response> => { const fetchPkiAlertsV2 = async (params: TGetPkiAlertsV2): Promise<TGetPkiAlertsV2Response> => {
const { data } = await apiRequest.get<TGetPkiAlertsV2Response>("/api/v2/pki/alerts", { const { data } = await apiRequest.get<TGetPkiAlertsV2Response>("/api/v1/cert-manager/alerts", {
params params
}); });
return data; return data;
}; };
const fetchPkiAlertV2ById = async ({ alertId }: TGetPkiAlertV2ById): Promise<TPkiAlertV2> => { const fetchPkiAlertV2ById = async ({ alertId }: TGetPkiAlertV2ById): Promise<TPkiAlertV2> => {
const { data } = await apiRequest.get<{ alert: TPkiAlertV2 }>(`/api/v2/pki/alerts/${alertId}`); const { data } = await apiRequest.get<{ alert: TPkiAlertV2 }>(
`/api/v1/cert-manager/alerts/${alertId}`
);
return data.alert; return data.alert;
}; };
@@ -40,7 +42,7 @@ const fetchPkiAlertV2MatchingCertificates = async (
): Promise<TGetPkiAlertV2MatchingCertificatesResponse> => { ): Promise<TGetPkiAlertV2MatchingCertificatesResponse> => {
const { alertId, ...queryParams } = params; const { alertId, ...queryParams } = params;
const { data } = await apiRequest.get<TGetPkiAlertV2MatchingCertificatesResponse>( const { data } = await apiRequest.get<TGetPkiAlertV2MatchingCertificatesResponse>(
`/api/v2/pki/alerts/${alertId}/certificates`, `/api/v1/cert-manager/alerts/${alertId}/certificates`,
{ params: queryParams } { params: queryParams }
); );
return data; return data;
@@ -50,7 +52,7 @@ const fetchPkiAlertV2CurrentMatchingCertificates = async (
params: TGetPkiAlertV2CurrentMatchingCertificates params: TGetPkiAlertV2CurrentMatchingCertificates
): Promise<TGetPkiAlertV2CurrentMatchingCertificatesResponse> => { ): Promise<TGetPkiAlertV2CurrentMatchingCertificatesResponse> => {
const { data } = await apiRequest.post<TGetPkiAlertV2CurrentMatchingCertificatesResponse>( const { data } = await apiRequest.post<TGetPkiAlertV2CurrentMatchingCertificatesResponse>(
"/api/v2/pki/alerts/preview/certificates", "/api/v1/cert-manager/alerts/preview/certificates",
params params
); );
return data; return data;
+27 -11
View File
@@ -17,7 +17,10 @@ export const useCreatePkiSync = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: async ({ destination, ...params }: TCreatePkiSyncDTO) => { mutationFn: async ({ destination, ...params }: TCreatePkiSyncDTO) => {
const { data } = await apiRequest.post<TPkiSync>(`/api/v1/pki/syncs/${destination}`, params); const { data } = await apiRequest.post<TPkiSync>(
`/api/v1/cert-manager/syncs/${destination}`,
params
);
return data; return data;
}, },
@@ -31,7 +34,7 @@ export const useUpdatePkiSync = () => {
return useMutation({ return useMutation({
mutationFn: async ({ syncId, projectId, destination, ...params }: TUpdatePkiSyncDTO) => { mutationFn: async ({ syncId, projectId, destination, ...params }: TUpdatePkiSyncDTO) => {
const { data } = await apiRequest.patch<TPkiSync>( const { data } = await apiRequest.patch<TPkiSync>(
`/api/v1/pki/syncs/${destination}/${syncId}`, `/api/v1/cert-manager/syncs/${destination}/${syncId}`,
params, params,
{ params: { projectId } } { params: { projectId } }
); );
@@ -49,9 +52,12 @@ export const useDeletePkiSync = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: async ({ syncId, projectId, destination }: TDeletePkiSyncDTO) => { mutationFn: async ({ syncId, projectId, destination }: TDeletePkiSyncDTO) => {
const { data } = await apiRequest.delete(`/api/v1/pki/syncs/${destination}/${syncId}`, { const { data } = await apiRequest.delete(
`/api/v1/cert-manager/syncs/${destination}/${syncId}`,
{
params: { projectId } params: { projectId }
}); }
);
return data; return data;
}, },
@@ -66,7 +72,9 @@ export const useTriggerPkiSyncSyncCertificates = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: async ({ syncId, destination }: TTriggerPkiSyncSyncCertificatesDTO) => { mutationFn: async ({ syncId, destination }: TTriggerPkiSyncSyncCertificatesDTO) => {
const { data } = await apiRequest.post(`/api/v1/pki/syncs/${destination}/${syncId}/sync`); const { data } = await apiRequest.post(
`/api/v1/cert-manager/syncs/${destination}/${syncId}/sync`
);
return data; return data;
}, },
@@ -111,7 +119,9 @@ export const useTriggerPkiSyncImportCertificates = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: async ({ syncId, destination }: TTriggerPkiSyncImportCertificatesDTO) => { mutationFn: async ({ syncId, destination }: TTriggerPkiSyncImportCertificatesDTO) => {
const { data } = await apiRequest.post(`/api/v1/pki/syncs/${destination}/${syncId}/import`); const { data } = await apiRequest.post(
`/api/v1/cert-manager/syncs/${destination}/${syncId}/import`
);
return data; return data;
}, },
@@ -157,7 +167,7 @@ export const useTriggerPkiSyncRemoveCertificates = () => {
return useMutation({ return useMutation({
mutationFn: async ({ syncId, destination }: TTriggerPkiSyncRemoveCertificatesDTO) => { mutationFn: async ({ syncId, destination }: TTriggerPkiSyncRemoveCertificatesDTO) => {
const { data } = await apiRequest.post( const { data } = await apiRequest.post(
`/api/v1/pki/syncs/${destination}/${syncId}/remove-certificates` `/api/v1/cert-manager/syncs/${destination}/${syncId}/remove-certificates`
); );
return data; return data;
@@ -209,9 +219,12 @@ export const useAddCertificatesToPkiSync = () => {
pkiSyncId: string; pkiSyncId: string;
certificateIds: string[]; certificateIds: string[];
}) => { }) => {
const { data } = await apiRequest.post(`/api/v1/pki/syncs/${pkiSyncId}/certificates`, { const { data } = await apiRequest.post(
`/api/v1/cert-manager/syncs/${pkiSyncId}/certificates`,
{
certificateIds certificateIds
}); }
);
return data; return data;
}, },
@@ -231,9 +244,12 @@ export const useRemoveCertificatesFromPkiSync = () => {
pkiSyncId: string; pkiSyncId: string;
certificateIds: string[]; certificateIds: string[];
}) => { }) => {
const { data } = await apiRequest.delete(`/api/v1/pki/syncs/${pkiSyncId}/certificates`, { const { data } = await apiRequest.delete(
`/api/v1/cert-manager/syncs/${pkiSyncId}/certificates`,
{
data: { certificateIds } data: { certificateIds }
}); }
);
return data; return data;
}, },
+6 -4
View File
@@ -37,7 +37,9 @@ export const usePkiSyncOptions = (
return useQuery({ return useQuery({
queryKey: pkiSyncKeys.options(), queryKey: pkiSyncKeys.options(),
queryFn: async () => { queryFn: async () => {
const { data } = await apiRequest.get<TListPkiSyncOptions>("/api/v1/pki/syncs/options"); const { data } = await apiRequest.get<TListPkiSyncOptions>(
"/api/v1/cert-manager/syncs/options"
);
return data.pkiSyncOptions; return data.pkiSyncOptions;
}, },
@@ -58,7 +60,7 @@ export const fetchPkiSyncsByProjectId = async (projectId: string, certificateId?
params.certificateId = certificateId; params.certificateId = certificateId;
} }
const { data } = await apiRequest.get<TListPkiSyncs>("/api/v1/pki/syncs", { const { data } = await apiRequest.get<TListPkiSyncs>("/api/v1/cert-manager/syncs", {
params params
}); });
@@ -110,7 +112,7 @@ export const useGetPkiSync = (
return useQuery({ return useQuery({
queryKey: pkiSyncKeys.byId(syncId, projectId), queryKey: pkiSyncKeys.byId(syncId, projectId),
queryFn: async () => { queryFn: async () => {
const { data } = await apiRequest.get<TPkiSync>(`/api/v1/pki/syncs/${syncId}`, { const { data } = await apiRequest.get<TPkiSync>(`/api/v1/cert-manager/syncs/${syncId}`, {
params: { projectId } params: { projectId }
}); });
@@ -138,7 +140,7 @@ export const useListPkiSyncCertificates = (
return useQuery({ return useQuery({
queryKey: pkiSyncKeys.certificates(syncId, { offset, limit }), queryKey: pkiSyncKeys.certificates(syncId, { offset, limit }),
queryFn: async () => { queryFn: async () => {
const { data } = await apiRequest.get(`/api/v1/pki/syncs/${syncId}/certificates`, { const { data } = await apiRequest.get(`/api/v1/cert-manager/syncs/${syncId}/certificates`, {
params: { offset, limit } params: { offset, limit }
}); });
return { return {
@@ -44,10 +44,9 @@ const Page = () => {
const params = useParams({ const params = useParams({
from: ROUTE_PATHS.CertManager.CertAuthDetailsByIDPage.id from: ROUTE_PATHS.CertManager.CertAuthDetailsByIDPage.id
}); });
const { caName } = params as { caName: string }; const { caId } = params as { caId: string };
const { data } = useGetCa({ const { data } = useGetCa({
caName, caId,
projectId: currentProject?.id || "",
type: CaType.INTERNAL type: CaType.INTERNAL
}) as { data: TInternalCertificateAuthority }; }) as { data: TInternalCertificateAuthority };
@@ -66,7 +65,7 @@ const Page = () => {
if (!currentProject?.slug) return; if (!currentProject?.slug) return;
await deleteCa({ await deleteCa({
caName, id: data.id,
projectId: currentProject.id, projectId: currentProject.id,
type: CaType.INTERNAL type: CaType.INTERNAL
}); });
@@ -138,7 +137,7 @@ const Page = () => {
</PageHeader> </PageHeader>
<div className="flex"> <div className="flex">
<div className="mr-4 w-96"> <div className="mr-4 w-96">
<CaDetailsSection caName={data.name} handlePopUpOpen={handlePopUpOpen} /> <CaDetailsSection caId={data.id} handlePopUpOpen={handlePopUpOpen} />
</div> </div>
<div className="w-full"> <div className="w-full">
<CaCertificatesSection caId={data.id} /> <CaCertificatesSection caId={data.id} />
@@ -51,7 +51,7 @@ export const CaCrlsTable = ({ caId }: Props) => {
<Tr key={`ca-crl-${id}`}> <Tr key={`ca-crl-${id}`}>
<Td> <Td>
<div className="flex items-center"> <div className="flex items-center">
{`${window.origin}/api/v1/pki/crl/${id}`} {`${window.origin}/api/v1/cert-manager/crl/${id}`}
</div> </div>
</Td> </Td>
{/* <Td>{format(new Date(caCrlObj.thisUpdate), "yyyy-MM-dd")}</Td> */} {/* <Td>{format(new Date(caCrlObj.thisUpdate), "yyyy-MM-dd")}</Td> */}
@@ -4,7 +4,7 @@ import { format } from "date-fns";
import { ProjectPermissionCan } from "@app/components/permissions"; import { ProjectPermissionCan } from "@app/components/permissions";
import { Button, IconButton, Tooltip } from "@app/components/v2"; import { Button, IconButton, Tooltip } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useProject } from "@app/context"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { useTimedReset } from "@app/hooks"; import { useTimedReset } from "@app/hooks";
import { CaStatus, CaType, InternalCaType, useGetCa } from "@app/hooks/api"; import { CaStatus, CaType, InternalCaType, useGetCa } from "@app/hooks/api";
import { caStatusToNameMap, caTypeToNameMap } from "@app/hooks/api/ca/constants"; import { caStatusToNameMap, caTypeToNameMap } from "@app/hooks/api/ca/constants";
@@ -13,15 +13,14 @@ import { certKeyAlgorithmToNameMap } from "@app/hooks/api/certificates/constants
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
type Props = { type Props = {
caName: string; caId: string;
handlePopUpOpen: ( handlePopUpOpen: (
popUpName: keyof UsePopUpState<["ca", "renewCa", "installCaCert"]>, popUpName: keyof UsePopUpState<["ca", "renewCa", "installCaCert"]>,
data?: object data?: object
) => void; ) => void;
}; };
export const CaDetailsSection = ({ caName, handlePopUpOpen }: Props) => { export const CaDetailsSection = ({ caId, handlePopUpOpen }: Props) => {
const { currentProject } = useProject();
const [copyTextId, isCopyingId, setCopyTextId] = useTimedReset<string>({ const [copyTextId, isCopyingId, setCopyTextId] = useTimedReset<string>({
initialState: "Copy ID to clipboard" initialState: "Copy ID to clipboard"
}); });
@@ -30,8 +29,7 @@ export const CaDetailsSection = ({ caName, handlePopUpOpen }: Props) => {
}); });
const { data } = useGetCa({ const { data } = useGetCa({
caName, caId,
projectId: currentProject.id,
type: CaType.INTERNAL type: CaType.INTERNAL
}); });
@@ -53,7 +51,7 @@ export const CaDetailsSection = ({ caName, handlePopUpOpen }: Props) => {
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handlePopUpOpen("ca", { handlePopUpOpen("ca", {
name: ca.name caId: ca.id
}); });
}} }}
> >
@@ -3,7 +3,7 @@ import { createFileRoute, linkOptions } from "@tanstack/react-router";
import { CertAuthDetailsByIDPage } from "./CertAuthDetailsByIDPage"; import { CertAuthDetailsByIDPage } from "./CertAuthDetailsByIDPage";
export const Route = createFileRoute( export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caName" "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caId"
)({ )({
component: CertAuthDetailsByIDPage, component: CertAuthDetailsByIDPage,
beforeLoad: ({ context, params }) => { beforeLoad: ({ context, params }) => {
@@ -21,7 +21,7 @@ export const Route = createFileRoute(
}) })
}, },
{ {
label: params.caName label: params.caId
} }
] ]
}; };
@@ -9,8 +9,8 @@ import { Button, FormControl, Input, Select, SelectItem } from "@app/components/
import { useProject } from "@app/context"; import { useProject } from "@app/context";
import { import {
CaStatus, CaStatus,
useGetCaById,
useGetCaCsr, useGetCaCsr,
useGetInternalCaById,
useImportCaCertificate, useImportCaCertificate,
useListWorkspaceCas, useListWorkspaceCas,
useSignIntermediate useSignIntermediate
@@ -51,7 +51,7 @@ export const InternalCaInstallForm = ({ caId, handlePopUpToggle }: Props) => {
projectId: currentProject.id, projectId: currentProject.id,
status: CaStatus.ACTIVE status: CaStatus.ACTIVE
}); });
const { data: ca } = useGetCaById(caId); const { data: ca } = useGetInternalCaById(caId);
const { data: csr } = useGetCaCsr(caId); const { data: csr } = useGetCaCsr(caId);
const { mutateAsync: signIntermediate } = useSignIntermediate(); const { mutateAsync: signIntermediate } = useSignIntermediate();
@@ -83,18 +83,21 @@ export const InternalCaInstallForm = ({ caId, handlePopUpToggle }: Props) => {
const parentCaId = watch("parentCaId"); const parentCaId = watch("parentCaId");
const { data: parentCa } = useGetCaById(parentCaId); const { data: parentCa } = useGetInternalCaById(parentCaId);
useEffect(() => { useEffect(() => {
if (parentCa?.maxPathLength) { if (parentCa?.configuration.maxPathLength) {
setValue( setValue(
"maxPathLength", "maxPathLength",
(parentCa.maxPathLength === -1 ? 3 : parentCa.maxPathLength - 1).toString() (parentCa.configuration.maxPathLength === -1
? 3
: parentCa.configuration.maxPathLength - 1
).toString()
); );
} }
if (parentCa?.notAfter) { if (parentCa?.configuration.notAfter) {
const parentCaNotAfter = new Date(parentCa.notAfter); const parentCaNotAfter = new Date(parentCa.configuration.notAfter);
const middleDate = getMiddleDate(new Date(), parentCaNotAfter); const middleDate = getMiddleDate(new Date(), parentCaNotAfter);
setValue("notAfter", format(middleDate, "yyyy-MM-dd")); setValue("notAfter", format(middleDate, "yyyy-MM-dd"));
} }
@@ -197,7 +200,7 @@ export const InternalCaInstallForm = ({ caId, handlePopUpToggle }: Props) => {
onValueChange={onChange} onValueChange={onChange}
className="w-full" className="w-full"
> >
{generatePathLengthOpts(parentCa?.maxPathLength || 0).map((value) => ( {generatePathLengthOpts(parentCa?.configuration.maxPathLength || 0).map((value) => (
<SelectItem value={String(value)} key={`ca-path-length-${value}`}> <SelectItem value={String(value)} key={`ca-path-length-${value}`}>
{`${value}`} {`${value}`}
</SelectItem> </SelectItem>
@@ -80,8 +80,7 @@ const caTypes = [
export const CaModal = ({ popUp, handlePopUpToggle }: Props) => { export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
const { currentProject } = useProject(); const { currentProject } = useProject();
const { data: ca } = useGetCa({ const { data: ca } = useGetCa({
caName: (popUp?.ca?.data as { name: string })?.name || "", caId: (popUp?.ca?.data as { caId: string })?.caId || "",
projectId: currentProject?.id || "",
type: CaType.INTERNAL type: CaType.INTERNAL
}); });
@@ -180,8 +179,7 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
if (ca) { if (ca) {
// update // update
await updateMutateAsync({ await updateMutateAsync({
caName: ca.name, id: ca.id,
projectId: currentProject.id,
name, name,
type: CaType.INTERNAL, type: CaType.INTERNAL,
status, status,
@@ -26,10 +26,10 @@ export const CaSection = () => {
"caStatus" // enable / disable "caStatus" // enable / disable
] as const); ] as const);
const onRemoveCaSubmit = async (caName: string) => { const onRemoveCaSubmit = async (id: string) => {
if (!currentProject?.slug) return; if (!currentProject?.slug) return;
await deleteCa({ caName, projectId: currentProject.id, type: CaType.INTERNAL }); await deleteCa({ id, projectId: currentProject.id, type: CaType.INTERNAL });
createNotification({ createNotification({
text: "Successfully deleted CA", text: "Successfully deleted CA",
@@ -39,10 +39,10 @@ export const CaSection = () => {
handlePopUpClose("deleteCa"); handlePopUpClose("deleteCa");
}; };
const onUpdateCaStatus = async ({ caName, status }: { caName: string; status: CaStatus }) => { const onUpdateCaStatus = async ({ caId, status }: { caId: string; status: CaStatus }) => {
if (!currentProject?.slug) return; if (!currentProject?.slug) return;
await updateCa({ caName, projectId: currentProject.id, type: CaType.INTERNAL, status }); await updateCa({ id: caId, type: CaType.INTERNAL, status });
createNotification({ createNotification({
text: `Successfully ${status === CaStatus.ACTIVE ? "enabled" : "disabled"} CA`, text: `Successfully ${status === CaStatus.ACTIVE ? "enabled" : "disabled"} CA`,
@@ -85,9 +85,7 @@ export const CaSection = () => {
subTitle="This action will delete other CAs and certificates below it in your CA hierarchy." subTitle="This action will delete other CAs and certificates below it in your CA hierarchy."
onChange={(isOpen) => handlePopUpToggle("deleteCa", isOpen)} onChange={(isOpen) => handlePopUpToggle("deleteCa", isOpen)}
deleteKey="confirm" deleteKey="confirm"
onDeleteApproved={() => onDeleteApproved={() => onRemoveCaSubmit((popUp?.deleteCa?.data as { caId: string })?.caId)}
onRemoveCaSubmit((popUp?.deleteCa?.data as { caName: string })?.caName)
}
/> />
<DeleteActionModal <DeleteActionModal
isOpen={popUp.caStatus.isOpen} isOpen={popUp.caStatus.isOpen}
@@ -104,7 +102,7 @@ export const CaSection = () => {
onChange={(isOpen) => handlePopUpToggle("caStatus", isOpen)} onChange={(isOpen) => handlePopUpToggle("caStatus", isOpen)}
deleteKey="confirm" deleteKey="confirm"
onDeleteApproved={() => onDeleteApproved={() =>
onUpdateCaStatus(popUp?.caStatus?.data as { caName: string; status: CaStatus }) onUpdateCaStatus(popUp?.caStatus?.data as { caId: string; status: CaStatus })
} }
/> />
</div> </div>
@@ -81,11 +81,11 @@ export const CaTable = ({ handlePopUpOpen }: Props) => {
key={`ca-${ca.id}`} key={`ca-${ca.id}`}
onClick={() => onClick={() =>
navigate({ navigate({
to: "/organizations/$orgId/projects/cert-management/$projectId/ca/$caName", to: "/organizations/$orgId/projects/cert-management/$projectId/ca/$caId",
params: { params: {
orgId: currentOrg.id, orgId: currentOrg.id,
projectId: currentProject.id, projectId: currentProject.id,
caName: ca.name caId: ca.id
} }
}) })
} }
@@ -180,7 +180,7 @@ export const CaTable = ({ handlePopUpOpen }: Props) => {
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handlePopUpOpen("caStatus", { handlePopUpOpen("caStatus", {
caName: ca.name, caId: ca.id,
status: status:
ca.status === CaStatus.ACTIVE ca.status === CaStatus.ACTIVE
? CaStatus.DISABLED ? CaStatus.DISABLED
@@ -207,7 +207,7 @@ export const CaTable = ({ handlePopUpOpen }: Props) => {
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handlePopUpOpen("deleteCa", { handlePopUpOpen("deleteCa", {
caName: ca.name caId: ca.id
}); });
}} }}
disabled={!isAllowed} disabled={!isAllowed}
@@ -131,8 +131,7 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
const { currentProject } = useProject(); const { currentProject } = useProject();
const { data: ca, isLoading: isCaLoading } = useGetCa({ const { data: ca, isLoading: isCaLoading } = useGetCa({
caName: (popUp?.ca?.data as { name: string })?.name || "", caId: (popUp?.ca?.data as { caId: string })?.caId || "",
projectId: currentProject?.id || "",
type: (popUp?.ca?.data as { type: CaType })?.type || "" type: (popUp?.ca?.data as { type: CaType })?.type || ""
}); });
@@ -320,7 +319,7 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
if (ca) { if (ca) {
await updateMutateAsync({ await updateMutateAsync({
caName: ca.name, id: ca.id,
projectId: currentProject.id, projectId: currentProject.id,
name, name,
type, type,
@@ -22,10 +22,10 @@ export const ExternalCaSection = () => {
"caStatus" // enable / disable "caStatus" // enable / disable
] as const); ] as const);
const onRemoveCaSubmit = async (caName: string, type: CaType) => { const onRemoveCaSubmit = async (id: string, type: CaType) => {
if (!currentProject?.id) return; if (!currentProject?.id) return;
await deleteCa({ caName, type, projectId: currentProject.id }); await deleteCa({ id, type, projectId: currentProject.id });
createNotification({ createNotification({
text: "Successfully deleted CA", text: "Successfully deleted CA",
@@ -36,17 +36,17 @@ export const ExternalCaSection = () => {
}; };
const onUpdateCaStatus = async ({ const onUpdateCaStatus = async ({
name, caId,
type, type,
status status
}: { }: {
name: string; caId: string;
type: CaType; type: CaType;
status: CaStatus; status: CaStatus;
}) => { }) => {
if (!currentProject?.slug) return; if (!currentProject?.slug) return;
await updateCa({ caName: name, type, status, projectId: currentProject.id }); await updateCa({ id: caId, type, status });
createNotification({ createNotification({
text: `Successfully ${status === CaStatus.ACTIVE ? "enabled" : "disabled"} CA`, text: `Successfully ${status === CaStatus.ACTIVE ? "enabled" : "disabled"} CA`,
@@ -88,7 +88,7 @@ export const ExternalCaSection = () => {
deleteKey="confirm" deleteKey="confirm"
onDeleteApproved={() => onDeleteApproved={() =>
onRemoveCaSubmit( onRemoveCaSubmit(
(popUp?.deleteCa?.data as { name: string })?.name, (popUp?.deleteCa?.data as { caId: string })?.caId,
(popUp?.deleteCa?.data as { type: CaType })?.type (popUp?.deleteCa?.data as { type: CaType })?.type
) )
} }
@@ -110,7 +110,7 @@ export const ExternalCaSection = () => {
deleteKey="confirm" deleteKey="confirm"
onDeleteApproved={() => onDeleteApproved={() =>
onUpdateCaStatus( onUpdateCaStatus(
popUp?.caStatus?.data as { name: string; type: CaType; status: CaStatus } popUp?.caStatus?.data as { caId: string; type: CaType; status: CaStatus }
) )
} }
/> />
@@ -35,6 +35,7 @@ type Props = {
handlePopUpOpen: ( handlePopUpOpen: (
popUpName: keyof UsePopUpState<["ca", "deleteCa", "caStatus"]>, popUpName: keyof UsePopUpState<["ca", "deleteCa", "caStatus"]>,
data?: { data?: {
caId?: string;
name?: string; name?: string;
type?: CaType; type?: CaType;
status?: CaStatus; status?: CaStatus;
@@ -70,6 +71,7 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
key={`ca-${ca.id}`} key={`ca-${ca.id}`}
onClick={() => { onClick={() => {
handlePopUpOpen("ca", { handlePopUpOpen("ca", {
caId: ca.id,
name: ca.name, name: ca.name,
type: ca.type type: ca.type
}); });
@@ -104,6 +106,7 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handlePopUpOpen("ca", { handlePopUpOpen("ca", {
caId: ca.id,
name: ca.name, name: ca.name,
type: ca.type type: ca.type
}); });
@@ -129,7 +132,7 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handlePopUpOpen("caStatus", { handlePopUpOpen("caStatus", {
name: ca.name, caId: ca.id,
type: ca.type, type: ca.type,
status: status:
ca.status === CaStatus.ACTIVE ca.status === CaStatus.ACTIVE
@@ -157,7 +160,7 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handlePopUpOpen("deleteCa", { handlePopUpOpen("deleteCa", {
name: ca.name, caId: ca.id,
type: ca.type type: ca.type
}); });
}} }}
@@ -50,11 +50,11 @@ export const CertificateRevocationModal = ({ popUp, handlePopUpToggle }: Props)
const onFormSubmit = async ({ revocationReason }: FormData) => { const onFormSubmit = async ({ revocationReason }: FormData) => {
if (!currentProject?.slug) return; if (!currentProject?.slug) return;
const { serialNumber } = popUp.revokeCertificate.data as { serialNumber: string }; const { certificateId } = popUp.revokeCertificate.data as { certificateId: string };
await revokeCertificate({ await revokeCertificate({
projectId: currentProject.id, projectId: currentProject.id,
serialNumber, id: certificateId,
revocationReason revocationReason
}); });
@@ -26,8 +26,8 @@ import { useProject } from "@app/context";
import { import {
CaStatus, CaStatus,
useCreateCertTemplate, useCreateCertTemplate,
useGetCaById,
useGetCertTemplate, useGetCertTemplate,
useGetInternalCaById,
useListWorkspaceCas, useListWorkspaceCas,
useListWorkspacePkiCollections, useListWorkspacePkiCollections,
useUpdateCertTemplate useUpdateCertTemplate
@@ -84,7 +84,7 @@ type Props = {
export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Props) => { export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Props) => {
const { currentProject } = useProject(); const { currentProject } = useProject();
const { data: ca } = useGetCaById(caId); const { data: ca } = useGetInternalCaById(caId);
const { data: certTemplate } = useGetCertTemplate( const { data: certTemplate } = useGetCertTemplate(
(popUp?.certificateTemplate?.data as { id: string })?.id || "" (popUp?.certificateTemplate?.data as { id: string })?.id || ""
@@ -39,11 +39,11 @@ export const CertificatesSection = () => {
"managePkiSyncs" "managePkiSyncs"
] as const); ] as const);
const onRemoveCertificateSubmit = async (serialNumber: string) => { const onRemoveCertificateSubmit = async (id: string) => {
if (!currentProject?.slug) return; if (!currentProject?.slug) return;
await deleteCert({ await deleteCert({
serialNumber, id,
projectId: currentProject.id projectId: currentProject.id
}); });
@@ -150,7 +150,7 @@ export const CertificatesSection = () => {
deleteKey="confirm" deleteKey="confirm"
onDeleteApproved={() => onDeleteApproved={() =>
onRemoveCertificateSubmit( onRemoveCertificateSubmit(
(popUp?.deleteCertificate?.data as { serialNumber: string })?.serialNumber (popUp?.deleteCertificate?.data as { certificateId: string })?.certificateId
) )
} }
/> />
@@ -501,7 +501,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
)} )}
onClick={async () => onClick={async () =>
handlePopUpOpen("revokeCertificate", { handlePopUpOpen("revokeCertificate", {
serialNumber: certificate.serialNumber certificateId: certificate.id
}) })
} }
disabled={!isAllowed} disabled={!isAllowed}
@@ -524,7 +524,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
)} )}
onClick={async () => onClick={async () =>
handlePopUpOpen("deleteCertificate", { handlePopUpOpen("deleteCertificate", {
serialNumber: certificate.serialNumber, certificateId: certificate.id,
commonName: certificate.commonName commonName: certificate.commonName
}) })
} }
@@ -171,7 +171,7 @@ export const PkiSubscriberCertificatesTable = ({ subscriberName, handlePopUpOpen
onClick={() => onClick={() =>
handlePopUpOpen && handlePopUpOpen &&
handlePopUpOpen("revokeCertificate", { handlePopUpOpen("revokeCertificate", {
serialNumber: certificate.serialNumber certificateId: certificate.id
}) })
} }
disabled={!isAllowed} disabled={!isAllowed}
@@ -29,7 +29,7 @@ import {
ProjectPermissionSub ProjectPermissionSub
} from "@app/context/ProjectPermissionContext/types"; } from "@app/context/ProjectPermissionContext/types";
import { usePopUp, useToggle } from "@app/hooks"; import { usePopUp, useToggle } from "@app/hooks";
import { useGetCaById } from "@app/hooks/api/ca/queries"; import { useGetInternalCaById } from "@app/hooks/api/ca/queries";
import { IssuerType, TCertificateProfile } from "@app/hooks/api/certificateProfiles"; import { IssuerType, TCertificateProfile } from "@app/hooks/api/certificateProfiles";
import { useGetCertificateTemplateV2ById } from "@app/hooks/api/certificateTemplates/queries"; import { useGetCertificateTemplateV2ById } from "@app/hooks/api/certificateTemplates/queries";
import { CertificateIssuanceModal } from "@app/pages/cert-manager/CertificatesPage/components/CertificateIssuanceModal"; import { CertificateIssuanceModal } from "@app/pages/cert-manager/CertificatesPage/components/CertificateIssuanceModal";
@@ -49,7 +49,7 @@ export const ProfileRow = ({
}: Props) => { }: Props) => {
const { permission } = useProjectPermission(); const { permission } = useProjectPermission();
const { data: caData } = useGetCaById(profile.caId ?? ""); const { data: caData } = useGetInternalCaById(profile.caId ?? "");
const { popUp, handlePopUpToggle } = usePopUp(["issueCertificate"] as const); const { popUp, handlePopUpToggle } = usePopUp(["issueCertificate"] as const);
@@ -123,7 +123,9 @@ export const ProfileRow = ({
<span className="text-sm text-mineshaft-300"> <span className="text-sm text-mineshaft-300">
{profile.issuerType === IssuerType.SELF_SIGNED {profile.issuerType === IssuerType.SELF_SIGNED
? "Self-signed" ? "Self-signed"
: caData?.friendlyName || caData?.commonName || profile.caId} : caData?.configuration.friendlyName ||
caData?.configuration.commonName ||
profile.caId}
</span> </span>
</Td> </Td>
<Td> <Td>
+14 -14
View File
@@ -1515,8 +1515,8 @@ const certManagerPkiSyncDetailsByIDPageRouteRoute =
const certManagerCertAuthDetailsByIDPageRouteRoute = const certManagerCertAuthDetailsByIDPageRouteRoute =
certManagerCertAuthDetailsByIDPageRouteImport.update({ certManagerCertAuthDetailsByIDPageRouteImport.update({
id: '/ca/$caName', id: '/ca/$caId',
path: '/ca/$caName', path: '/ca/$caId',
getParentRoute: () => certManagerLayoutRoute, getParentRoute: () => certManagerLayoutRoute,
} as any) } as any)
@@ -3182,10 +3182,10 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof secretScanningSecretScanningDataSourcesPageRouteImport preLoaderRoute: typeof secretScanningSecretScanningDataSourcesPageRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationsOrgIdProjectsSecretScanningProjectIdSecretScanningLayoutDataSourcesImport parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationsOrgIdProjectsSecretScanningProjectIdSecretScanningLayoutDataSourcesImport
} }
'/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caName': { '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caId': {
id: '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caName' id: '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caId'
path: '/ca/$caName' path: '/ca/$caId'
fullPath: '/organizations/$orgId/projects/cert-management/$projectId/ca/$caName' fullPath: '/organizations/$orgId/projects/cert-management/$projectId/ca/$caId'
preLoaderRoute: typeof certManagerCertAuthDetailsByIDPageRouteImport preLoaderRoute: typeof certManagerCertAuthDetailsByIDPageRouteImport
parentRoute: typeof certManagerLayoutImport parentRoute: typeof certManagerLayoutImport
} }
@@ -5199,7 +5199,7 @@ export interface FileRoutesByFullPath {
'/organizations/$orgId/projects/pam/$projectId/sessions/': typeof pamPamSessionsPageRouteRoute '/organizations/$orgId/projects/pam/$projectId/sessions/': typeof pamPamSessionsPageRouteRoute
'/organizations/$orgId/projects/secret-management/$projectId/integrations/': typeof secretManagerIntegrationsListPageRouteRoute '/organizations/$orgId/projects/secret-management/$projectId/integrations/': typeof secretManagerIntegrationsListPageRouteRoute
'/organizations/$orgId/projects/secret-scanning/$projectId/data-sources/': typeof secretScanningSecretScanningDataSourcesPageRouteRoute '/organizations/$orgId/projects/secret-scanning/$projectId/data-sources/': typeof secretScanningSecretScanningDataSourcesPageRouteRoute
'/organizations/$orgId/projects/cert-management/$projectId/ca/$caName': typeof certManagerCertAuthDetailsByIDPageRouteRoute '/organizations/$orgId/projects/cert-management/$projectId/ca/$caId': typeof certManagerCertAuthDetailsByIDPageRouteRoute
'/organizations/$orgId/projects/cert-management/$projectId/integrations/$syncId': typeof certManagerPkiSyncDetailsByIDPageRouteRoute '/organizations/$orgId/projects/cert-management/$projectId/integrations/$syncId': typeof certManagerPkiSyncDetailsByIDPageRouteRoute
'/organizations/$orgId/projects/cert-management/$projectId/subscribers/$subscriberName': typeof certManagerPkiSubscriberDetailsByIDPageRouteRoute '/organizations/$orgId/projects/cert-management/$projectId/subscribers/$subscriberName': typeof certManagerPkiSubscriberDetailsByIDPageRouteRoute
'/organizations/$orgId/projects/pam/$projectId/sessions/$sessionId': typeof pamPamSessionsByIDPageRouteRoute '/organizations/$orgId/projects/pam/$projectId/sessions/$sessionId': typeof pamPamSessionsByIDPageRouteRoute
@@ -5429,7 +5429,7 @@ export interface FileRoutesByTo {
'/organizations/$orgId/projects/pam/$projectId/sessions': typeof pamPamSessionsPageRouteRoute '/organizations/$orgId/projects/pam/$projectId/sessions': typeof pamPamSessionsPageRouteRoute
'/organizations/$orgId/projects/secret-management/$projectId/integrations': typeof secretManagerIntegrationsListPageRouteRoute '/organizations/$orgId/projects/secret-management/$projectId/integrations': typeof secretManagerIntegrationsListPageRouteRoute
'/organizations/$orgId/projects/secret-scanning/$projectId/data-sources': typeof secretScanningSecretScanningDataSourcesPageRouteRoute '/organizations/$orgId/projects/secret-scanning/$projectId/data-sources': typeof secretScanningSecretScanningDataSourcesPageRouteRoute
'/organizations/$orgId/projects/cert-management/$projectId/ca/$caName': typeof certManagerCertAuthDetailsByIDPageRouteRoute '/organizations/$orgId/projects/cert-management/$projectId/ca/$caId': typeof certManagerCertAuthDetailsByIDPageRouteRoute
'/organizations/$orgId/projects/cert-management/$projectId/integrations/$syncId': typeof certManagerPkiSyncDetailsByIDPageRouteRoute '/organizations/$orgId/projects/cert-management/$projectId/integrations/$syncId': typeof certManagerPkiSyncDetailsByIDPageRouteRoute
'/organizations/$orgId/projects/cert-management/$projectId/subscribers/$subscriberName': typeof certManagerPkiSubscriberDetailsByIDPageRouteRoute '/organizations/$orgId/projects/cert-management/$projectId/subscribers/$subscriberName': typeof certManagerPkiSubscriberDetailsByIDPageRouteRoute
'/organizations/$orgId/projects/pam/$projectId/sessions/$sessionId': typeof pamPamSessionsByIDPageRouteRoute '/organizations/$orgId/projects/pam/$projectId/sessions/$sessionId': typeof pamPamSessionsByIDPageRouteRoute
@@ -5682,7 +5682,7 @@ export interface FileRoutesById {
'/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions/': typeof pamPamSessionsPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions/': typeof pamPamSessionsPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/': typeof secretManagerIntegrationsListPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/': typeof secretManagerIntegrationsListPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-scanning/$projectId/_secret-scanning-layout/data-sources/': typeof secretScanningSecretScanningDataSourcesPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-scanning/$projectId/_secret-scanning-layout/data-sources/': typeof secretScanningSecretScanningDataSourcesPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caName': typeof certManagerCertAuthDetailsByIDPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caId': typeof certManagerCertAuthDetailsByIDPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/integrations/$syncId': typeof certManagerPkiSyncDetailsByIDPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/integrations/$syncId': typeof certManagerPkiSyncDetailsByIDPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/subscribers/$subscriberName': typeof certManagerPkiSubscriberDetailsByIDPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/subscribers/$subscriberName': typeof certManagerPkiSubscriberDetailsByIDPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions/$sessionId': typeof pamPamSessionsByIDPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions/$sessionId': typeof pamPamSessionsByIDPageRouteRoute
@@ -5927,7 +5927,7 @@ export interface FileRouteTypes {
| '/organizations/$orgId/projects/pam/$projectId/sessions/' | '/organizations/$orgId/projects/pam/$projectId/sessions/'
| '/organizations/$orgId/projects/secret-management/$projectId/integrations/' | '/organizations/$orgId/projects/secret-management/$projectId/integrations/'
| '/organizations/$orgId/projects/secret-scanning/$projectId/data-sources/' | '/organizations/$orgId/projects/secret-scanning/$projectId/data-sources/'
| '/organizations/$orgId/projects/cert-management/$projectId/ca/$caName' | '/organizations/$orgId/projects/cert-management/$projectId/ca/$caId'
| '/organizations/$orgId/projects/cert-management/$projectId/integrations/$syncId' | '/organizations/$orgId/projects/cert-management/$projectId/integrations/$syncId'
| '/organizations/$orgId/projects/cert-management/$projectId/subscribers/$subscriberName' | '/organizations/$orgId/projects/cert-management/$projectId/subscribers/$subscriberName'
| '/organizations/$orgId/projects/pam/$projectId/sessions/$sessionId' | '/organizations/$orgId/projects/pam/$projectId/sessions/$sessionId'
@@ -6156,7 +6156,7 @@ export interface FileRouteTypes {
| '/organizations/$orgId/projects/pam/$projectId/sessions' | '/organizations/$orgId/projects/pam/$projectId/sessions'
| '/organizations/$orgId/projects/secret-management/$projectId/integrations' | '/organizations/$orgId/projects/secret-management/$projectId/integrations'
| '/organizations/$orgId/projects/secret-scanning/$projectId/data-sources' | '/organizations/$orgId/projects/secret-scanning/$projectId/data-sources'
| '/organizations/$orgId/projects/cert-management/$projectId/ca/$caName' | '/organizations/$orgId/projects/cert-management/$projectId/ca/$caId'
| '/organizations/$orgId/projects/cert-management/$projectId/integrations/$syncId' | '/organizations/$orgId/projects/cert-management/$projectId/integrations/$syncId'
| '/organizations/$orgId/projects/cert-management/$projectId/subscribers/$subscriberName' | '/organizations/$orgId/projects/cert-management/$projectId/subscribers/$subscriberName'
| '/organizations/$orgId/projects/pam/$projectId/sessions/$sessionId' | '/organizations/$orgId/projects/pam/$projectId/sessions/$sessionId'
@@ -6407,7 +6407,7 @@ export interface FileRouteTypes {
| '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions/' | '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions/'
| '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/' | '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/'
| '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-scanning/$projectId/_secret-scanning-layout/data-sources/' | '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-scanning/$projectId/_secret-scanning-layout/data-sources/'
| '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caName' | '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caId'
| '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/integrations/$syncId' | '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/integrations/$syncId'
| '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/subscribers/$subscriberName' | '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/subscribers/$subscriberName'
| '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions/$sessionId' | '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions/$sessionId'
@@ -7030,7 +7030,7 @@ export const routeTree = rootRoute
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/certificate-templates", "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/certificate-templates",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/integrations", "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/integrations",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/subscribers", "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/subscribers",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caName", "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/groups/$groupId", "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/groups/$groupId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/identities/$identityId", "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/identities/$identityId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/members/$membershipId", "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/members/$membershipId",
@@ -7415,7 +7415,7 @@ export const routeTree = rootRoute
"filePath": "secret-scanning/SecretScanningDataSourcesPage/route.tsx", "filePath": "secret-scanning/SecretScanningDataSourcesPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-scanning/$projectId/_secret-scanning-layout/data-sources" "parent": "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-scanning/$projectId/_secret-scanning-layout/data-sources"
}, },
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caName": { "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caId": {
"filePath": "cert-manager/CertAuthDetailsByIDPage/route.tsx", "filePath": "cert-manager/CertAuthDetailsByIDPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout" "parent": "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout"
}, },
+1 -1
View File
@@ -282,7 +282,7 @@ const certManagerRoutes = route("/organizations/$orgId/projects/cert-management/
route("/certificate-templates", [index("cert-manager/PkiTemplateListPage/route.tsx")]), route("/certificate-templates", [index("cert-manager/PkiTemplateListPage/route.tsx")]),
route("/certificate-authorities", "cert-manager/CertificateAuthoritiesPage/route.tsx"), route("/certificate-authorities", "cert-manager/CertificateAuthoritiesPage/route.tsx"),
route("/alerting", "cert-manager/AlertingPage/route.tsx"), route("/alerting", "cert-manager/AlertingPage/route.tsx"),
route("/ca/$caName", "cert-manager/CertAuthDetailsByIDPage/route.tsx"), route("/ca/$caId", "cert-manager/CertAuthDetailsByIDPage/route.tsx"),
route("/pki-collections/$collectionId", "cert-manager/PkiCollectionDetailsByIDPage/routes.tsx"), route("/pki-collections/$collectionId", "cert-manager/PkiCollectionDetailsByIDPage/routes.tsx"),
route("/integrations", [ route("/integrations", [
index("cert-manager/IntegrationsListPage/route.tsx"), index("cert-manager/IntegrationsListPage/route.tsx"),