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: organization access tokens #6493

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from
Draft

Conversation

n1ru4l
Copy link
Contributor

@n1ru4l n1ru4l commented Feb 10, 2025

Background

Adds organization access tokens which will supersede target access tokens.

Organization access tokens allow selecting scopes and permissions, allowing granular resource-based access control.

Description

This PR introduces the CRUD parts for creating, updating, and deleting organization access tokens.

Checklist

  • Input validation
  • Output encoding
  • Authentication management
  • Session management
  • Access control
  • Cryptographic practices
  • Error handling and logging
  • Data protection
  • Communication security
  • System configuration
  • Database security
  • File management
  • Memory management
  • Testing

Copy link
Contributor

coderabbitai bot commented Feb 10, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Introduced GraphQL API endpoints enabling creation and deletion of organization access tokens.
    • Enhanced access control with token-based authentication and refined permission management.
  • Tests

    • Added comprehensive integration tests covering token creation, validation, and permission checks.
  • Refactor

    • Streamlined resource assignment and authentication workflows for improved security and performance.
  • Chores

    • Updated dependency patches and added database migration support to accommodate access token functionality.

Walkthrough

This pull request introduces integration tests and GraphQL API enhancements for managing organization access tokens. It adds new mutations for creating and deleting tokens along with corresponding resolvers and provider classes. A new migration creates the necessary database table, and related package configuration updates, dependency patches, and export mappings are implemented. The authentication and authorization modules are refactored to support token-based access while reorganizing permission definitions and resource assignment logic throughout the organization module. Additionally, changes in shared utilities and server configuration update the access token strategy and error handling.

Changes

