class Conjur::API
API
contains each of the methods for access the Conjur
API
endpoints
This class provides access to the Conjur
services.
Constants
- POLICY_METHOD_PATCH
Allow explicit deletion statements, but don’t delete implicitly delete data.
- POLICY_METHOD_POST
Append only.
- POLICY_METHOD_PUT
Replace the policy entirely, deleting any existing data that is not declared in the new policy.
- VERSION
Attributes
@!attribute [r] api_key
The api key used to create this instance. This is only present when you created the api with {Conjur::API.new_from_key}.# @return [String] the api key, or nil if this instance was created from a token.
@!attribute [r] remote_ip
An optional IP address to be recorded in the audit record for any actions performed by this API instance.
Public Class Methods
Exchanges Conjur
the API
key (refresh token) for an access token. The access token can then be used to authenticate further API
calls.
@param [String] username The username or host id for which we want a token @param [String] api_key
The api key @param [String] account The organization account. @return [String] A JSON formatted authentication token.
# File lib/conjur/api/authn.rb, line 79 def authenticate username, api_key, account: Conjur.configuration.account account ||= Conjur.configuration.account if Conjur.log Conjur.log << "Authenticating #{username} to account #{account}\n" end JSON.parse url_for(:authn_authenticate, account, username).post(api_key, content_type: 'text/plain') end
Obtains an access token from the authn_local
service. The access token can then be used to authenticate further API
calls.
@param [String] username The username or host id for which we want a token @param [String] account The organization account. @return [String] A JSON formatted authentication token.
# File lib/conjur/api/authn.rb, line 93 def authenticate_local username, account: Conjur.configuration.account, expiration: nil, cidr: nil account ||= Conjur.configuration.account if Conjur.log Conjur.log << "Authenticating #{username} to account #{account} using authn_local\n" end require 'json' require 'socket' message = url_for(:authn_authenticate_local, username, account, expiration, cidr) JSON.parse(UNIXSocket.open(Conjur.configuration.authn_local_socket) {|s| s.puts message; s.gets }) end
Authenticates using a third party authenticator like authn-oidc. It will return an access token to be used to authenticate further API
calls.
@param [String] authenticator @param [String] service_id @param [String] account The organization account. @param [Hash] params Additional params to send to authenticator @return [String] A JSON formatted authentication token.
# File lib/conjur/api/authn.rb, line 65 def authenticator_authenticate authenticator, service_id, account: Conjur.configuration.account, options: {} if Conjur.log Conjur.log << "Authenticating to account #{account} using #{authenticator}/#{service_id}\n" end JSON.parse url_for(:authenticator_authenticate, account, service_id, authenticator, options).get end
Use a host factory token to create a new host. Unlike most other methods, this method does not require a Conjur
access token. The host factory token is the authentication and authorization to create the host.
The token must be valid. The host id can be a new host, or an existing host. If the host already exists, the server verifies that its layer memberships match the host factory exactly. Then, its API
key is rotated and returned with the response.
@param [String] token the host factory token. @param [String] id the id of a new or existing host. @param options [Hash] additional host creation options. @return [Host]
# File lib/conjur/api/host_factories.rb, line 37 def host_factory_create_host token, id, options = {} token = token.token if token.is_a?(HostFactoryToken) response = url_for(:host_factory_create_host, token) .post(options.merge(id: id)).body attributes = JSON.parse(response) # in v4 'id' is just the identifier host_id = attributes['roleid'] || attributes['id'] Host.new(host_id, {}).tap do |host| host.attributes = attributes end end
Exchanges a username and a password for an api key. The api key
is preferable for storage and use in code, as it can be rotated and has far greater entropy than a user memorizable password. * Note that this method works only for {Conjur::User}s. While {Conjur::Host}s are roles, they do not have passwords. * If you pass an api key to this method instead of a password, it will verify and return the API key. * This method uses HTTP Basic Authentication to send the credentials.
@example
bob_api_key = Conjur::API.login('bob', 'bob_password') bob_api_key == Conjur::API.login('bob', bob_api_key) # => true
@param [String] username The ‘username` or `login` for the
{http://developer.conjur.net/reference/services/directory/user Conjur User}.
@param [String] password The ‘password` or `api key` to authenticate with. @param [String] account The organization account. @return [String] the API
key.
# File lib/conjur/api/authn.rb, line 50 def login username, password, account: Conjur.configuration.account if Conjur.log Conjur.log << "Logging in #{username} to account #{account} via Basic authentication\n" end url_for(:authn_login, account, username, password).get end
Create a new {Conjur::API} instance which authenticates using authn-local
using the specified username.
@param [String] username the username to use when making authenticated requests. @param [String] account The organization account. @param [String] remote_ip
the optional IP address to be recorded in the audit record. @param [String] expiration the optional expiration time of the token (supported in V5 only). @param [String] cidr the optional CIDR
restriction on the token (supported in V5 only). @return [Conjur::API] an api that will authenticate with the given username.
# File lib/conjur/base.rb, line 116 def new_from_authn_local username, account: Conjur.configuration.account, remote_ip: nil, expiration: nil, cidr: nil self.new.init_from_authn_local username, account: account, remote_ip: remote_ip, expiration: expiration, cidr: cidr end
Create a new {Conjur::API} instance from a username and a password or api key.
@example Create an API
with valid credentials
api = Conjur::API.new_from_key 'admin', '<admin password>' api.current_role # => 'conjur:user:admin' api.token['data'] # => 'admin'
@example Authentication is lazy
api = Conjur::API.new_from_key 'admin', 'wrongpassword' # succeeds api.user 'foo' # raises a 401 error
@param [String] username the username to use when making authenticated requests. @param [String] api_key
the api key or password for ‘username` @param [String] remote_ip
the optional IP address to be recorded in the audit record. @param [String] account The organization account. @return [Conjur::API] an api that will authenticate with the given username and api key.
# File lib/conjur/base.rb, line 60 def new_from_key username, api_key, account: Conjur.configuration.account, remote_ip: nil self.new.init_from_key username, api_key, remote_ip: remote_ip, account: account end
Create a new {Conjur::API} instance from an access token.
Generally, you will have a Conjur
identitiy (username and API
key), and create an {Conjur::API} instance for the identity using {.new_from_key}. This method is useful when you are performing authorization checks given a token. For example, a Conjur
gateway that requires you to prove that you can ‘read’ a resource named ‘super-secret’ might get the token from a request header, create an {Conjur::API} instance with this method, and use {Conjur::Resource#permitted?} to decide whether to accept and forward the request.
@example A simple gatekeeper
RESOURCE_NAME = 'protected-service' def handle_request request token_header = request.header 'X-Conjur-Token' token = JSON.parse Base64.b64decode(token_header) api = Conjur::API.new_from_token token raise Forbidden unless api.resource(RESOURCE_NAME).permitted? 'read' proxy_to_service request end
@param [Hash] token the authentication token as parsed JSON to use when making authenticated requests @param [String] remote_ip
the optional IP address to be recorded in the audit record. @return [Conjur::API] an api that will authenticate with the token
# File lib/conjur/base.rb, line 88 def new_from_token token, remote_ip: nil self.new.init_from_token token, remote_ip: remote_ip end
Create a new {Conjur::API} instance from a file containing a token issued by the {developer.conjur.net/reference/services/authentication Conjur
authentication service}. The file is read the first time that a token is required. It is also re-read whenever the API
decides that the token it already has is getting close to expiration.
This method is useful when an external process, such as a sidecar container, is continuously obtaining fresh tokens and writing them to a known file.
@param [String] token_file the file path containing an authentication token as parsed JSON. @param [String] remote_ip
the optional IP address to be recorded in the audit record. @return [Conjur::API] an api that will authenticate with the tokens provided in the file.
# File lib/conjur/base.rb, line 103 def new_from_token_file token_file, remote_ip: nil self.new.init_from_token_file token_file, remote_ip: remote_ip end
Fetch all public keys for the user. This method returns a newline delimited String for compatibility with the authorized_keys SSH format.
If the given user does not exist, an empty String will be returned. This is to prevent attackers from determining whether a user exists.
## Permissions You do not need any special permissions to call this method, since public keys are, well, public.
@example
puts api.public_keys('jon') # ssh-rsa [big long string] jon@albert # ssh-rsa [big long string] jon@conjurops
@param [String] username the unqualified Conjur
username @return [String] newline delimited public keys
# File lib/conjur/api/pubkeys.rb, line 46 def public_keys username, account: Conjur.configuration.account url_for(:public_keys_for_user, account, username).get end
Revokes a host factory token. After revocation, the token can no longer be used to create hosts.
@param [Hash] credentials authentication credentials of the current user. @param [String] token the host factory token.
# File lib/conjur/api/host_factories.rb, line 56 def revoke_host_factory_token credentials, token url_for(:host_factory_revoke_token, credentials, token).delete end
@api private
# File lib/conjur/api/roles.rb, line 83 def role_from_username api, username, account api.role role_name_from_username(username, account) end
@api private
# File lib/conjur/api/roles.rb, line 88 def role_name_from_username username, account tokens = username.split('/') if tokens.size == 1 [ account, 'user', username ].join(':') else [ account, tokens[0], tokens[1..-1].join('/') ].join(':') end end
Rotate the currently authenticated user or host API
key by generating and returning a new one. The old API
key is no longer valid after calling this method. You must have the current API
key or password to perform this operation. This method *does not* affect a user’s password.
@param [String] username the name of the user or host whose API
key we want to change @param [String] password the user’s current api key @param [String] account The organization account. @return [String] the new API
key
# File lib/conjur/api/authn.rb, line 133 def rotate_api_key username, password, account: Conjur.configuration.account if Conjur.log Conjur.log << "Rotating API key for self (#{username} in account #{account})\n" end url_for(:authn_rotate_own_api_key, account, username, password).put('').body end
Change a user’s password. To do this, you must have the user’s current password. This does not change or rotate
api keys. However, you *can* use the user's api key as the *current* password, if the user was not created with a password.
@param [String] username the name of the user whose password we want to change. @param [String] password the user’s current password or api key. @param [String] new_password the new password for the user. @param [String] account The organization account. @return [void]
# File lib/conjur/api/authn.rb, line 114 def update_password username, password, new_password, account: Conjur.configuration.account if Conjur.log Conjur.log << "Updating password for #{username} in account #{account}\n" end url_for(:authn_update_password, account, username, password).put new_password end
Public Instance Methods
Fetches the available authentication providers for the authenticator and account. The authenticators must be loaded in Conjur
policy prior to fetching.
@param [String] authenticator the authenticator type to retrieve providers for
# File lib/conjur/api/authenticators.rb, line 20 def authentication_providers authenticator, account: Conjur.configuration.account JSON.parse(url_for(:authentication_providers, account, authenticator, credentials).get) end
Disables an authenticator in Conjur
.
@param [String] authenticator the authenticator type to disable (e.g. authn-k8s) @param [String] id the service ID of the authenticator to disable
# File lib/conjur/api/authenticators.rb, line 37 def authenticator_disable authenticator, id, account: Conjur.configuration.account url_for(:authenticator, account, authenticator, id, credentials).patch(enabled: false) end
Enables an authenticator in Conjur
. The authenticator must be defined and loaded in Conjur
policy prior to enabling it.
@param [String] authenticator the authenticator type to enable (e.g. authn-k8s) @param [String] id the service ID of the authenticator to enable
# File lib/conjur/api/authenticators.rb, line 29 def authenticator_enable authenticator, id, account: Conjur.configuration.account url_for(:authenticator, account, authenticator, id, credentials).patch(enabled: true) end
List all configured authenticators
# File lib/conjur/api/authenticators.rb, line 12 def authenticator_list JSON.parse(url_for(:authenticators).get) end
Credentials that can be merged with options to be passed to ‘RestClient::Resource` HTTP request methods. These include a username and an Authorization header containing the authentication token.
@return [Hash] the options. @raise [RestClient::Unauthorized] if fetching the token fails.
# File lib/conjur/base.rb, line 170 def credentials headers = {}.tap do |h| h[:authorization] = "Token token=\"#{Base64.strict_encode64 token.to_json}\"" h[:x_forwarded_for] = @remote_ip if @remote_ip end { headers: headers, username: username } end
Return a {Conjur::Role} object representing the role (typically a user or host) that this API
instance is authenticated as. This is derived either from the ‘login` argument to {Conjur::API.new_from_key} or from the contents of the `token` given to {Conjur::API.new_from_token} or {Conjur::API.new_from_token_file}.
@example Current role for a user
api = Conjur::API.new_from_key 'jon', 'somepassword' api.current_role.id # => 'conjur:user:jon'
@example Current role for a host
host = api.create_host id: 'exapmle-host' # Host and User have an `api` method that returns an api with their credentials. Note # that this only works with a newly created host or user, which has an `api_key` attribute. host.api.current_role.id # => 'conjur:host:example-host'
@param [String] account the organization account @return [Conjur::Role] the authenticated role for this API
instance
# File lib/conjur/api/roles.rb, line 75 def current_role account self.class.role_from_username self, username, account end
@api private Force the API
to obtain a new access token on the next invocation.
# File lib/conjur/base.rb, line 161 def force_token_refresh @token = nil end
@api private used to delegate to host providing subclasses. @return [String] the host
# File lib/conjur/base.rb, line 144 def host self.class.host end
# File lib/conjur/base.rb, line 316 def init_from_authn_local username, account: Conjur.configuration.account, remote_ip: nil, expiration: nil, cidr: nil @username = username @api_key = api_key @remote_ip = remote_ip @authenticator = LocalAuthenticator.new(account, username, expiration, cidr) self end
# File lib/conjur/base.rb, line 295 def init_from_key username, api_key, account: Conjur.configuration.account, remote_ip: nil @username = username @api_key = api_key @remote_ip = remote_ip @authenticator = APIKeyAuthenticator.new(account, username, api_key) self end
# File lib/conjur/base.rb, line 303 def init_from_token token, remote_ip: nil @token = token @remote_ip = remote_ip @authenticator = UnableAuthenticator.new self end
# File lib/conjur/base.rb, line 310 def init_from_token_file token_file, remote_ip: nil @remote_ip = remote_ip @authenticator = TokenFileAuthenticator.new(token_file) self end
Retrieve the policy for the given LDAP sync configuration. Configurations created through the Conjur
UI are named default
, so the default value of config_name
can be used.
For details on the use of LDAP sync, see developer.conjur.net/reference/services/ldap_sync/ .
@param [String] config_name the name of the LDAP sync configuration.
# File lib/conjur/api/ldap_sync.rb, line 34 def ldap_sync_policy config_name: 'default' JSON.parse(url_for(:ldap_sync_policy, credentials, config_name).get) end
Load a policy document into the server.
The modes are support for policy loading:
-
POLICY_METHOD_POST
Policy
data will be added to the named policy. Deletions are not allowed. -
POLICY_METHOD_PATCH
Policy
data can be added to or deleted from the named policy. Deletions
are performed by an explicit ‘!delete` statement.
-
POLICY_METHOD_PUT
The policy completely replaces the name policy.Policy
data which is present
in the server, but not present in the new policy definition, is deleted.
@param id [String] id of the policy to load. @param policy [String] YAML-formatted policy definition. @param account [String] Conjur
organization account @param method [Symbol] Policy
load method to use: {POLICY_METHOD_POST} (default), {POLICY_METHOD_PATCH}, or {POLICY_METHOD_PUT}.
# File lib/conjur/api/policies.rb, line 49 def load_policy id, policy, account: Conjur.configuration.account, method: POLICY_METHOD_POST request = url_for(:policies_load_policy, credentials, account, id) PolicyLoadResult.new JSON.parse(request.send(method, policy)) end
Find a resource by its id. @note The id given to this method must be fully qualified.
### Permissions
The resource must be visible to the current role. This is the case if the current role is the owner of the resource, or has any privilege on it.
@param id [String] a fully qualified resource identifier @return [Conjur::Resource] the resource, which may or may not exist
# File lib/conjur/api/resources.rb, line 36 def resource id build_object id end
Find all resources visible to the current role that match the given search criteria.
## Full Text Search Conjur
supports full text search over the identifiers and annotation values of resources. For example, if ‘opts` is `“pubkeys”`, any resource with an id containing `“pubkeys”` or an annotation whose value contains `“pubkeys”` will match.
Notes
* Annotation *keys* are *not* indexed for full text search. * Conjur indexes the content of ids and annotation values by word. * Only resources visible to the current role (either owned by that role or having a privilege on it) are returned. * If you do not provide `:offset` or `:limit`, all records will be returned. For systems with a huge number of resources, you may want to paginate as shown in the example below. * If `:offset` is provided and `:limit` is not, 10 records starting at `:offset` will be returned. You may choose an arbitrarily large number for `:limit`, but the same performance considerations apply as when omitting `:offset` and `:limit`.
@example Search for resources annotated with the text “WebService Route”
webservice_routes = api.resources search: "WebService Route"
@example Restrict the search to ‘group’ resources
groups = api.resources kind: 'group' # Correct behavior: expect(groups.all?{|g| g.kind == 'group'}).to be_true
@example Get every single resource in a performant way
resources = [] limit = 25 offset = 0 until (batch = api.resources limit: limit, offset: offset).empty? offset += batch.length resources.concat results end # do something with your resources
@param options [Hash] search criteria @option options [String] :search find resources whose ids or annotations contain this string @option options [String] :kind find resources whose ‘kind` matches this string @option options [Integer] :limit the maximum number of records to return (Conjur
may return fewer) @option options [Integer] :offset offset of the first record to return @option options [Boolean] :count return a count of records instead of the records themselves when set to true @return [Array<Conjur::Resource>] the resources matching the criteria given
# File lib/conjur/api/resources.rb, line 84 def resources options = {} options = { host: Conjur.configuration.core_url, credentials: credentials }.merge options options[:account] ||= Conjur.configuration.account host, credentials, account, kind = options.values_at(*[:host, :credentials, :account, :kind]) fail ArgumentError, "host and account are required" unless [host, account].all? %w(host credentials account kind).each do |name| options.delete(name.to_sym) end result = JSON.parse(url_for(:resources, credentials, account, kind, options).get) result = result['count'] if result.is_a?(Hash) if result.is_a?(Numeric) result else result.map do |result| resource(result['id']).tap do |r| r.attributes = result end end end end
Revokes a host factory token. After revocation, the token can no longer be used to create hosts.
@param [String] token the host factory token.
# File lib/conjur/api/host_factories.rb, line 65 def revoke_host_factory_token token self.class.revoke_host_factory_token credentials, token end
Return a {Conjur::Role} representing a role with the given id. Note that the {Conjur::Role} may or may not exist (see {Conjur::Exists#exists?}).
### Permissions
Because this method returns roles that may or may not exist, it doesn’t require any permissions to call it: in fact, it does not perform an HTTP request (except for authentication if necessary).
@example Create and show a role
iggy = api.role 'cat:iggy' iggy.exists? # true iggy.members.map(&:member).map(&:id) # => ['conjur:user:admin'] api.current_role.id # => 'conjur:user:admin' # creator role is a member of created role.
@example No permissions are required to call this method
api.current_role # => "user:no-access" # current role is only a member of itself, so it can't see other roles. api.current_role.memberships.count # => 1 admin = api.role 'user:admin' # OK admin.exists? # => true admin.members # => RestClient::Forbidden: 403 Forbidden
@param id [String] a fully qualified role identifier @return [Conjur::Role] an object representing the role
# File lib/conjur/api/roles.rb, line 54 def role id build_object id, default_class: Role end
The token used to authenticate requests made with the api. The token will be fetched, if possible, when not present or about to expire. Accordingly, this method may raise a RestClient::Unauthorized exception if the credentials are invalid.
@return [Hash] the authentication token as a Hash @raise [RestClient::Unauthorized] if the username and api key are invalid.
# File lib/conjur/base.rb, line 154 def token refresh_token if needs_token_refresh? return @token end
The name of the user as which this api instance is authenticated. This is available whether the api instance was created from credentials or an authentication token. If the instance was created from credentials, we will use that value directly otherwise we will attempt to extract the username from the token (either the old-style data field or the new-style JWT ‘sub` field).
@return [String] the login of the current user.
# File lib/conjur/base.rb, line 137 def username @username || token['data'] || jwt_username(token) end
Fetch the values of a list of variables. This operation is more efficient than fetching the values one by one.
This method will fail unless:
* All of the variables exist * You have permission to `'execute'` all of the variables
@example Fetch multiple variable values
values = variable_values ['myorg:variable:postgres_uri', 'myorg:variable:aws_secret_access_key', 'myorg:variable:aws_access_key_id'] values # => { "postgres://...", "the-secret-key", "the-access-key-id" }
This method is used to implement the {developer.conjur.net/reference/tools/utilities/conjurenv ‘conjur env`} commands. You may consider using that instead to run your program in an environment with the necessary secrets.
@param [Array<String>] variable_ids list of variable ids to fetch @return [Array<String>] a list of variable values corresponding to the variable ids. @raise [RestClient::Forbidden, RestClient::ResourceNotFound] if any of the variables don’t exist or aren’t accessible.
# File lib/conjur/api/variables.rb, line 50 def variable_values variable_ids raise ArgumentError, "Variables list must be an array" unless variable_ids.kind_of? Array raise ArgumentError, "Variables list is empty" if variable_ids.empty? JSON.parse(url_for(:secrets_values, credentials, variable_ids).get.body) end
# File lib/conjur/api/authn.rb, line 25 def whoami JSON.parse(url_for(:whoami, credentials).get) end
Private Instance Methods
Tries to get the username (subject) from a JWT API
token by examining its content.
@return [String] of the ‘sub’ payload field from the JWT if present, otherwise return nil
# File lib/conjur/base.rb, line 333 def jwt_username raw_token return nil unless raw_token return nil unless raw_token.include? 'payload' JSON.parse(Base64.strict_decode64(raw_token["payload"]))["sub"] end
Checks if the token is old (or not present).
@return [Boolean]
# File lib/conjur/base.rb, line 351 def needs_token_refresh? !@token || @authenticator.needs_token_refresh? end
Tries to refresh the token if possible.
@return [Hash, false] false if the token couldn’t be refreshed due to unavailable API
key; otherwise, the new token.
# File lib/conjur/base.rb, line 344 def refresh_token @token = @authenticator.refresh_token end