Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create recoverable keys from dashboard #2783

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

harshsbhat
Copy link
Contributor

@harshsbhat harshsbhat commented Dec 28, 2024

What does this PR do?

Fixes #2097

This PR allows users to create recoverable keys through the dashboard, which is only possible through API as of now.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

  • Test A:

Make sure you have store_encrypted_keys enabled. Head over to the dashboard and create a new key http://localhost:3000/apis/api_id/keys/ks_id/new with recoverable: true. Get the key_id of the newly created key and use it with getKey API like this ( make sure you have appropriate permissions to get the decrypted key ):

curl --request GET \
  --url 'http://localhost:8787/v1/keys.getKey?keyId=key_id123&decrypt=true' \
  --header 'Authorization: Bearer unkey_rootKey'

The decrypted key should be logged in the response

  • Test B (few edge cases that might be worth testing ):
  1. Without store_encrypted_keys the recoverable card is not visible.
  2. Added in the transaction so even if the encryption fails an extra key won't be added.

Checklist

https://www.loom.com/share/713ebadf169b43eb9055dda724dd7008?sid=82985e82-072f-4a10-9595-78c010d7484f

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

Summary by CodeRabbit

  • New Features

    • Added option to enable key recovery during key creation.
    • Introduced a configurable switch to store encrypted keys for future recovery.
    • Enhanced key management with optional encryption and recovery capabilities.
  • Bug Fixes

    • Improved validation and handling of key recovery settings.
  • Documentation

    • Updated user interface to explain key recovery implications.

Copy link

changeset-bot bot commented Dec 28, 2024

⚠️ No Changeset found

Latest commit: f3f5fa4

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

coderabbitai bot commented Dec 28, 2024

Warning

Rate limit exceeded

@harshsbhat has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 13 minutes and 5 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 3a66720 and f3f5fa4.

📒 Files selected for processing (1)
  • apps/dashboard/lib/trpc/routers/key/create.ts (3 hunks)
📝 Walkthrough

Walkthrough

This pull request introduces functionality to allow setting recoverable keys when creating keys in the dashboard. The changes span multiple files in the dashboard application, adding a new recoverEnabled option to the key creation process. The implementation includes UI modifications in the client component, updates to the validation schema, and backend changes in the key creation procedure to support optional key encryption and recovery.

Changes

File Change Summary
apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/client.tsx Added storeEncryptedKeys prop, introduced recoverEnabled state, updated form submission logic to handle key recoverability.
apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/page.tsx Passed storeEncryptedKeys prop to CreateKey component from keyAuth object.
apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/validation.ts Added recoverEnabled boolean field to form schema with default false.
apps/dashboard/lib/trpc/routers/key/create.ts Updated input schema with optional recoverEnabled, added conditional encryption logic for recoverable keys.

Assessment against linked issues

Objective Addressed Explanation
Add toggle to mark key as recoverable
Support recoverable keys in dashboard UI
Update trpc handler for key recovery

Possibly related PRs

  • feat: Show keyId after creating a new key #2107: The changes in this PR enhance the CreateKey component, which is directly related to the modifications made in the main PR regarding the CreateKey component's functionality and state management.
  • feat: allow-developer-to-set-a-custom-refill-time-when-using-the #2114: This PR also modifies the CreateKey component by adding a new property related to refill functionality, which aligns with the changes in the main PR that enhance the key creation process.
  • feat: Add default bytes and prefix #2146: This PR introduces new props (defaultBytes and defaultPrefix) to the CreateKey component, which are relevant to the changes made in the main PR that also involve modifications to the CreateKey component.
  • fix: create key toggle issue #2711: This PR addresses a bug in the CreateKey component related to the "Limited Use" toggle, which is relevant to the changes made in the main PR that enhance the component's functionality and form handling.
  • fix: keys setting vulns #2718: This PR removes the workspaceId from the DefaultBytes and DefaultPrefix components, which are related to the changes in the main PR that involve the CreateKey component and its associated settings.

Suggested Labels

Dashboard

Suggested Reviewers

  • mcstepp
  • perkinsjr
  • chronark
  • ogzhanolguncu

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

vercel bot commented Dec 28, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
engineering ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 28, 2024 5:35am

Copy link

vercel bot commented Dec 28, 2024

@harshsbhat is attempting to deploy a commit to the Unkey Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions bot added the Feature New feature or request label Dec 28, 2024
Copy link
Contributor

github-actions bot commented Dec 28, 2024

Thank you for following the naming conventions for pull request titles! 🙏

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
apps/dashboard/lib/trpc/routers/key/create.ts (2)

3-7: Add relevant error logging for new imports
You have introduced new imports (env, EncryptRequest, RequestContext, and Vault). While everything looks fine, consider logging or re-throwing with error details when these modules fail to load to aid in debugging unexpected runtime issues.


