rubygems/rubygems.org

View on GitHub
app/controllers/api/v1/oidc/trusted_publisher_controller.rb

Summary

Maintainability
A
0 mins
Test Coverage
class Api::V1::OIDC::TrustedPublisherController < Api::BaseController
  include ApiKeyable

  before_action :decode_jwt
  before_action :find_provider
  before_action :verify_signature
  before_action :find_trusted_publisher
  before_action :validate_claims

  class UnsupportedIssuer < StandardError; end
  class UnverifiedJWT < StandardError; end

  rescue_from(
    UnsupportedIssuer, UnverifiedJWT,
    JSON::JWT::VerificationFailed, JSON::JWK::Set::KidNotFound,
    OIDC::AccessPolicy::AccessError,
    with: :render_not_found
  )

  def exchange_token
    key = generate_unique_rubygems_key
    iat = Time.at(@jwt[:iat].to_i, in: "UTC")
    api_key = @trusted_publisher.api_keys.create!(
      hashed_key: hashed_key(key),
      name: "#{@trusted_publisher.name} #{iat.iso8601}",
      scopes: %i[push_rubygem],
      expires_at: 15.minutes.from_now
    )

    render json: {
      rubygems_api_key: key,
      name: api_key.name,
      scopes: api_key.scopes,
      gem: api_key.rubygem,
      expires_at: api_key.expires_at
    }.compact, status: :created
  end

  private

  def decode_jwt
    @jwt = JSON::JWT.decode_compact_serialized(params.permit(:jwt).require(:jwt), :skip_verification)
  rescue JSON::JWT::InvalidFormat, JSON::ParserError, ArgumentError
    # invalid base64 raises ArgumentError
    render_bad_request
  end

  def find_provider
    @provider = OIDC::Provider.find_by!(issuer: @jwt[:iss])
  end

  def verify_signature
    raise UnsupportedIssuer, "Provider is missing jwks" if @provider.jwks.blank?
    raise UnverifiedJWT, "Invalid time" unless (@jwt["nbf"]..@jwt["exp"]).cover?(Time.now.to_i)
    @jwt.verify!(@provider.jwks)
  end

  def find_trusted_publisher
    unless (trusted_publisher_class = @provider.trusted_publisher_class)
      raise UnsupportedIssuer, "Unsuported issuer for trusted publishing"
    end
    @trusted_publisher = trusted_publisher_class.for_claims(@jwt)
  end

  def validate_claims
    @trusted_publisher.to_access_policy(@jwt).verify_access!(@jwt)
  end

  def render_bad_request
    render json: { error: "Bad Request" }, status: :bad_request
  end
end