File(s) Change Summary
integration-tests/tests/api/organization-access-tokens.spec.ts Added integration tests covering creation, input validation, permission checks, and resource access queries for organization access tokens.
package.json, packages/services/api/package.json, patches/bentocache.patch Modified patched dependencies: re-added patch for p-cancelable and added new patch and export mappings for bentocache; added bentocache to devDependencies.
packages/migrations/src/actions/2025.01.30T00-02-03.organization-access-tokens.ts, packages/migrations/src/run-pg-migrations.ts Introduced migration for creating the organization_access_tokens table and updated the migration runner to include it.
packages/services/api/src/modules/auth/... Updated permission configurations in authz.ts; added new classes (OrganizationAccessTokenSession, OrganizationAccessTokenStrategy) in organization-access-token-strategy.ts; removed redundant methods in supertokens-strategy.ts; simplified token length check and updated policies in target-access-token-strategy.ts; adjusted import paths in module.graphql.mappers.ts.
packages/services/api/src/modules/organization/** Introduced new providers (OrganizationAccessTokens, OrganizationAccessTokensCache, ResourceAssignments); added a new module (organization-access-key.ts); restructured permission definitions (moved from organization-member-permissions.ts to permissions.ts and added organization-access-token-permissions.ts); added new GraphQL mutations and resolvers for creating and deleting tokens; updated resource assignment handling and member resolvers.
packages/services/api/src/modules/schema/providers/schema-manager.ts, packages/services/api/src/modules/shared/providers/id-translator.ts Updated error handling for permission checks and added a new method to resolve organization references.
packages/services/server/src/index.ts Replaced the previous user auth strategy with the new organization access token strategy utilizing the new cache.
packages/services/storage/src/db/types.ts Added new interface organization_access_tokens and updated DBTables to include it.

Sequence Diagram(s)

sequenceDiagram
    participant C as Client
    participant GS as GraphQL Server
    participant R as Resolver (createOrganizationAccessToken)
    participant P as OrganizationAccessTokens Provider
    participant DB as Database
    participant Cache as OrganizationAccessTokensCache

    C->>GS: Send createOrganizationAccessToken mutation (input)
    GS->>R: Forward mutation input
    R->>P: Call create() with input data
    P->>DB: Insert new token record
    DB-->>P: Return created token data
    P->>Cache: Add token to cache
    P-->>R: Return token details and private access key
    R-->>GS: Respond with success object
    GS-->>C: Return token data and access key
Loading
sequenceDiagram
    participant C as Client
    participant GS as GraphQL Server
    participant DR as Resolver (deleteOrganizationAccessToken)
    participant P as OrganizationAccessTokens Provider
    participant DB as Database
    participant Cache as OrganizationAccessTokensCache

    C->>GS: Send deleteOrganizationAccessToken mutation (token ID)
    GS->>DR: Forward mutation input
    DR->>P: Call delete() with token ID
    P->>DB: Delete token record
    DB-->>P: Confirm deletion
    P->>Cache: Purge token from cache
    P-->>DR: Return deletion confirmation
    DR-->>GS: Respond with deletion success
    GS-->>C: Return deletion result
Loading

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 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 Feb 10, 2025

🚀 Snapshot Release (alpha)

The latest changes of this PR are available as alpha on npm (based on the declared changesets):

Package Version Info
@graphql-hive/cli 0.48.2-alpha-20250212122623-1ff10816d44ee474c802b10c555435fe75f98016 npm ↗︎ unpkg ↗︎
hive 5.1.1-alpha-20250212122623-1ff10816d44ee474c802b10c555435fe75f98016 npm ↗︎ unpkg ↗︎

Copy link
Contributor

github-actions bot commented Feb 10, 2025

🐋 This PR was built and pushed to the following Docker images:

Targets: build

Platforms: linux/arm64

Image Tag: 1ff10816d44ee474c802b10c555435fe75f98016

Docker Bake metadata
{
"app": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/api/health",
          "build-arg:IMAGE_DESCRIPTION": "The app of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/app",
          "build-arg:PORT": "3000",
          "build-arg:RELEASE": "1ff10816d44ee474c802b10c555435fe75f98016",
          "build-arg:SERVICE_DIR_NAME": "@hive/app",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:dockerfile": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:shared": "xerowlb9b6lm8x6mj03utdlls"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-fce81dbe-7ae6-4180-89da-ac73c7f8d846/builder-fce81dbe-7ae6-4180-89da-ac73c7f8d8460/qda2iooldodnyz3jcc2v3xh2j",
  "containerimage.config.digest": "sha256:0ac3d33fd5d89d55c60f22ee7447c334b63a67a20de7a803c8ae56aab506daa3",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:0be1befbe32da4365af6a3e7c86d1a5ac0f70b2b014b70c501fb32a9619cb36b",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:0be1befbe32da4365af6a3e7c86d1a5ac0f70b2b014b70c501fb32a9619cb36b",
  "image.name": "ghcr.io/graphql-hive/app:1ff10816d44ee474c802b10c555435fe75f98016-arm64,ghcr.io/graphql-hive/app:wip_organization_access_tokens-arm64"
},
"buildx.build.warnings": [
  {
    "vertex": "sha256:9990a4502c9b1bfe3e00af65bdf72c5fcec88aa282e2090ec57b0214cafdc537",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9ERVNDUklQVElPTicgKGxpbmUgMTQp",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 14
        },
        "end": {
          "line": 14
        }
      }
    ]
  },
  {
    "vertex": "sha256:368c92bff6a328559d46cc19c7a755dc09d6f30ba6d05c91bd66a042bef4f11f",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9USVRMRScgKGxpbmUgMTUp",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 15
        },
        "end": {
          "line": 15
        }
      }
    ]
  },
  {
    "vertex": "sha256:368c92bff6a328559d46cc19c7a755dc09d6f30ba6d05c91bd66a042bef4f11f",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9ERVNDUklQVElPTicgKGxpbmUgMTcp",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 17
        },
        "end": {
          "line": 17
        }
      }
    ]
  },
  {
    "vertex": "sha256:9990a4502c9b1bfe3e00af65bdf72c5fcec88aa282e2090ec57b0214cafdc537",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDIwKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 20
        },
        "end": {
          "line": 20
        }
      }
    ]
  },
  {
    "vertex": "sha256:9990a4502c9b1bfe3e00af65bdf72c5fcec88aa282e2090ec57b0214cafdc537",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDIxKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 21
        },
        "end": {
          "line": 21
        }
      }
    ]
  },
  {
    "vertex": "sha256:9990a4502c9b1bfe3e00af65bdf72c5fcec88aa282e2090ec57b0214cafdc537",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDIyKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 22
        },
        "end": {
          "line": 22
        }
      }
    ]
  },
  {
    "vertex": "sha256:368c92bff6a328559d46cc19c7a755dc09d6f30ba6d05c91bd66a042bef4f11f",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDEwKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 10
        },
        "end": {
          "line": 10
        }
      }
    ]
  },
  {
    "vertex": "sha256:368c92bff6a328559d46cc19c7a755dc09d6f30ba6d05c91bd66a042bef4f11f",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDEyKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 12
        },
        "end": {
          "line": 12
        }
      }
    ]
  },
  {
    "vertex": "sha256:9990a4502c9b1bfe3e00af65bdf72c5fcec88aa282e2090ec57b0214cafdc537",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRSRUxFQVNFJyAobGluZSAxMyk=",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 13
        },
        "end": {
          "line": 13
        }
      }
    ]
  },
  {
    "vertex": "sha256:9990a4502c9b1bfe3e00af65bdf72c5fcec88aa282e2090ec57b0214cafdc537",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRQT1JUJyAobGluZSAyMik=",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 22
        },
        "end": {
          "line": 22
        }
      }
    ]
  },
  {
    "vertex": "sha256:9990a4502c9b1bfe3e00af65bdf72c5fcec88aa282e2090ec57b0214cafdc537",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9USVRMRScgKGxpbmUgMTIp",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 12
        },
        "end": {
          "line": 12
        }
      }
    ]
  },
  {
    "vertex": "sha256:9990a4502c9b1bfe3e00af65bdf72c5fcec88aa282e2090ec57b0214cafdc537",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRSRUxFQVNFJyAobGluZSAyMSk=",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 21
        },
        "end": {
          "line": 21
        }
      }
    ]
  },
  {
    "vertex": "sha256:368c92bff6a328559d46cc19c7a755dc09d6f30ba6d05c91bd66a042bef4f11f",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDExKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 11
        },
        "end": {
          "line": 11
        }
      }
    ]
  },
  {
    "vertex": "sha256:368c92bff6a328559d46cc19c7a755dc09d6f30ba6d05c91bd66a042bef4f11f",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRSRUxFQVNFJyAobGluZSAxMik=",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 12
        },
        "end": {
          "line": 12
        }
      }
    ]
  }
],
"composition-federation-2": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "Federation 2 Composition Service for GraphQL Hive.",
          "build-arg:IMAGE_TITLE": "graphql-hive/composition-federation-2",
          "build-arg:PORT": "3069",
          "build-arg:RELEASE": "1ff10816d44ee474c802b10c555435fe75f98016",
          "build-arg:SERVICE_DIR_NAME": "@hive/external-composition",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:dockerfile": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:shared": "xerowlb9b6lm8x6mj03utdlls"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-fce81dbe-7ae6-4180-89da-ac73c7f8d846/builder-fce81dbe-7ae6-4180-89da-ac73c7f8d8460/nse9b0zjyxns8m0uto55aq19j",
  "containerimage.config.digest": "sha256:f13271a41b202397991532303193bd11c111d121ffb5cae3c0c132fca042500e",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:e94e243a68c9f5b8cf12c68108a5e3ac0e6c053dc95e957fc6bbd687b90d87ac",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:e94e243a68c9f5b8cf12c68108a5e3ac0e6c053dc95e957fc6bbd687b90d87ac",
  "image.name": "ghcr.io/graphql-hive/composition-federation-2:1ff10816d44ee474c802b10c555435fe75f98016-arm64,ghcr.io/graphql-hive/composition-federation-2:wip_organization_access_tokens-arm64"
},
"emails": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The emails service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/emails",
          "build-arg:PORT": "3006",
          "build-arg:RELEASE": "1ff10816d44ee474c802b10c555435fe75f98016",
          "build-arg:SERVICE_DIR_NAME": "@hive/emails",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:dockerfile": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:shared": "xerowlb9b6lm8x6mj03utdlls"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-fce81dbe-7ae6-4180-89da-ac73c7f8d846/builder-fce81dbe-7ae6-4180-89da-ac73c7f8d8460/fwizv6kd52v339ky1gamouhi0",
  "containerimage.config.digest": "sha256:ceee69fb8bbcae5448f14f2381d5a4df407f21da281395827b427fcc952c5ff4",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:10838c270a1377b508caf48b2f6dc58b989ce544b9477536169733a6742f68af",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:10838c270a1377b508caf48b2f6dc58b989ce544b9477536169733a6742f68af",
  "image.name": "ghcr.io/graphql-hive/emails:1ff10816d44ee474c802b10c555435fe75f98016-arm64,ghcr.io/graphql-hive/emails:wip_organization_access_tokens-arm64"
},
"policy": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The policy service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/policy",
          "build-arg:PORT": "3012",
          "build-arg:RELEASE": "1ff10816d44ee474c802b10c555435fe75f98016",
          "build-arg:SERVICE_DIR_NAME": "@hive/policy",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:dockerfile": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:shared": "xerowlb9b6lm8x6mj03utdlls"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-fce81dbe-7ae6-4180-89da-ac73c7f8d846/builder-fce81dbe-7ae6-4180-89da-ac73c7f8d8460/1bdir9ze87v4n8u48ii2bqru1",
  "containerimage.config.digest": "sha256:6df31dea03da4ea077143ba662185e8598160c26c599d86ebf0a3a22543a4067",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:c88431804706ddb7fa556acad8acfa3854f2e65d39c932727e5dad08da7838a4",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:c88431804706ddb7fa556acad8acfa3854f2e65d39c932727e5dad08da7838a4",
  "image.name": "ghcr.io/graphql-hive/policy:1ff10816d44ee474c802b10c555435fe75f98016-arm64,ghcr.io/graphql-hive/policy:wip_organization_access_tokens-arm64"
},
"rate-limit": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The rate limit service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/rate-limit",
          "build-arg:PORT": "3009",
          "build-arg:RELEASE": "1ff10816d44ee474c802b10c555435fe75f98016",
          "build-arg:SERVICE_DIR_NAME": "@hive/rate-limit",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:dockerfile": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:shared": "xerowlb9b6lm8x6mj03utdlls"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-fce81dbe-7ae6-4180-89da-ac73c7f8d846/builder-fce81dbe-7ae6-4180-89da-ac73c7f8d8460/cwxd729jwdmtlngklh0wg5w1c",
  "containerimage.config.digest": "sha256:4a8090bc9123871894698f9b586eb062945ab4c4ffc4ebc0d92af02471b50dfc",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:a11778b0c332aaadd8713a46c6b7f6a3dffe6ea14252a329f1baa176b4a57c11",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:a11778b0c332aaadd8713a46c6b7f6a3dffe6ea14252a329f1baa176b4a57c11",
  "image.name": "ghcr.io/graphql-hive/rate-limit:1ff10816d44ee474c802b10c555435fe75f98016-arm64,ghcr.io/graphql-hive/rate-limit:wip_organization_access_tokens-arm64"
},
"schema": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The schema service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/schema",
          "build-arg:PORT": "3002",
          "build-arg:RELEASE": "1ff10816d44ee474c802b10c555435fe75f98016",
          "build-arg:SERVICE_DIR_NAME": "@hive/schema",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:dockerfile": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:shared": "xerowlb9b6lm8x6mj03utdlls"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-fce81dbe-7ae6-4180-89da-ac73c7f8d846/builder-fce81dbe-7ae6-4180-89da-ac73c7f8d8460/tw7zc2px7cnpcc6ll2qe2fsf4",
  "containerimage.config.digest": "sha256:1bec9e23909b031bbcc211b2b86648a745568cf6356eb6ab70fdd9cd4d557bfb",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:4416fc7fd637fbf2dc1a8565b2e9097b386332d351ab3899c1e31c4994a2a02a",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:4416fc7fd637fbf2dc1a8565b2e9097b386332d351ab3899c1e31c4994a2a02a",
  "image.name": "ghcr.io/graphql-hive/schema:1ff10816d44ee474c802b10c555435fe75f98016-arm64,ghcr.io/graphql-hive/schema:wip_organization_access_tokens-arm64"
},
"server": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The server service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/server",
          "build-arg:PORT": "3001",
          "build-arg:RELEASE": "1ff10816d44ee474c802b10c555435fe75f98016",
          "build-arg:SERVICE_DIR_NAME": "@hive/server",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:dockerfile": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:shared": "xerowlb9b6lm8x6mj03utdlls"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-fce81dbe-7ae6-4180-89da-ac73c7f8d846/builder-fce81dbe-7ae6-4180-89da-ac73c7f8d8460/iqd24h6hy3c0t38mi0v6463c3",
  "containerimage.config.digest": "sha256:021a78c037eebd561e64e3baabfc19977514d61441efcc09747feeff1cf892d9",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:b2f40d5b823d195bc6ed69727d900626dc81053f755f3b6c628c98dc1840ee47",
    "size": 2076,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:b2f40d5b823d195bc6ed69727d900626dc81053f755f3b6c628c98dc1840ee47",
  "image.name": "ghcr.io/graphql-hive/server:1ff10816d44ee474c802b10c555435fe75f98016-arm64,ghcr.io/graphql-hive/server:wip_organization_access_tokens-arm64"
},
"storage": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "migrations.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:IMAGE_DESCRIPTION": "The migrations service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/storage",
          "build-arg:RELEASE": "1ff10816d44ee474c802b10c555435fe75f98016",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:dockerfile": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:shared": "xerowlb9b6lm8x6mj03utdlls"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-fce81dbe-7ae6-4180-89da-ac73c7f8d846/builder-fce81dbe-7ae6-4180-89da-ac73c7f8d8460/l27p2lwee3375srjx0f0vya4q",
  "containerimage.config.digest": "sha256:bc552f0a77cbfc3cb4030245f01d8c8acbc2bb623d3af7be270c758dc6a0338f",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:37ea3f38daf1226eef8b38e16c04be4b47f7d7c0e6b39afd728e4f1e6e2fffcc",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:37ea3f38daf1226eef8b38e16c04be4b47f7d7c0e6b39afd728e4f1e6e2fffcc",
  "image.name": "ghcr.io/graphql-hive/storage:1ff10816d44ee474c802b10c555435fe75f98016-arm64,ghcr.io/graphql-hive/storage:wip_organization_access_tokens-arm64"
},
"stripe-billing": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The stripe billing service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/stripe-billing",
          "build-arg:PORT": "3010",
          "build-arg:RELEASE": "1ff10816d44ee474c802b10c555435fe75f98016",
          "build-arg:SERVICE_DIR_NAME": "@hive/stripe-billing",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:dockerfile": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:shared": "xerowlb9b6lm8x6mj03utdlls"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-fce81dbe-7ae6-4180-89da-ac73c7f8d846/builder-fce81dbe-7ae6-4180-89da-ac73c7f8d8460/ztm6df30fuc5whyi8k11aukk3",
  "containerimage.config.digest": "sha256:e92c4c662838044b10ffef2569161d967ebe93634059120fef205e47706911da",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:8f709842010ecd57a7f709c7e37ade57b849c05dd6781fd9d4a37edaf571194a",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:8f709842010ecd57a7f709c7e37ade57b849c05dd6781fd9d4a37edaf571194a",
  "image.name": "ghcr.io/graphql-hive/stripe-billing:1ff10816d44ee474c802b10c555435fe75f98016-arm64,ghcr.io/graphql-hive/stripe-billing:wip_organization_access_tokens-arm64"
},
"tokens": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The tokens service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/tokens",
          "build-arg:PORT": "3003",
          "build-arg:RELEASE": "1ff10816d44ee474c802b10c555435fe75f98016",
          "build-arg:SERVICE_DIR_NAME": "@hive/tokens",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:dockerfile": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:shared": "xerowlb9b6lm8x6mj03utdlls"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-fce81dbe-7ae6-4180-89da-ac73c7f8d846/builder-fce81dbe-7ae6-4180-89da-ac73c7f8d8460/xvslgoacmd8piflc543gwy2w7",
  "containerimage.config.digest": "sha256:b95761f97a3e233e989f7a2828e3846dd95051bb940ed196cdd32fd7862c4618",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:60e5da36435cf1acbba1ec2c6872b52fe9a5d63c097a8c80b86f1e0baa9e835f",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:60e5da36435cf1acbba1ec2c6872b52fe9a5d63c097a8c80b86f1e0baa9e835f",
  "image.name": "ghcr.io/graphql-hive/tokens:1ff10816d44ee474c802b10c555435fe75f98016-arm64,ghcr.io/graphql-hive/tokens:wip_organization_access_tokens-arm64"
},
"usage": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The usage ingestor service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/usage",
          "build-arg:PORT": "3006",
          "build-arg:RELEASE": "1ff10816d44ee474c802b10c555435fe75f98016",
          "build-arg:SERVICE_DIR_NAME": "@hive/usage",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:dockerfile": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:shared": "xerowlb9b6lm8x6mj03utdlls"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-fce81dbe-7ae6-4180-89da-ac73c7f8d846/builder-fce81dbe-7ae6-4180-89da-ac73c7f8d8460/t3tmdmcm42scb63z62invhwex",
  "containerimage.config.digest": "sha256:20aa814f5c7b9a51d1daf320756c6cd5c3b86243abdec335dc86b71c5f6d0fcf",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:4f9806c4c8b6de34ee1ffba1b239e55977c36f0efc48177587ccb5b1b737ad33",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:4f9806c4c8b6de34ee1ffba1b239e55977c36f0efc48177587ccb5b1b737ad33",
  "image.name": "ghcr.io/graphql-hive/usage:1ff10816d44ee474c802b10c555435fe75f98016-arm64,ghcr.io/graphql-hive/usage:wip_organization_access_tokens-arm64"
},
"usage-estimator": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The usage estimator service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/usage-estimator",
          "build-arg:PORT": "3008",
          "build-arg:RELEASE": "1ff10816d44ee474c802b10c555435fe75f98016",
          "build-arg:SERVICE_DIR_NAME": "@hive/usage-estimator",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:dockerfile": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:shared": "xerowlb9b6lm8x6mj03utdlls"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-fce81dbe-7ae6-4180-89da-ac73c7f8d846/builder-fce81dbe-7ae6-4180-89da-ac73c7f8d8460/v78vse5eiy6v4mncl5ptc4hc5",
  "containerimage.config.digest": "sha256:3948e584cc38d49e0cd8df6cd2fb5424beb614faadd89d177d780f6322a579f9",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:324972fc696d540e4da6f6afb094a0a9e4af363cc204cb718e8a527bd586fe67",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:324972fc696d540e4da6f6afb094a0a9e4af363cc204cb718e8a527bd586fe67",
  "image.name": "ghcr.io/graphql-hive/usage-estimator:1ff10816d44ee474c802b10c555435fe75f98016-arm64,ghcr.io/graphql-hive/usage-estimator:wip_organization_access_tokens-arm64"
},
"usage-ingestor": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The usage ingestor service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/usage-ingestor",
          "build-arg:PORT": "3007",
          "build-arg:RELEASE": "1ff10816d44ee474c802b10c555435fe75f98016",
          "build-arg:SERVICE_DIR_NAME": "@hive/usage-ingestor",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:dockerfile": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:shared": "xerowlb9b6lm8x6mj03utdlls"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-fce81dbe-7ae6-4180-89da-ac73c7f8d846/builder-fce81dbe-7ae6-4180-89da-ac73c7f8d8460/cnhz3sr4wu7nz7vd32h5ljr97",
  "containerimage.config.digest": "sha256:abf5eec56d25ad4b7b27d950efc4164a20bbaf95bdb23374d784b37466e2a357",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:d0a98df163c4f82b24ab012880d3739e47060a86b829472113e99cfff6caa10a",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:d0a98df163c4f82b24ab012880d3739e47060a86b829472113e99cfff6caa10a",
  "image.name": "ghcr.io/graphql-hive/usage-ingestor:1ff10816d44ee474c802b10c555435fe75f98016-arm64,ghcr.io/graphql-hive/usage-ingestor:wip_organization_access_tokens-arm64"
},
"webhooks": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Farm64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The webhooks ingestor service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/webhooks",
          "build-arg:PORT": "3005",
          "build-arg:RELEASE": "1ff10816d44ee474c802b10c555435fe75f98016",
          "build-arg:SERVICE_DIR_NAME": "@hive/webhooks",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:dockerfile": "xerowlb9b6lm8x6mj03utdlls",
          "local-sessionid:shared": "xerowlb9b6lm8x6mj03utdlls"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/arm64"
      }
    }
  },
  "buildx.build.ref": "builder-fce81dbe-7ae6-4180-89da-ac73c7f8d846/builder-fce81dbe-7ae6-4180-89da-ac73c7f8d8460/idq3blt6g2wtxzl91ptq6uxts",
  "containerimage.config.digest": "sha256:668d5861341a0264a975ba66c0eb9469384470aa0898bbb621e4634c0f929966",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:4d677d989afc3e74269c63392337d90189f96cafa6cf83e5dfbbc6b6a6bb25b2",
    "size": 2075,
    "platform": {
      "architecture": "arm64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:4d677d989afc3e74269c63392337d90189f96cafa6cf83e5dfbbc6b6a6bb25b2",
  "image.name": "ghcr.io/graphql-hive/webhooks:1ff10816d44ee474c802b10c555435fe75f98016-arm64,ghcr.io/graphql-hive/webhooks:wip_organization_access_tokens-arm64"
}
}

@n1ru4l n1ru4l force-pushed the wip-organization-access-tokens branch from d1be7ea to 66165f3 Compare February 12, 2025 10:39
@n1ru4l n1ru4l force-pushed the wip-organization-access-tokens branch from f258b53 to 58fc5bf Compare February 12, 2025 11:46
Copy link
Contributor Author

@n1ru4l n1ru4l Feb 12, 2025

Choose a reason for hiding this comment

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

The logic here is shared between organization members and organization access tokens, so I moved it form the organization members to this new shared file.

Comment on lines 1009 to +1010
await this.session.assertPerformAction({
action: 'schema:loadFromRegistry',
action: 'project:describe',
Copy link
Contributor Author

Choose a reason for hiding this comment

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

schema:loadFromRegistry is too specific. Instead ,it should be project:describe access that must be granted to the access token. Since we do not expose the permission to the outside world, this change is safe to perform.

Comment on lines +9 to +10
+ "./build/src/drivers/redis": "./build/src/drivers/redis.js",
+ "./build/src/drivers/memory": "./build/src/drivers/memory.js",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we have an esm <-> ts module resolution conflict. Adding these files to the exports map unblocked me from importing the modules without typescript errors.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Almost all changes here are related to moving the resource assignment logic to a new standalone file providers/resource-assignments.ts

Comment on lines 40 to +43
/**
* Resolved resource groups, used for runtime permission checks.
* Resolved policy statements
*/
resolvedResources: ResolvedResourceAssignments;
authorizationPolicyStatements: AuthorizationPolicyStatement[];
Copy link
Contributor Author