116-145: Encryption logic and database insert
The addition of the encryption logic is well-structured:

  • Checking recoverEnabled and storeEncryptedKeys before using Vault.
  • Handling errors with TRPCError.
    A couple of suggestions:
  1. Logging: Consider logging the _err details to a secure or masked target so you have more insight into the failure root cause instead of discarding the caught error.
  2. Atomicity: The current transaction ensures atomic writes. If the Vault call times out or fails, the transaction is rolled back, which is good. Confirm that any partial failures are handled properly in logs for debugging.
apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/client.tsx (2)

186-215: Conditional messaging on recovery
Displaying context-dependent messaging ("This key can be recovered" vs. "cannot be recovered") provides clarity to the user. Great approach.


794-844: UI block for Recoverable Keys
The UI block is well-organized, with a switch for recoverEnabled and explanatory text about encrypted vault storage. A minor enhancement could be to clarify the data retention policy or accountability if there are multiple vault encryption keys across partitions.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1115fd5 and 3b36e6c.

📒 Files selected for processing (4)
  • apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/client.tsx (8 hunks)
  • apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/page.tsx (1 hunks)
  • apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/validation.ts (1 hunks)
  • apps/dashboard/lib/trpc/routers/key/create.ts (3 hunks)
🔇 Additional comments (7)
apps/dashboard/lib/trpc/routers/key/create.ts (1)

38-38: New optional field in input schema
The new recoverEnabled: z.boolean().optional() field is clear and well-typed. Ensure consumers of the createKey procedure are aware that omitting the field defaults it to undefined. This might require additional checks on the front-end if usage patterns vary.

apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/page.tsx (1)

65-65: Propagating storeEncryptedKeys
Passing storeEncryptedKeys={keyAuth.storeEncryptedKeys} is a neat integration point, ensuring the UI reacts accordingly. Verify that any edge cases—like missing or false values—are gracefully handled in the CreateKey component.

apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/validation.ts (1)

90-90: Default value for recoverEnabled
Setting recoverEnabled to false by default is a reasonable approach to ensuring users explicitly opt in to recoverability. This helps with best-practice security defaults.

apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/client.tsx (4)

49-60: New props in CreateKey
Introducing storeEncryptedKeys to the Props and handling them in the CreateKey component is clear. Great job ensuring default values are assigned for optional fields like prefix and bytes.


77-77: recoverEnabled: false
Defaulting recoverEnabled to false aligns with security best practices. Good choice to prevent accidental creation of recoverable keys.


116-118: Resetting recoverable state
This logic ensures recoverable toggles off on form submission if the user did not explicitly check recoverEnabled. Verify that partial form submissions or validations do not leave the component in an inconsistent state.


252-252: Resetting recoverEnabled after creation
Resetting the field ensures a fresh state for each subsequent key creation. Double-check that user preferences related to recoverability are appropriately re-initialized if they create multiple keys in succession.

@harshsbhat
Copy link
Contributor Author

third try 😔

hopefully, I get it this time

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b36e6c and 3a66720.

📒 Files selected for processing (1)
  • apps/dashboard/lib/trpc/routers/key/create.ts (3 hunks)
🔇 Additional comments (2)
apps/dashboard/lib/trpc/routers/key/create.ts (2)

3-3: LGTM! Clean schema extension and imports.

The new imports and schema changes are well-structured and properly typed.

Also applies to: 7-7, 38-38


116-147: Verify security measures for recoverable keys

Please ensure the following security aspects are properly addressed:

  1. Verify that key material is properly cleaned up if the transaction fails
  2. Confirm that the vault's encryption meets security requirements
  3. Ensure audit logs capture the recovery-enabled status

Run the following script to check audit logging implementation:

✅ Verification successful

Security measures for recoverable keys are properly implemented

The implementation has appropriate security measures in place:

  1. Key material cleanup is handled through database transactions which ensure atomicity - if any part fails, the entire transaction is rolled back
  2. The vault encryption service:
    • Uses secure HTTPS communication
    • Requires authentication via bearer token
    • Includes request tracing via request IDs
    • Properly handles and reports encryption errors
  3. Errors during encryption or storage are properly caught and logged, with appropriate error messages
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if audit logs capture recovery-enabled status

# Check audit log schema and implementations
rg -A 5 "insertAuditLogs" 

# Check for any existing audit log patterns
ast-grep --pattern 'insertAuditLogs($_, {
  $$$
  description: $_,
  $$$
})'

Length of output: 66102


Script:

#!/bin/bash
# Check if audit logs capture recovery-enabled status in the create key router
rg -A 10 "key.create" apps/dashboard/lib/trpc/routers/key/create.ts

# Check if there are any audit logs related to key recovery
rg -A 10 "recovery" apps/dashboard/lib/trpc/routers/key/create.ts

Length of output: 431


Script:

#!/bin/bash
# Check the key creation implementation for cleanup on failure
rg -A 10 "catch\(" apps/dashboard/lib/trpc/routers/key/create.ts

