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: encrypt metadata feature #2059

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

Conversation

harshsbhat
Copy link
Contributor

@harshsbhat harshsbhat commented Sep 2, 2024

What does this PR do?

Fixes: #1678

This PR introduces a new feature called encrypted metadata so that users can share sensitive data associated with the key.
It also introduces an internal package named @unkey/vault so that other applications like dashboard use the agent/vault fairly easily.

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?

https://www.loom.com/share/99b4d4f9f9124b6b87ebaa5544216d86?sid=612b293b-4e23-49d3-8aeb-99fe44e113bc

  • Test A
  • Test B

Checklist

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

Summary by CodeRabbit

  • New Features

    • Introduced optional encryptedMeta field for secure metadata handling during key creation and verification.
    • Enhanced key verification process to return decrypted metadata.
    • Implemented a Vault class for efficient encryption and decryption operations.
    • Added a new cache namespace for storing encrypted metadata.
  • Bug Fixes

    • Improved error handling for metadata encryption and decryption processes.
  • Chores

    • Added new dependency for vault management to enhance security features.

Copy link

changeset-bot bot commented Sep 2, 2024

⚠️ No Changeset found

Latest commit: 616beb4

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

vercel bot commented Sep 2, 2024

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

A member of the Team first needs to authorize it.

Copy link
Contributor

coderabbitai bot commented Sep 2, 2024

Walkthrough

Walkthrough

The changes introduce a feature for managing encrypted metadata across the application. This includes the addition of an encryptedMeta field in key creation and verification processes, enhancements to the logic for handling this metadata, and the implementation of a vault service for encryption and decryption operations. The modifications ensure that sensitive information can be securely managed, addressing concerns related to data privacy.

Changes

Files Change Summary
apps/api/src/routes/v1_keys_createKey.ts, apps/api/src/routes/v1_keys_verifyKey.ts Added support for an optional encryptedMeta field in key creation and verification, including logic for encrypting and decrypting the metadata.
apps/dashboard/lib/trpc/routers/key/create.ts Introduced encryptedMeta parameter to the createKey procedure for handling encrypted metadata securely in database operations.
apps/dashboard/package.json Added dependency on @unkey/vault for vault management functionalities related to encryption and decryption.
internal/vault/src/index.ts Implemented a Vault class for encryption and decryption operations, including necessary request and response types.
apps/dashboard/app/(app)/pkg/cache/index.ts, apps/dashboard/app/(app)/pkg/cache/namespaces.ts Added encryptedMeta property to caching mechanisms to store encrypted metadata, enhancing the caching structure.

Assessment against linked issues