@n1ru4l n1ru4l Feb 12, 2025

Choose a reason for hiding this comment

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

This change is not related to moving resource assignment logic to a different file.

I reduced the complexity here a bit. Before we created the ResolvedResourceAssignments object which we transformed to AuthorizationPolicyStatement within auth/lib/supertokens-strategy.ts.

Now we already do that here (lazily using a getter function).

@n1ru4l
Copy link
Contributor Author

n1ru4l commented Feb 12, 2025

@coderabbitai review

Copy link
Contributor

coderabbitai bot commented Feb 12, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 7

🔭 Outside diff range comments (1)
packages/services/api/src/modules/organization/lib/organization-member-permissions.ts (1)

124-127: ⚠️ Potential issue

Fix incorrect permission description.

The description for 'project:delete' permission is incorrect. It currently states "Member can access the specified projects" which should describe the deletion capability instead.

Apply this diff to fix the description:

-        description: 'Member can access the specified projects.',
+        description: 'Member can delete the specified projects.',
🧹 Nitpick comments (18)
packages/services/api/src/modules/auth/lib/organization-access-token-strategy.ts (1)

43-99: Consider caching token verification
You have a // TODO: we should probably cache this verification. Implementing a short-lived cache for token verification results can reduce repeated cryptographic operations and improve performance, especially if multiple requests use the same token.