# Check vault encryption implementation
rg -A 10 "class Vault" -g "*.ts"

Length of output: 3151


Script:

#!/bin/bash
# Check vault encryption implementation details
cat internal/vault/src/index.ts

# Check for transaction rollback handling
rg -A 10 "transaction" apps/dashboard/lib/trpc/routers/key/create.ts

Length of output: 2797

Comment on lines 131 to 146
await tx
.insert(schema.encryptedKeys)
.values({
keyId: keyId,
workspaceId: workspace?.id,
encrypted: vaultRes.encrypted,
encryptionKeyId: vaultRes.keyId,
})
.catch((err) => {
console.error(err);
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message:
"We are unable to store encrypted the key. Please contact support using support@unkey.dev",
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve database operation safety

The database operations need similar improvements:

  1. Remove optional chaining for workspace.id
  2. Consider cleanup on failure
  3. Use consistent error messaging

Apply this diff:

   await tx
     .insert(schema.encryptedKeys)
     .values({
       keyId: keyId,
-      workspaceId: workspace?.id,
+      workspaceId: workspace.id,
       encrypted: vaultRes.encrypted,
       encryptionKeyId: vaultRes.keyId,
     })
     .catch((err) => {
       console.error(err);
+      // Consider cleanup of the key if encryption storage fails
       throw new TRPCError({
         code: "INTERNAL_SERVER_ERROR",
-        message:
-          "We are unable to store encrypted the key. Please contact support using support@unkey.dev",
+        message: "Unable to process request. Please contact support.",
       });
     });

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +116 to +130
if (input.recoverEnabled && keyAuth?.storeEncryptedKeys) {
const vault = new Vault(env().AGENT_URL, env().AGENT_TOKEN);
const encryptReq: EncryptRequest = {
keyring: workspace?.id,
data: key,
};
const requestId = crypto.randomUUID();
const context: RequestContext = { requestId };
const vaultRes = await vault.encrypt(context, encryptReq).catch((err) => {
console.error(err);
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Encryption Failed. Please contact support using support@unkey.dev",
});
});
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Strengthen error handling and null checks

A few suggestions to improve the robustness of the encryption implementation:

  1. Validate environment variables before vault initialization
  2. Replace optional chaining with explicit null check for workspace
  3. Consider using more user-friendly error messages

Apply this diff:

 if (input.recoverEnabled && keyAuth?.storeEncryptedKeys) {
+  if (!env().AGENT_URL || !env().AGENT_TOKEN) {
+    throw new TRPCError({
+      code: "INTERNAL_SERVER_ERROR",
+      message: "Unable to process request. Please contact support.",
+    });
+  }
+  if (!workspace) {
+    throw new TRPCError({
+      code: "INTERNAL_SERVER_ERROR",
+      message: "Unable to process request. Please contact support.",
+    });
+  }
   const vault = new Vault(env().AGENT_URL, env().AGENT_TOKEN);
   const encryptReq: EncryptRequest = {
-    keyring: workspace?.id,
+    keyring: workspace.id,
     data: key,
   };
   const requestId = crypto.randomUUID();
   const context: RequestContext = { requestId };
   const vaultRes = await vault.encrypt(context, encryptReq).catch((err) => {
     console.error(err);
     throw new TRPCError({
       code: "INTERNAL_SERVER_ERROR",
-      message: "Encryption Failed. Please contact support using support@unkey.dev",
+      message: "Unable to process request. Please contact support.",
     });
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (input.recoverEnabled && keyAuth?.storeEncryptedKeys) {
const vault = new Vault(env().AGENT_URL, env().AGENT_TOKEN);
const encryptReq: EncryptRequest = {
keyring: workspace?.id,
data: key,
};
const requestId = crypto.randomUUID();
const context: RequestContext = { requestId };
const vaultRes = await vault.encrypt(context, encryptReq).catch((err) => {
console.error(err);
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Encryption Failed. Please contact support using support@unkey.dev",
});
});
if (input.recoverEnabled && keyAuth?.storeEncryptedKeys) {
if (!env().AGENT_URL || !env().AGENT_TOKEN) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Unable to process request. Please contact support.",
});
}
if (!workspace) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Unable to process request. Please contact support.",
});
}
const vault = new Vault(env().AGENT_URL, env().AGENT_TOKEN);
const encryptReq: EncryptRequest = {
keyring: workspace.id,
data: key,
};
const requestId = crypto.randomUUID();
const context: RequestContext = { requestId };
const vaultRes = await vault.encrypt(context, encryptReq).catch((err) => {
console.error(err);
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Unable to process request. Please contact support.",
});
});

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine:

  • keyring: workspace?.id,
  • keyring: workspace.id,

I doubt the other two change requests tho. Let's wait for some maintainer

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@harshsbhat, understood! We'll defer to the maintainer's verdict on those additional changes. Let me know if you need anything else in the meantime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow setting recoverable: true when creating keys in the dashboard
1 participant