Repository: mrsked/mrsk
File: lib/kamal/secrets/adapters/gcp_secret_manager.rb (Ruby)

Code before:
  private
    def login(account)
      # Since only the account option is passed from the cli, we'll use it for both account and service account
      # impersonation.
      #
      # Syntax:
      # ACCOUNT: USER | USER "," DELEGATION_CHAIN
      # USER: DEFAULT_USER | EMAIL
      # DELEGATION_CHAIN: EMAIL | EMAIL "," DELEGATION_CHAIN
      # EMAIL: <The email address of the user or service account, like "my-user@example.com" >
      # DEFAULT_USER: "default"
      #
      # Some valid examples:
      # - "my-user@example.com" sets the user
      # - "my-user@example.com,my-service-user@example.com" will use my-user and enable service account impersonation as my-service-user
      # - "default" will use the default user and no impersonation
      # - "default,my-service-user@example.com" will use the default user, and enable service account impersonation as my-service-user
      # - "default,my-service-user@example.com,another-service-user@example.com" same as above, but with an impersonation delegation chain

      if !logged_in?
        raise RuntimeError, "gcloud is not authenticated, please run `gcloud auth login`"
      end

      user, impersonate_service_account = parse_account(account)

      {
        user: user,
        impersonate_service_account: impersonate_service_account
      }
    end

    def fetch_secrets(secrets, account:, session:)
      # puts("secrets spec: #{secrets.inspect}")
      {}.tap do |results|
        secrets_with_metadata(secrets).each do |secret, metadata|
          project, secret_name, secret_version = metadata
          item_name = project == "default" ? secret_name : "#{project}/#{secret_name}"
          results[item_name] = fetch_secret(session, project, secret_name, secret_version)
          raise RuntimeError, "Could not read #{item_name} from Google Secret Manager" unless $?.success?
        end
      end
    end

    def fetch_secret(session, project, secret_name, secret_version)
      secret = run_command("secrets versions access #{secret_version} --secret=#{secret_name.shellescape}", session: session, project: project)
      Base64.decode64(secret.dig("payload", "data"))
    end

    # The secret needs to at least contain a secret name, but project name, and secret version can also be specified.
    #
    # The string "default" can be used to refer to the default project configured for gcloud.

Review comment (security):
I think we could parse this out of the account in the `fetch_secrets` method rather than here. We can return nil if there's no session involved.

Code after:
  private
    def login(account)
      # Since only the account option is passed from the cli, we'll use it for both account and service account
      # impersonation.
      #
      # Syntax:
      # ACCOUNT: USER | USER "|" DELEGATION_CHAIN
      # USER: DEFAULT_USER | EMAIL
      # DELEGATION_CHAIN: EMAIL | EMAIL "," DELEGATION_CHAIN
      # EMAIL: <The email address of the user or service account, like "my-user@example.com" >
      # DEFAULT_USER: "default"
      #
      # Some valid examples:
      # - "my-user@example.com" sets the user
      # - "my-user@example.com|my-service-user@example.com" will use my-user and enable service account impersonation as my-service-user
      # - "default" will use the default user and no impersonation
      # - "default|my-service-user@example.com" will use the default user, and enable service account impersonation as my-service-user
      # - "default|my-service-user@example.com,another-service-user@example.com" same as above, but with an impersonation delegation chain

      if !logged_in?
        `gcloud auth login`
        raise RuntimeError, "gcloud is not authenticated, please run `gcloud auth login`" if !logged_in?
      end

      nil
    end

    def fetch_secrets(secrets, account:, session:)
      user, service_account = parse_account(account)

      {}.tap do |results|
        secrets_with_metadata(secrets).each do |secret, (project, secret_name, secret_version)|
          item_name = "#{project}/#{secret_name}"
          results[item_name] = fetch_secret(project, secret_name, secret_version, user, service_account)
          raise RuntimeError, "Could not read #{item_name} from Google Secret Manager" unless $?.success?
        end
      end
    end

    def fetch_secret(project, secret_name, secret_version, user, service_account)
      secret = run_command(
        "secrets versions access #{secret_version.shellescape} --secret=#{secret_name.shellescape}",
        project: project,
        user: user,
        service_account: service_account
      )
      Base64.decode64(secret.dig("payload", "data"))
    end

    # The secret needs to at least contain a secret name, but project name, and secret version can also be specified.
    #