packages/services/api/src/modules/organization/providers/organization-access-tokens.ts (2)

25-37: Consider expanding allowed characters
TitleInputModel uses [ a-zA-Z0-9_-]+. If you anticipate titles with international characters or user-defined naming, consider relaxing or extending this regex. Otherwise, this is fine for a strictly limited input set.


38-68: Validate 'createdAt' more strictly
Currently 'createdAt' is validated as a simple string. For stronger guarantees, consider z.string().datetime() or a refinement to ensure it matches a predictable format (e.g., ISO 8601).

packages/services/api/src/modules/organization/providers/organization-members.ts (2)

40-43: Doc comment accurately explains the new field
Providing context for authorizationPolicyStatements clarifies how resolved policies fit into membership. Consider elaborating on whether it’s stored in memory only or persisted elsewhere.


133-145: On-demand resource resolution
This getter dynamically computes policy statements. If performance becomes an issue, consider memoizing the result within the same session or membership object.

packages/services/api/src/modules/organization/module.graphql.ts (2)

51-82: Consider using an enum for permissions.

Currently, permissions: [String!]! is free-form. Using an enum instead would help prevent undefined permission strings and provide stricter validation.


107-108: Harmonize error details.

For consistency with other error types that include detailed fields, consider adding an optional details property if additional debugging info is needed.

packages/services/api/src/modules/organization/providers/resource-assignments.ts (2)

88-183: Ensure logging and monitoring around database lookups.

The constructor and class scaffolding look fine. However, multiple calls to storage.findProjectsByIds can become performance hotspots. Monitor or cache as needed.