Objective Addressed Explanation
Encrypted metadata storage (Issue #1678)
Toggle encryption for existing metadata (Issue #1678) No toggle functionality for existing metadata was implemented.
New encryptedMeta field alongside original metadata (Issue #1678)

Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 0e9c23d and 616beb4.

Files selected for processing (1)
  • apps/api/src/routes/v1_keys_verifyKey.test.ts (5 hunks)
Files skipped from review as they are similar to previous changes (1)
  • apps/api/src/routes/v1_keys_verifyKey.test.ts

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 generate interesting stats about this repository and render them as a table.
    -- @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 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
Contributor

github-actions bot commented Sep 2, 2024

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

@harshsbhat harshsbhat changed the title Encrypted meta for API [WIP] Encrypted meta for API Sep 2, 2024
@harshsbhat harshsbhat changed the title [WIP] Encrypted meta for API feat: encrypt metadata feature Sep 2, 2024
@harshsbhat harshsbhat marked this pull request as ready for review September 2, 2024 18:38
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

Outside diff range, codebase verification and nitpick comments (1)
internal/vault/src/index.ts (1)

1-124: Add import statements and JSDoc comments.

The file is missing import statements and JSDoc comments.

Consider adding:

  • Import statements for the types and modules used, such as RequestInit and performance.
  • JSDoc comments for the class and methods to describe their purpose, parameters, and return values. For example:
/**
 * Client for the Vault service.
 */
export class Vault {
  // ...

  /**
   * Encrypts the given data using the specified keyring.
   * @param c The request context.
   * @param req The encrypt request.
   * @returns The encrypt response.
   * @throws Error if the encrypt request fails.
   */
  public async encrypt(c: RequestContext, req: EncryptRequest): Promise<EncryptResponse> {
    // ...
  }

  // ...
}
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between fde3d83 and 82db90b.

Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
Files selected for processing (10)
  • apps/api/src/routes/v1_keys_createKey.ts (3 hunks)
  • apps/api/src/routes/v1_keys_verifyKey.ts (4 hunks)
  • apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/client.tsx (6 hunks)
  • apps/dashboard/app/(app)/settings/root-keys/[keyId]/permissions/permissions.ts (2 hunks)
  • apps/dashboard/lib/trpc/routers/key/create.ts (5 hunks)
  • apps/dashboard/package.json (1 hunks)
  • internal/db/src/schema/keys.ts (1 hunks)
  • internal/vault/package.json (1 hunks)
  • internal/vault/src/index.ts (1 hunks)
  • packages/rbac/src/permissions.ts (1 hunks)
Files skipped from review due to trivial changes (1)
  • internal/vault/package.json
Additional comments not posted (24)
packages/rbac/src/permissions.ts (1)

41-42: LGTM!

The addition of the "encrypt_meta" and "decrypt_meta" actions to the apiActions enum is consistent with the PR objectives of introducing a new feature for handling encrypted metadata. The naming of the actions follows the existing convention in the enum, and the changes are limited to the enum without affecting the logic or control flow in the file.

internal/vault/src/index.ts (2)

1-28: LGTM!

The type definitions for encryption and decryption requests and responses are correctly defined.


33-40: LGTM!

The Vault class constructor is correctly implemented.

apps/dashboard/package.json (1)

56-56: LGTM!

The addition of the @unkey/vault dependency with version specifier workspace:^ is approved. This change suggests that the dashboard project will now utilize functionality related to vault management, which aligns with the objectives outlined in the PR summary and linked issue.

apps/dashboard/lib/trpc/routers/key/create.ts (5)

7-7: LGTM!

The import statement is necessary to use the Vault class and related types for encrypting metadata.


20-20: LGTM!

The addition of the encryptedMeta parameter to the input schema allows users to provide metadata in an encrypted format, enhancing the functionality to handle sensitive information securely.


72-84: LGTM!

The code segment correctly implements the logic for encrypting the encryptedMeta using the Vault class. It follows the necessary steps to construct the encryption request, generate a request context, and call the vault.encrypt method. The encrypted metadata is then stored in the metaSecret variable for further use.


95-95: LGTM!

Including the metaSecret in the database insertion ensures that the encrypted metadata is saved alongside other key details.


115-115: LGTM!

The change in the error message is minor and maintains consistency with other error messages in the codebase.

internal/db/src/schema/keys.ts (1)

51-51: LGTM!

The addition of the encryptedMeta field aligns with the PR objective of introducing a new feature for handling encrypted metadata. The field is of type text, which is suitable for storing encrypted data, and the field name clearly conveys its purpose. The field is seamlessly integrated into the existing schema without disrupting the overall structure.

apps/dashboard/app/(app)/settings/root-keys/[keyId]/permissions/permissions.ts (2)

54-61: LGTM!

The code changes are approved for the following reasons:

  • The new entries are consistent with the existing pattern of defining permissions.
  • The permission strings follow the established pattern for workspace permissions.
  • The changes align with the PR objective of introducing metadata encryption functionality.

189-196: LGTM!

The code changes are approved for the following reasons:

  • The new entries are consistent with the existing pattern of defining permissions.
  • The permission strings follow the established pattern for API permissions.
  • The changes align with the PR objective of introducing metadata encryption functionality.
apps/api/src/routes/v1_keys_verifyKey.ts (4)

181-190: LGTM!

The addition of the optional encryptedMeta field to the response schema enhances the API by allowing clients to access additional encrypted metadata associated with the key. This change aligns with the PR objective of introducing support for encrypted metadata.


Line range hint 301-365: LGTM!

The added logic for decrypting the encryptedMeta field is a crucial part of the encrypted metadata feature. It ensures that only authorized requests with the necessary permissions can decrypt the metadata, enhancing security. The use of rootKeyAuth and specific permission checks is a good approach. The retry mechanism improves the robustness of the decryption process, and assigning a default empty JSON object to metaSecret if decryption fails is a sensible fallback.


373-373: LGTM!

Adding the decrypted encryptedMeta to the response body is necessary to provide clients with access to the additional metadata associated with the key. Parsing the metaSecret variable ensures that the metadata is returned in the correct format.


1-5: LGTM!

The remaining code changes are minor and necessary to support the encrypted metadata feature. They include adding the vault service to the destructured services object and updating the requestBody and responseBody fields in the analytics ingestion for proper logging.

Also applies to: 340-340, 349-349, 354-359

apps/api/src/routes/v1_keys_createKey.ts (4)

77-87: LGTM!

The code changes are approved.


350-351: LGTM!

The code changes are approved.


352-390: LGTM!

The code changes are approved.


403-403: LGTM!

The code changes are approved.

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

81-97: LGTM!

The code changes for adding the encryptMetaEnabled and encryptMeta form fields look good. The refinement for encryptMeta is a nice touch to ensure only valid JSON can be entered.


210-212: LGTM!

The code changes for deleting the encryptMeta field from the form values if encryptMetaEnabled is set to false look good. This ensures that the encryptMeta field is not sent to the server unnecessarily.


224-224: LGTM!

The code changes for parsing the encryptMeta field as JSON before sending it to the server look good.


832-925: LGTM!

The code changes for adding the UI components for handling encrypted metadata look good. The card component, switch, textarea, and format button all work together nicely to provide a good user experience for entering and managing encrypted metadata.

internal/vault/src/index.ts Outdated Show resolved Hide resolved
internal/vault/src/index.ts Outdated Show resolved Hide resolved
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

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 82db90b and 20acdfd.

Files selected for processing (1)
  • internal/vault/src/index.ts (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • internal/vault/src/index.ts

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

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 20acdfd and 07ad642.

Files selected for processing (1)
  • apps/api/src/routes/v1_keys_createKey.ts (3 hunks)
Files skipped from review as they are similar to previous changes (1)
  • apps/api/src/routes/v1_keys_createKey.ts

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

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 07ad642 and 7a4f0f7.

Files selected for processing (1)
  • apps/api/src/routes/v1_keys_createKey.ts (3 hunks)
Files skipped from review as they are similar to previous changes (1)
  • apps/api/src/routes/v1_keys_createKey.ts

Copy link
Collaborator

@chronark chronark left a comment

Choose a reason for hiding this comment

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

Can you add a test case for the API please, it needs to ensure that the verification response includes the decrypted metadata

you can run the test suite via

pnpm local --service=api
pnpm --dir=apps/api test:integration

.optional()
.openapi({
description:
"This is a place for dynamic meta data, anything that feels useful for you should go here",
Copy link
Collaborator

Choose a reason for hiding this comment

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

This needs to explain that it's encrypted too, as this string is used in sdks and documentation.
I know the field is named encryptedMeta but it doesn't hurt to be obvious.

}),
);

metaSecret = encryptedMeta.encrypted;
Copy link
Collaborator

Choose a reason for hiding this comment

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

you're ignoring the returned encryption key id, that needs to be stored as well, otherwise we can't reroll the encryption keys

),
);
const encryptedMeta =
typeof val.key.encryptedMeta === "string"
Copy link
Collaborator

Choose a reason for hiding this comment

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

what else could it be?
This should always be a string or a falsy value, right?

@@ -326,13 +337,40 @@ export const registerV1KeysVerifyKey = (app: App) =>
code: val.code,
});
}
let metaSecret = "";
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is way too expensive to do uncached. Making a network request to the agent in the critical path is too slow.
We need to move this into the cache's swr callback

