module Gems::API::APIKeyEndpoints
The API key endpoints, including trusted publishing @api public
Public Instance Methods
Return one of the API key roles of the user the API key belongs to
@api public @authenticated true @param token [String, APIKeyRole] The token of the role, or a role. @return [APIKeyRole] the role @example
Gems.api_key_role("0123456789abcdef0123456789abcdef").api_key_permissions
# File lib/gems/api/api_key_endpoints.rb, line 110 def api_key_role(token) APIKeyRole.new(parse_json(get("/api/v1/oidc/api_key_roles/#{escape(token_of(token))}.json"))) end
List the API key roles of the user the API key belongs to
@api public @authenticated true @return [Array<APIKeyRole>] the roles @example
Gems.api_key_roles.map(&:name)
# File lib/gems/api/api_key_endpoints.rb, line 98 def api_key_roles APIKeyRole.list(parse_json(get("/api/v1/oidc/api_key_roles.json"))) end
Exchange an OIDC ID token for an API key by assuming an API key role
The role issues a key granted the permissions it was set up with when the ID token satisfies its access policy, whichever gem the key will act on, where {#exchange_trusted_publisher_token} issues one for the gems that trust the workflow the token names. The exchange is sent as that one is: without the credentials of the client, and again when the server turns it away or the network loses it.
@api public @authenticated false @param token [String, APIKeyRole] The token of the role, or a role. @param id_token [String] The OIDC ID token. @return [APIKey] the API key the role issued, including its name, scopes, and expiry @example
Gems.assume_api_key_role("0123456789abcdef0123456789abcdef", ENV.fetch("ID_TOKEN")).key
# File lib/gems/api/api_key_endpoints.rb, line 177 def assume_api_key_role(token, id_token) TrustedPublisherAuthenticator.new(id_token:, host:, connection:, request_builder:, redirect_handler:, retry_handler:, exchange_path: "/api/v1/oidc/api_key_roles/#{escape(token_of(token))}/assume_role.json") .exchange_token! end
Create an API key using HTTP basic auth
The key is only returned once, so store it somewhere safe.
The endpoint answers with the key alone, so the name, scopes, expiry, and gem the key was asked for are kept in the result, as the name of a gem is kept in the versions of it that {API::VersionEndpoints#versions} returns, and the key answers {APIKey#scopes} and {APIKey#expires_at} as the keys of a token exchange do. What the endpoint answers with is left as it is.
@api public @authenticated true @param name [String] A name for the key. @param scopes [Array<Symbol, String>] The scopes to grant the key, which are among the scopes
{APIKey::SCOPES} names, such as push_rubygem and yank_rubygem; the key is granted these alone.
@param expires_at [Time, String, nil] When the key expires, as a Time or an ISO 8601 string. @param rubygem_name [String, Gem, nil] A gem to restrict the key to. @param mfa [Boolean, nil] Whether to require a one-time passcode when the key is used. @return [APIKey] the new API key @raise [ArgumentError] if a scope is not one the API defines @example
Gems.configure do |config| config.username = "nick@gemcutter.org" config.password = "schwwwwing" end Gems.create_api_key("ci-push", scopes: %i[push_rubygem]).key
@example
Gems.create_api_key("ci-push", scopes: %i[push_rubygem], rubygem_name: "gems", expires_at: Time.now + 86_400, mfa: true)
# File lib/gems/api/api_key_endpoints.rb, line 47 def create_api_key(name, scopes:, expires_at: nil, rubygem_name: nil, mfa: nil) fields = scope_fields(scopes) expires_at = timestamp_of(expires_at) rubygem_name = name_of(rubygem_name) settings = {expires_at:, rubygem_name:, mfa:}.compact requested = {"name" => name, "scopes" => granted_scopes(fields), "expires_at" => expires_at, "gem" => rubygem_name && {"name" => rubygem_name}}.compact APIKey.new(requested.merge(parse_json(post("/api/v1/api_key.json", {**fields, **settings, name:})))) end
Exchange an OIDC ID token for an API key via trusted publishing
@api public @authenticated false @param id_token [String] The OIDC ID token. @return [APIKey] the exchanged API key, including its name, scopes, and expiry @example
Gems.exchange_trusted_publisher_token(ENV.fetch("ID_TOKEN")).key
# File lib/gems/api/api_key_endpoints.rb, line 86 def exchange_trusted_publisher_token(id_token) TrustedPublisherAuthenticator.new(id_token:, host:, connection:, request_builder:, redirect_handler:, retry_handler:).exchange_token! end
Return one of the OIDC ID tokens the API key roles of the user have accepted
The endpoint looks the ID token up by its ID, which neither it nor {#oidc_id_tokens} answers with.
@api public @authenticated true @param id [Integer, String] The ID of the ID token. @return [OIDCIDToken] the ID token, with its claims and header @example
Gems.oidc_id_token(42).api_key_role_token
# File lib/gems/api/api_key_endpoints.rb, line 159 def oidc_id_token(id) OIDCIDToken.new(parse_json(get("/api/v1/oidc/id_tokens/#{escape(id)}.json"))) end
List the OIDC ID tokens the API key roles of the user have accepted
@api public @authenticated true @return [Array<OIDCIDToken>] the ID tokens, with their claims and headers @example
Gems.oidc_id_tokens.map { |id_token| id_token.jwt["claims"]["repository"] }
# File lib/gems/api/api_key_endpoints.rb, line 145 def oidc_id_tokens OIDCIDToken.list(parse_json(get("/api/v1/oidc/id_tokens.json"))) end
Return one of the OIDC providers whose ID tokens RubyGems.org accepts
@api public @authenticated true @param provider [Integer, String, OIDCProvider] The ID of the provider, such as the {APIKeyRole#provider_id}
of a role, or a provider.
@return [OIDCProvider] the provider @example
Gems.oidc_provider(Gems.api_key_role("0123456789abcdef0123456789abcdef").provider_id).issuer
# File lib/gems/api/api_key_endpoints.rb, line 134 def oidc_provider(provider) OIDCProvider.new(parse_json(get("/api/v1/oidc/providers/#{escape(id_of(provider))}.json"))) end
List the OIDC providers whose ID tokens RubyGems.org accepts
@api public @authenticated true @return [Array<OIDCProvider>] the providers @example
Gems.oidc_providers.map(&:issuer)
# File lib/gems/api/api_key_endpoints.rb, line 121 def oidc_providers OIDCProvider.list(parse_json(get("/api/v1/oidc/providers.json"))) end
Update the scopes of an API key using HTTP basic auth
The key is granted the scopes it is given alone, and every other scope it had is taken away.
The endpoint answers with a message rather than with the key it updated, as the endpoints of {#push}, {API::OwnerEndpoints#add_owner}, and the rest of the endpoints that act on something do, so that message is what is returned; {#create_api_key} answers with an {APIKey} because its endpoint answers with the key.
@api public @authenticated true @param key [String, APIKey] The API key to update. @param scopes [Array<Symbol, String>] The scopes to grant the key, which are among the scopes
{APIKey::SCOPES} names, such as push_rubygem and yank_rubygem.
@return [String] the message the endpoint answers with @raise [ArgumentError] if a scope is not one the API defines @example
Gems.update_api_key "rubygems_701243f217cdf23b1370c7b66b65ca97", scopes: %i[push_rubygem yank_rubygem]
# File lib/gems/api/api_key_endpoints.rb, line 74 def update_api_key(key, scopes:) patch("/api/v1/api_key", {**scope_fields(scopes), api_key: key_of(key)}) end