479-513: Ensure stable resource identifier format.

The toResourceIdentifier method is well structured. Keep an eye on future expansions in resource hierarchy, as it may affect the HRN scheme.

packages/services/api/src/modules/organization/lib/permissions.ts (1)

3-10: Well-defined PermissionRecord.

Fields like dependsOn and warning provide flexibility for advanced permission handling. Just confirm that isReadyOnly is spelled correctly / intended as “read-only.”

packages/services/api/src/modules/organization/lib/organization-access-key.ts (2)

61-75: Enhance secret generation robustness.

While the current implementation is secure, consider using a more robust secret generation approach.

Apply this diff to enhance secret generation:

 export async function create(id: string) {
-  const secret = Crypto.createHash('sha256')
-    .update(Crypto.randomBytes(20).toString())
-    .digest('hex');
+  const secret = Crypto.randomBytes(32).toString('base64url');
 
   const hash = await bcrypt.hash(secret, await bcrypt.genSalt());
   const privateAccessToken = encode(id, secret);
   const firstCharacters = privateAccessToken.substr(0, 10);
 
   return {
     privateAccessToken,
     hash,
     firstCharacters,
   };
 }

21-21: Improve error message specificity.

The current error message "Invalid access token" could be more specific about what exactly is invalid.

Apply this diff to provide more specific error messages:

-const decodeError = { type: 'error' as const, reason: 'Invalid access token.' };
+function createError(reason: string) {
+  return { type: 'error' as const, reason };
+}

 export function decode(
   accessToken: string,
 ): { type: 'error'; reason: string } | { type: 'ok'; accessKey: DecodedAccessKey } {
   if (!accessToken.startsWith(keyPrefix)) {
-    return decodeError;
+    return createError('Invalid token prefix. Expected "hvo1/".');
   }
 
   accessToken = accessToken.slice(keyPrefix.length);
 
   let str: string;
 
   try {
     str = globalThis.atob(accessToken);
   } catch (error) {
-    return decodeError;
+    return createError('Invalid base64 encoding.');
   }
 
   const parts = str.split(':');
 
   if (parts.length > 2) {
-    return decodeError;
+    return createError('Invalid token format. Too many segments.');
   }
 
   const id = parts.at(0);
   const privateKey = parts.at(1);
 
   if (id && privateKey) {
     return { type: 'ok', accessKey: { id, privateKey } } as const;
   }
 
-  return decodeError;
+  return createError('Invalid token format. Missing required segments.');
 }

Also applies to: 28-59

packages/services/api/src/modules/organization/providers/organization-access-tokens-cache.ts (1)

29-43: Consider making cache configuration more configurable.

The cache configuration values (maxItems, TTL, grace period) are hardcoded. Consider making these configurable through environment variables or configuration files.

Apply this diff to make the configuration more flexible:

+const CACHE_CONFIG = {
+  maxItems: process.env.CACHE_MAX_ITEMS ? parseInt(process.env.CACHE_MAX_ITEMS, 10) : 10_000,
+  ttl: process.env.CACHE_TTL || '5min',
+  grace: process.env.CACHE_GRACE || '24h',
+  prefix: 'bentocache:organization-access-tokens',
+};

   constructor(
     @Inject(REDIS_INSTANCE) redis: Redis,
     @Inject(PG_POOL_CONFIG) pool: DatabasePool,
     logger: Logger,
   ) {
     this.findById = findById({ pool, logger });
     this.cache = new BentoCache({
       default: 'organizationAccessTokens',
       stores: {
         organizationAccessTokens: bentostore()
           .useL1Layer(
             memoryDriver({
-              maxItems: 10_000,
-              prefix: 'bentocache:organization-access-tokens',
+              maxItems: CACHE_CONFIG.maxItems,
+              prefix: CACHE_CONFIG.prefix,
             }),
           )
           .useL2Layer(
             redisDriver({
               connection: redis,
-              prefix: 'bentocache:organization-access-tokens'
+              prefix: CACHE_CONFIG.prefix
             }),
           ),
       },
     });
   }
integration-tests/tests/api/organization-access-tokens.spec.ts (3)

51-81: Consider adding more assertions for the success case.

While the test verifies the basic success case, consider adding assertions for:

  • The format of the generated privateAccessKey
  • The format of the generated id
  • The timestamp format and range of createdAt

94-95: Consider using a more descriptive test title.

The title "a access token" could be more descriptive, e.g., "Test Access Token" or "Integration Test Token".

Also applies to: 126-127


177-223: Consider testing additional permission combinations.

The test only verifies organization:describe and project:describe permissions. Consider testing:

  • Different permission combinations
  • Edge cases with minimal and maximal permissions
  • Invalid permission strings
packages/services/storage/src/db/types.ts (1)

159-169: Consider adding JSDoc comments for the interface.

Adding documentation for the interface and its fields would improve code maintainability.

+/**
+ * Represents an organization access token in the database.
+ */
 export interface organization_access_tokens {
+  /** The unique identifier of the token */
   id: string;
+  /** The organization this token belongs to */
   organization_id: string;
+  /** When the token was created */
   created_at: Date;
+  /** User-provided title for the token */
   title: string;
+  /** User-provided description for the token */
   description: string;
+  /** List of permissions granted to this token */
   permissions: Array<string>;
+  /** Resources this token has access to */
   assigned_resources: any | null;
+  /** Hash of the token for validation */
   hash: string | null;
+  /** First few characters of the token for display */
   first_characters: string | null;
 }
packages/services/api/src/modules/auth/lib/authz.ts (1)

371-371: Consider documenting the purpose of usage:report permission.