let metaSecret = "";
if (val.key.encryptedMeta) {
try {
await rootKeyAuth(
Copy link
Collaborator

Choose a reason for hiding this comment

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

what rootKey are you using here?
verifying keys does not require a root key. I'm very surprised this didn't cause issues when you tested it.


metaSecret = decryptRes.plaintext;
} catch {
metaSecret = "{}";
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think undefined and therefore not returning the field in the api response is preferrable

<>
<p className="text-xs text-content-subtle">
Encrypt sensitive data before associating it with this key to store it
securely. Whenever you verify this key with the decrypt metadata
Copy link
Collaborator

Choose a reason for hiding this comment

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

As mentioned above, verifying keys does not require a root key (yet), therefore you can't do permission checks

@@ -48,6 +48,7 @@ export const keys = mysqlTable(
ownerId: varchar("owner_id", { length: 256 }),
identityId: varchar("identity_id", { length: 256 }),
meta: text("meta"),
encryptedMeta: text("encryptedMeta"),
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is not sufficient, we need to store both the ciphertext and the encryptionKeyId, you can check out the encrypted_keys table for a reference.

We probably want to put this in a different table as well, or reuse the secrets table, wdyt?

this.token = token;
}

private async fetchWithMetrics(
Copy link
Collaborator

Choose a reason for hiding this comment

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

this doesn't seem to do anything, I assume you just copied it.

Let me give you some context on why we had stuff like the requestId here. We were running into some latency issues between the API and the agent and needed to log latency times for each requestId, to correlate them with what the customer saw.

The dashboard talking to the agent doesn't really need that and we can remove it entirely

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

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 7a4f0f7 and 5f1c4da.

Files selected for processing (1)
  • apps/dashboard/package.json (1 hunks)
Files skipped from review due to trivial changes (1)
  • apps/dashboard/package.json

@perkinsjr
Copy link
Collaborator

Hey @harshsbhat are you waiting on anything from the team?

Looking at Andreas comments there are still things to resolve in your changes.

@harshsbhat
Copy link
Contributor Author

Hey @harshsbhat are you waiting on anything from the team?

Looking at Andreas comments there are still things to resolve in your changes.

Yeah, I was a little busy with my college work. I have a few unpushed changes. Also, few small questions that I will put on discord in a bit if that's fine as I belive that to be a faster way to communicate. But should be able to get this done in this week.

Copy link
Collaborator

No rush,

Just making sure I didn't miss something, please ping me in Discord when you ask the questions. Andreas is on PTO and I might miss them

@harshsbhat
Copy link
Contributor Author

No rush,

Just making sure I didn't miss something, please ping me in Discord when you ask the questions. Andreas is on PTO and I might miss them

Pushed! I have added the requested changes.

  • Adding it to tests
  • Encrypted data, id should go into secrets table
  • Add decryption for the key in cache
  • Remove fetch metrics from @unkey/vault

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

Successfully merging this pull request may close these issues.

Encrypted metadata
3 participants