mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 19:28:16 +00:00
misc: moved all to /cert-manager and corrected API issues
This commit is contained in:
@@ -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 |
|
||||||
|
|||||||
@@ -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,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
|
||||||
|
|||||||
@@ -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 |
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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 => {
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
+13
-26
@@ -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;
|
||||||
|
|
||||||
|
|||||||
+85
@@ -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 ?? [])]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
+411
-1
@@ -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);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -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(
|
||||||
|
|||||||
@@ -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,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,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
|
||||||
);
|
);
|
||||||
|
|||||||
+2
-21
@@ -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;
|
|
||||||
};
|
};
|
||||||
|
|||||||
+4
-4
@@ -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),
|
||||||
|
|||||||
+11
-12
@@ -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),
|
||||||
|
|||||||
-4
@@ -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),
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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} />
|
||||||
|
|||||||
+1
-1
@@ -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> */}
|
||||||
|
|||||||
+5
-7
@@ -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
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
+11
-8
@@ -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}
|
||||||
|
|||||||
+2
-3
@@ -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,
|
||||||
|
|||||||
+7
-7
@@ -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 }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+5
-2
@@ -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
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
|||||||
+2
-2
@@ -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
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -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}
|
||||||
|
|||||||
+5
-3
@@ -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>
|
||||||
|
|||||||
@@ -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"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
Reference in New Issue
Block a user