The new usage:report permission's purpose and scope should be documented for clarity.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2b01750 and 58fc5bf.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (33)
  • integration-tests/tests/api/organization-access-tokens.spec.ts (1 hunks)
  • package.json (1 hunks)
  • packages/migrations/src/actions/2025.01.30T00-02-03.organization-access-tokens.ts (1 hunks)
  • packages/migrations/src/run-pg-migrations.ts (1 hunks)
  • packages/services/api/package.json (1 hunks)
  • packages/services/api/src/modules/auth/lib/authz.ts (2 hunks)
  • packages/services/api/src/modules/auth/lib/organization-access-token-strategy.ts (1 hunks)
  • packages/services/api/src/modules/auth/lib/supertokens-strategy.ts (2 hunks)
  • packages/services/api/src/modules/auth/lib/target-access-token-strategy.ts (0 hunks)
  • packages/services/api/src/modules/auth/module.graphql.mappers.ts (1 hunks)
  • packages/services/api/src/modules/organization/index.ts (2 hunks)
  • packages/services/api/src/modules/organization/lib/organization-access-key.ts (1 hunks)
  • packages/services/api/src/modules/organization/lib/organization-access-token-permissions.ts (1 hunks)
  • packages/services/api/src/modules/organization/lib/organization-member-permissions.ts (4 hunks)
  • packages/services/api/src/modules/organization/lib/permissions.ts (1 hunks)
  • packages/services/api/src/modules/organization/module.graphql.mappers.ts (2 hunks)
  • packages/services/api/src/modules/organization/module.graphql.ts (2 hunks)
  • packages/services/api/src/modules/organization/providers/organization-access-tokens-cache.ts (1 hunks)
  • packages/services/api/src/modules/organization/providers/organization-access-tokens.ts (1 hunks)
  • packages/services/api/src/modules/organization/providers/organization-manager.ts (3 hunks)
  • packages/services/api/src/modules/organization/providers/organization-members.ts (3 hunks)
  • packages/services/api/src/modules/organization/providers/resource-assignments.spec.ts (1 hunks)
  • packages/services/api/src/modules/organization/providers/resource-assignments.ts (1 hunks)
  • packages/services/api/src/modules/organization/resolvers/Member.ts (2 hunks)
  • packages/services/api/src/modules/organization/resolvers/Mutation/createOrganizationAccessToken.ts (1 hunks)
  • packages/services/api/src/modules/organization/resolvers/Mutation/deleteOrganizationAccessToken.ts (1 hunks)
  • packages/services/api/src/modules/organization/resolvers/Organization.ts (3 hunks)
  • packages/services/api/src/modules/organization/resolvers/OrganizationAccessToken.ts (1 hunks)
  • packages/services/api/src/modules/schema/providers/schema-manager.ts (2 hunks)
  • packages/services/api/src/modules/shared/providers/id-translator.ts (2 hunks)
  • packages/services/server/src/index.ts (2 hunks)
  • packages/services/storage/src/db/types.ts (2 hunks)
  • patches/bentocache.patch (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/services/api/src/modules/auth/lib/target-access-token-strategy.ts
✅ Files skipped from review due to trivial changes (2)
  • package.json
  • packages/services/api/src/modules/organization/providers/resource-assignments.spec.ts
🧰 Additional context used
📓 Path-based instructions (2)
`packages/services/**`: Microservices written in NodeJS and ...

packages/services/**: Microservices written in NodeJS and TypeScript. Most of the dirs under this directory are packages and deployed as Docker packages.
Interaction between services is done using tRPC.
We prefer writing code that does input/output validations with Zod.
The directories that ends with -worker are projects that are built on top of CloudFlare Workers infrastructure and deployed there.
The cdn-worker is a special one, it serves a high-available, detached CDN on CloudFlare Workers. This is how our end-users fetches the crucial data from our platform, in a way that does not depend/couple on our main API (served as GraphQL API).

  • packages/services/api/package.json
  • packages/services/api/src/modules/organization/resolvers/OrganizationAccessToken.ts
  • packages/services/storage/src/db/types.ts
  • packages/services/api/src/modules/organization/resolvers/Mutation/deleteOrganizationAccessToken.ts
  • packages/services/api/src/modules/organization/providers/organization-manager.ts
  • packages/services/api/src/modules/organization/providers/organization-access-tokens-cache.ts
  • packages/services/api/src/modules/auth/lib/authz.ts
  • packages/services/api/src/modules/organization/resolvers/Member.ts
  • packages/services/api/src/modules/organization/module.graphql.mappers.ts
  • packages/services/api/src/modules/schema/providers/schema-manager.ts
  • packages/services/api/src/modules/auth/module.graphql.mappers.ts
  • packages/services/api/src/modules/organization/resolvers/Mutation/createOrganizationAccessToken.ts
  • packages/services/api/src/modules/organization/lib/organization-access-token-permissions.ts
  • packages/services/api/src/modules/organization/resolvers/Organization.ts
  • packages/services/api/src/modules/organization/index.ts
  • packages/services/api/src/modules/auth/lib/supertokens-strategy.ts
  • packages/services/api/src/modules/organization/lib/organization-member-permissions.ts
  • packages/services/api/src/modules/organization/lib/permissions.ts
  • packages/services/api/src/modules/organization/lib/organization-access-key.ts
  • packages/services/api/src/modules/organization/providers/organization-members.ts
  • packages/services/server/src/index.ts
  • packages/services/api/src/modules/auth/lib/organization-access-token-strategy.ts
  • packages/services/api/src/modules/shared/providers/id-translator.ts
  • packages/services/api/src/modules/organization/providers/resource-assignments.ts
  • packages/services/api/src/modules/organization/module.graphql.ts
  • packages/services/api/src/modules/organization/providers/organization-access-tokens.ts
`integration-tests/**`: integration tests for ensuring featu...

integration-tests/**: integration tests for ensuring features and important flows are running. Usually, changes to the packages/ directory will affect this. In ideal flow, each PR should include integration tests.

  • integration-tests/tests/api/organization-access-tokens.spec.ts
🔇 Additional comments (48)
packages/services/api/src/modules/auth/lib/organization-access-token-strategy.ts (4)

1-5: No issues with initial imports
These imports are straightforward and appropriate for the module’s functionality.


7-30: Session extension logic looks solid
OrganizationAccessTokenSession nicely extends Session. The overridden loadPolicyStatementsForOrganization method returns statically assigned policies, which is fine as long as the session remains short-lived or policy changes are infrequent.


32-36: Class definition is coherent
OrganizationAccessTokenStrategy extends AuthNStrategy with a clear type parameter. Storing logger and cache as private fields is good practice.


37-41: Constructor usage
Accepting deps as a single object is a neat approach for dependency injection. Creates a well-scoped logger child for this strategy.

packages/services/api/src/modules/organization/providers/organization-access-tokens.ts (3)

93-174: Creation flow appears robust
The create method handles input validation (title, description), checks permissions, and stores the new token. It returns a structured success or error object. The approach is consistent and modular. Consider wrapping everything in a transaction if you plan on adding more DB actions in a single operation later.


176-202: Properly checks permissions before deleting
The delete method ensures the record exists, verifies permissions, removes the entry, and purges from cache. This is a good pattern for consistent resource cleanup.


209-251: Straightforward retrieval with logging
findById correctly validates the ID as UUID, retrieves the token, parses it through the Zod model, and logs relevant details. Returning null if nothing is found is clear and easy to handle.

packages/services/api/src/modules/organization/providers/organization-members.ts (2)

4-6: Imports are well chosen
Adding type Organization and AuthorizationPolicyStatement helps clarify usage without over-importing.


10-15: Sound modular approach
Using the resource-assignments module promotes better separation of concerns. This should simplify maintenance and future enhancements.

packages/services/api/src/modules/organization/module.graphql.ts (5)

38-44: Consider validating input arguments.

The new mutations appear correct overall, but ensure proper input validation for security and consistency.


46-49: Verify one-of directive usage.

Confirm that @oneOf is recognized and validated in your GraphQL setup to ensure that exactly one field is used.


85-92: Looks good.

The OrganizationAccessToken type is consistent with the creation input.


94-105: Straightforward deletion schema.

The input and result types for deleting an organization access token appear clearly defined.


318-321: Confirm resolver implementation.

Ensure that your resolver or data loader populates availableOrganizationPermissionGroups correctly. Consider handling edge cases when no permissions exist.

packages/services/api/src/modules/organization/providers/resource-assignments.ts (6)

1-37: Validate domain logic for assignment modes.

Your Zod schemas for wildcard vs. granular modes look well-structured. Ensure consistent handling throughout your codebase where these modes are interpreted.


38-87: Check for performance in nested loops.

Union-based assignments (e.g., AssignedServicesModel) are quite flexible. Bear in mind that nested transformations can be costly if used frequently. Consider caching or batching in higher call volumes.


185-324: Comprehensive resource filtering.

The logic to filter out non-existent or non-org-bound Projects/Targets is essential for preventing privilege escalation. Great job. Ensure thorough testing of edge cases.


327-329: Small utility isSome.

The isSome function is concise and effective. No issues here.


365-473: Very clear resource resolution.

resolveResourceAssignment correctly handles wildcard expansions. Validate that front-end or other callers handle the varying data structures (arrays vs. single object).


515-563: Check for missing policies.

translateResolvedResourcesToAuthorizationPolicyStatements allows for dynamic policy generation. Verify that each permission group is tested to avoid overlooked actions.

packages/services/api/src/modules/organization/lib/permissions.ts (1)

12-16: PermissionGroup structure is clear.

The grouping logic is solid. Ensure the UI or relevant clients properly render nested permissions if needed.

patches/bentocache.patch (1)

9-10: LGTM! The additional export mappings resolve module resolution conflicts.

The new export mappings provide alternative paths for accessing the Redis and memory drivers, which aligns with the existing exports and resolves the ESM <-> TS module resolution conflict.

packages/services/api/src/modules/organization/module.graphql.mappers.ts (1)

6-6: LGTM! The new access token mapper follows established patterns.

The addition of OrganizationAccessTokenMapper is consistent with the existing mapper types and aligns with the PR's objective of introducing organization access tokens.

Also applies to: 17-17

packages/migrations/src/actions/2025.01.30T00-02-03.organization-access-tokens.ts (1)

18-22: LGTM! Index optimization looks good.

The composite index on (organization_id, created_at DESC, id DESC) is well-designed for:

  1. Filtering by organization
  2. Sorting by creation date
  3. Ensuring deterministic ordering with id
packages/services/api/src/modules/organization/index.ts (1)

16-23: Verify provider dependency order.

The order of providers in the array matters for dependency injection. Ensure that providers are listed in the correct dependency order, with base providers (those with no dependencies) first, followed by providers that depend on them.

Run this script to analyze provider dependencies:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Description: Check provider dependencies by searching for injector.get() calls

echo "Analyzing provider dependencies..."

# Search for dependencies in OrganizationAccessTokens
echo -e "\nDependencies of OrganizationAccessTokens:"
ast-grep --pattern 'injector.get($_)' packages/services/api/src/modules/organization/providers/organization-access-tokens.ts

# Search for dependencies in OrganizationAccessTokensCache
echo -e "\nDependencies of OrganizationAccessTokensCache:"
ast-grep --pattern 'injector.get($_)' packages/services/api/src/modules/organization/providers/organization-access-tokens-cache.ts

# Search for dependencies in ResourceAssignments
echo -e "\nDependencies of ResourceAssignments:"
ast-grep --pattern 'injector.get($_)' packages/services/api/src/modules/organization/providers/resource-assignments.ts

Length of output: 756


Provider Dependency Order Verified
Based on the dependency analysis, none of the providers—specifically OrganizationAccessTokens, OrganizationAccessTokensCache, and ResourceAssignments—contain any injector.get(…) calls that indicate inter-provider dependencies. This suggests that there is no imposed ordering required for dependency injection in this module, and the current order is acceptable.

packages/services/api/src/modules/organization/resolvers/Mutation/createOrganizationAccessToken.ts (2)

15-23: LGTM! Success case is well-handled.

The success case properly structures the response with the created token and private access key.


25-31: LGTM! Error case is well-structured.

The error case properly includes both a message and details for better error handling by clients.

packages/services/api/src/modules/organization/resolvers/Member.ts (1)

3-3: LGTM! Good refactoring towards granular resource-based access control.

The changes align well with the PR's objective of implementing more granular resource-based access control. The modification to use ResourceAssignments and pass specific properties instead of the entire member object is a good practice.

Also applies to: 38-43

packages/services/api/src/modules/organization/lib/organization-access-token-permissions.ts (1)

65-67: LGTM! Good use of Set for unique permissions.

Using Set with flatMap is an elegant way to ensure unique permission IDs across all groups.

packages/services/api/src/modules/organization/lib/organization-access-key.ts (1)

1-79: LGTM! Secure implementation with proper error handling.

The implementation uses appropriate cryptographic functions, proper error handling, and secure token generation. The use of bcrypt for hashing is a good security practice.

packages/services/api/src/modules/organization/providers/organization-access-tokens-cache.ts (1)

46-68: LGTM! Well-implemented caching with proper TTL and grace period.

The implementation uses a two-layer caching strategy with proper TTL and grace period. The methods are concise and focused.

packages/services/api/src/modules/organization/resolvers/Organization.ts (2)

2-3: LGTM! Clean import refactoring.

The imports have been cleanly split into separate modules for organization access token permissions and member permissions, improving code organization.


12-12: LGTM! Well-structured permission group resolver.

The new resolver availableOrganizationPermissionGroups follows the same pattern as availableMemberPermissionGroups, maintaining consistency in the codebase.

Also applies to: 187-192

packages/services/api/src/modules/auth/lib/supertokens-strategy.ts (1)

93-93: LGTM! Simplified policy statement loading.

The change reduces complexity by directly using the policy statements from the assigned role, making the code more maintainable.

packages/services/api/src/modules/shared/providers/id-translator.ts (1)

160-210: LGTM! Well-implemented organization reference resolver.

The new resolveOrganizationReference method:

  • Follows consistent error handling patterns
  • Includes comprehensive debug logging
  • Handles both selector and ID-based references
packages/services/api/src/modules/organization/lib/organization-member-permissions.ts (1)

269-270: LGTM! New permissions for organization access tokens.

The addition of 'accessToken:modify' and 'usage:report' permissions aligns with the PR objectives of introducing organization access tokens.

integration-tests/tests/api/organization-access-tokens.spec.ts (2)

6-49: LGTM! Well-structured GraphQL operations.

The GraphQL operations are well-defined with appropriate fields for both mutation and query operations.


225-273: LGTM! Thorough negative test case.

The test effectively validates that granular resource access control works as expected.

packages/migrations/src/run-pg-migrations.ts (1)

161-161: LGTM! Migration properly added to the sequence.

The new migration is correctly added at the end of the migrations list.

packages/services/api/src/modules/auth/lib/authz.ts (2)

352-352: LGTM! New permission aligns with feature scope.

The accessToken:modify permission is appropriately added to the organization level permissions.


386-392: Verify the impact of permission changes.

The changes to permissions might affect existing roles and access patterns.

❌ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Description: Check for existing usage of removed/modified permissions

# Check for usage of schema:loadFromRegistry permission
rg -l "schema:loadFromRegistry"

# Check for existing roles that might be affected
ast-grep --pattern 'permissions: [$_]'

Length of output: 72


Permission Impact Review – Manual Verification Recommended

The automated checks did not reveal any usage of the schema:loadFromRegistry permission or references within role definitions. However, since no output was returned from the search command, it’s advisable to perform a manual review to ensure that no dynamic or indirect usages have been overlooked.

packages/services/api/package.json (1)

47-47: Verify the bentocache version and security advisories.

The new dependency bentocache is pinned to version 1.1.0. Let's verify this version is secure and up-to-date.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Description: Check for security advisories and latest versions of the `bentocache` library.

# Check NPM for latest versions
curl -s https://registry.npmjs.org/bentocache | jq '.["dist-tags"].latest'

# Check for security advisories
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NPM, package: "bentocache") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

Length of output: 428


Bentocache version 1.1.0 is verified as secure.

  • The NPM registry confirms that 1.1.0 is the latest version.
  • No security advisories were found in the GitHub security query.
packages/services/server/src/index.ts (2)

58-61: LGTM! New imports for organization access token support.

The imports are correctly organized and follow the module structure.


421-425: LGTM! New organization access token strategy implementation.

The strategy is correctly configured with logger and cache dependencies.

packages/services/api/src/modules/organization/providers/organization-manager.ts (3)

23-23: LGTM! New import for resource assignments.

The import is correctly placed with other organization-related imports.


48-48: LGTM! New resource assignments dependency.

The dependency is correctly injected and follows the established pattern.


1007-1010: LGTM! Updated resource assignment transformation.

The implementation correctly uses the new resourceAssignments service.

packages/services/api/src/modules/schema/providers/schema-manager.ts (1)

999-1010: LGTM! Permission change from schema:loadFromRegistry to project:describe.

The permission change aligns with the previous feedback and is safe as the permission is not exposed externally.

Comment on lines +4 to +17
export const deleteOrganizationAccessToken: NonNullable<
MutationResolvers['deleteOrganizationAccessToken']
> = async (_parent, args, { injector }) => {
const result = await injector.get(OrganizationAccessTokens).delete({
organizationAccessTokenId: args.input.organizationAccessTokenId,
});

return {
ok: {
__typename: 'DeleteOrganizationAccessTokenResultOk',
deletedOrganizationAccessTokenId: result.organizationAccessTokenId,
},
};
};
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

Add error handling for token deletion failures.

While the resolver is well-structured, it should handle potential errors that might occur during token deletion (e.g., token not found, permission issues).

Consider adding try-catch block and error result:

 export const deleteOrganizationAccessToken: NonNullable<
   MutationResolvers['deleteOrganizationAccessToken']
 > = async (_parent, args, { injector }) => {
+  try {
     const result = await injector.get(OrganizationAccessTokens).delete({
       organizationAccessTokenId: args.input.organizationAccessTokenId,
     });

     return {
       ok: {
         __typename: 'DeleteOrganizationAccessTokenResultOk',
         deletedOrganizationAccessTokenId: result.organizationAccessTokenId,
       },
     };
+  } catch (error) {
+    return {
+      error: {
+        __typename: 'DeleteOrganizationAccessTokenResultError',
+        message: error instanceof Error ? error.message : 'Failed to delete token',
+      },
+    };
+  }
 };
📝 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
export const deleteOrganizationAccessToken: NonNullable<
MutationResolvers['deleteOrganizationAccessToken']
> = async (_parent, args, { injector }) => {
const result = await injector.get(OrganizationAccessTokens).delete({
organizationAccessTokenId: args.input.organizationAccessTokenId,
});
return {
ok: {
__typename: 'DeleteOrganizationAccessTokenResultOk',
deletedOrganizationAccessTokenId: result.organizationAccessTokenId,
},
};
};
export const deleteOrganizationAccessToken: NonNullable<
MutationResolvers['deleteOrganizationAccessToken']
> = async (_parent, args, { injector }) => {
try {
const result = await injector.get(OrganizationAccessTokens).delete({
organizationAccessTokenId: args.input.organizationAccessTokenId,
});
return {
ok: {
__typename: 'DeleteOrganizationAccessTokenResultOk',
deletedOrganizationAccessTokenId: result.organizationAccessTokenId,
},
};
} catch (error) {
return {
error: {
__typename: 'DeleteOrganizationAccessTokenResultError',
message:
error instanceof Error ? error.message : 'Failed to delete token',
},
};
}
};

PermissionGroup,
PermissionRecord,
} from '../organization/lib/organization-member-permissions';
import { PermissionGroup, PermissionRecord } from '../organization/lib/permissions';
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify the impact of permissions module restructuring.

The import path change reflects a reorganization of the permissions module. Ensure that all references to these types have been updated consistently across the codebase.

Run the following script to verify the usage of the old import path:


🏁 Script executed:

#!/bin/bash
# Description: Check for any remaining references to the old permissions path.

rg -l "organization-member-permissions" --type ts

Length of output: 208


I'll now run a script to print the surrounding lines of the "organization-member-permissions" references in the two files. This will help determine whether these references need updating in line with the module reorganization.


🏁 Script executed:

#!/bin/bash
echo "=== organization-member-roles.ts ==="
rg -C 3 "organization-member-permissions" packages/services/api/src/modules/organization/providers/organization-member-roles.ts
echo ""
echo "=== Organization.ts ==="
rg -C 3 "organization-member-permissions" packages/services/api/src/modules/organization/resolvers/Organization.ts

Length of output: 1226


Based on the search results:

  • The new file in packages/services/api/src/modules/auth/module.graphql.mappers.ts now imports from:
    ../organization/lib/permissions
  • However, two other files still import from the legacy path (via the term “organization-member-permissions”):
    packages/services/api/src/modules/organization/providers/organization-member-roles.ts
    packages/services/api/src/modules/organization/resolvers/Organization.ts

To decide if this is an issue, we must confirm whether these two imports should have migrated to the new path as well. The review comment requests verifying that all references were updated, meaning that if a module’s internal reorganization calls for a singular updated location, then legacy paths should not be present. The fact that “organization-member-permissions” is still referenced in parts of the organization module shows inconsistency compared to the new permissions import used in the auth module.

Actionable Points:

  • Verify if ../organization/lib/permissions is now the canonical location for all permissions-related types.
    If so, then the usage of ../lib/organization-member-permissions in the organization providers and resolvers should be updated accordingly.
  • Update the import paths in organization-member-roles.ts and Organization.ts to use the new location if the reorganization mandates a single source for permissions types.

Thus, the evidence indicates that some references to permissions-related code have not been updated consistently.

Recommendation:
Review and, if needed, refactor the imports from ../lib/organization-member-permissions in the listed organization module files to align with the new module reorganization.

Comment on lines +14 to +19
resources: async (accessToken, _arg, { injector }) => {
return injector.get(ResourceAssignments).resolveGraphQLMemberResourceAssignment({
organizationId: accessToken.organizationId,
resources: accessToken.assignedResources,
});
},
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

Add error handling for resource resolution.

The resolver should handle potential errors from resolveGraphQLMemberResourceAssignment to prevent unhandled promise rejections.

Apply this diff to add error handling:

   resources: async (accessToken, _arg, { injector }) => {
-    return injector.get(ResourceAssignments).resolveGraphQLMemberResourceAssignment({
-      organizationId: accessToken.organizationId,
-      resources: accessToken.assignedResources,
-    });
+    try {
+      return await injector.get(ResourceAssignments).resolveGraphQLMemberResourceAssignment({
+        organizationId: accessToken.organizationId,
+        resources: accessToken.assignedResources,
+      });
+    } catch (error) {
+      console.error('Failed to resolve resources:', error);
+      return null; // or handle error appropriately based on your GraphQL schema
+    }
   },
📝 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
resources: async (accessToken, _arg, { injector }) => {
return injector.get(ResourceAssignments).resolveGraphQLMemberResourceAssignment({
organizationId: accessToken.organizationId,
resources: accessToken.assignedResources,
});
},
resources: async (accessToken, _arg, { injector }) => {
try {
return await injector.get(ResourceAssignments).resolveGraphQLMemberResourceAssignment({
organizationId: accessToken.organizationId,
resources: accessToken.assignedResources,
});
} catch (error) {
console.error('Failed to resolve resources:', error);
return null; // or handle error appropriately based on your GraphQL schema
}
},

packages/services/storage/src/db/types.ts Show resolved Hide resolved
@@ -158,5 +158,6 @@ export const runPGMigrations = async (args: { slonik: DatabasePool; runTo?: stri
await import('./actions/2025.01.17T10-08-00.drop-activities'),
await import('./actions/2025.01.20T00-00-00.legacy-registry-model-removal'),
await import('./actions/2025.01.30T00-00-00.granular-member-role-permissions'),
await import('./actions/2025.01.30T00-02-03.organization-access-tokens'),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO: update dates

z.literal('schema:compose'),
z.literal('usage:report'),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO: remove for now; will introduce it when we implement organization access token support in usage reporting service

);

const permissions = Array.from(
new Set(args.permissions.filter(permission => assignablePermissions.has(permission as any))),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO: any cast

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO: implement Organization.accessTokens field

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

Successfully merging this pull request may close these issues.

1 participant