module OpenShift::Controller::Authentication

Protected Instance Methods

auth_service() click to toggle source

Same note as for #broker_key_auth

# File lib/openshift/controller/authentication.rb, line 123
def auth_service
  @auth_service ||= OpenShift::AuthService.instance
end
authenticate_user!() click to toggle source

Filter a request to require an authenticated user

FIXME Handle exceptions more consistently, gracefully recover from misbehaving

services
# File lib/openshift/controller/authentication.rb, line 34
def authenticate_user!
  return @cloud_user if @cloud_user

  #
  # Each authentication type may return nil if no auth info is present,
  # false if the user failed authentication (may optionally render a response),
  # or a Hash with the following keys:
  #
  #   :user
  #     If present, use this user as the current request.  The current_identity
  #     field on the user will be used as the current identity, and will not
  #     be persisted.
  #
  #   :username
  #   :provider (CURRENTLY IGNORED)
  #     A user unique identifier, and a scoping provider.  The default provider
  #     is nil. :username must be unique within the provider scope.
  #
  info = authentication_types.find{ |i| not i.nil? }

  return if response_body
  unless info && (info[:username].present? || info[:user].present?)
    request_http_basic_authentication
    return
  end

  scopes = info[:scopes] || Scope::SESSION
  user = info[:user] ?
    info[:user] :
    impersonate(CloudUser.find_or_create_by_identity(info[:provider], info[:username]))

  raise "Service did not set the user login attribute" unless user.login.present?

  user.auth_method = info[:auth_method] || :login
  user.scopes = @current_user_scopes = scopes
  @cloud_user = user
  log_actions_as(user)

  headers['X-OpenShift-Identity'] = user.login
  headers['X-OAuth-Scopes'] = scopes

  log_action("AUTHENTICATE", nil, true, "Authenticated", 'IP' => request.remote_ip, 'SCOPES' => scopes)

  return unless check_controller_scopes

  user

rescue OpenShift::AccessDeniedException => e
  render_error(:unauthorized, e.message, 1)
end
authenticate_user_from_credentials(username, password) click to toggle source

Attempt to locate a user by their credentials. No impersonation is allowed.

This method is intended to be used from specific endpoints that must challenge authentication with credentials only. It is not used at this time.

# File lib/openshift/controller/authentication.rb, line 93
def authenticate_user_from_credentials(username, password)
  info =
    if auth_service.respond_to?(:authenticate) && auth_service.method(:authenticate).arity == 2
      auth_service.authenticate(username, password).tap do |info|
        log_action("CREDENTIAL_AUTHENTICATE", nil, true, "Access denied by auth service", {'IP' => request.remote_ip, 'LOGIN' => username}) unless info
      end
    end || nil

  if info
    raise "Authentication service must return a username with its response" if info[:username].nil?

    user = CloudUser.find_or_create_by_identity(info[:provider], info[:username])
    log_action("CREDENTIAL_AUTHENTICATE", nil, true, "Authenticated via credentials", {'LOGIN' => username, 'IP' => request.remote_ip})
    user
  end
rescue OpenShift::AccessDeniedException => e
  logger.debug "Service rejected credentials #{e.message} (#{e.class})\n  #{e.backtrace.join("\n  ")}"
  log_action("CREDENTIAL_AUTHENTICATE", nil, true, "Access denied by auth service", {'LOGIN' => username, 'IP' => request.remote_ip, 'ERROR' => e.message})
  nil
end
broker_key_auth() click to toggle source

This should be abstracted to an OpenShift.config service implementation that allows the product to easily reuse these without having to be exposed as helpers.

# File lib/openshift/controller/authentication.rb, line 119
def broker_key_auth
  @broker_key_auth ||= OpenShift::Auth::BrokerKey.new
end
check_controller_scopes() click to toggle source
# File lib/openshift/controller/authentication.rb, line 127
def check_controller_scopes
  if current_user_scopes.empty?
    render_error(:forbidden, "You are not authorized to perform any operations.", 1)
    false
  elsif !current_user_scopes.any?{ |s| s.allows_action?(self) }
    render_error(:forbidden, "This action is not allowed with your current authorization.", 1)
    false
  else
    true
  end
end
current_user() click to toggle source

Return the currently authenticated user or nil

# File lib/openshift/controller/authentication.rb, line 15
def current_user
  @cloud_user
end
current_user_scopes() click to toggle source
# File lib/openshift/controller/authentication.rb, line 25
def current_user_scopes
  @current_user_scopes || Scope::NONE
end
user_signed_in?() click to toggle source

True if the user is currently authenticated

# File lib/openshift/controller/authentication.rb, line 21
def user_signed_in?
  current_user.present?
end

Private Instance Methods

authenticate_basic_via_service() click to toggle source

Given an HTTP Authorization: BASIC header, determine whether the user credentials (if provided) are valid.

# File lib/openshift/controller/authentication.rb, line 211
def authenticate_basic_via_service
  return unless auth_service.respond_to? :authenticate

  username = nil
  authenticate_with_http_basic do |u, p|
    next if u.blank?
    username = u
    if auth_service.method(:authenticate).arity == 2
      auth_service.authenticate(u, p)
    else
      #DEPRECATED - Will be removed in favor of #authenticate_request
      auth_service.authenticate(request, u, p)
    end
  end.tap do |info|
    if info == false
      log_action_for(username, nil, "AUTHENTICATE", nil, true, "Access denied by authenticate", {'IP' => request.remote_ip})
    end
  end
rescue OpenShift::AccessDeniedException => e
  logger.debug "Service rejected credentials #{e.message} (#{e.class})\n  #{e.backtrace.join("\n  ")}"
  log_action_for(username, nil, "AUTHENTICATE", nil, true, "Access denied by authenticate", {'IP' => request.remote_ip, 'ERROR' => e.message})
  false
end
authenticate_bearer_token() click to toggle source

If an HTTP Authorization Bearer header is provided, check against the authorization table for access.

# File lib/openshift/controller/authentication.rb, line 168
def authenticate_bearer_token
  authenticate_with_bearer_token do |token|
    if auth = Authorization.authenticate(token)
      if auth.accessible?
        user = auth.user
        #user.current_identity = Identity.for('authorization_token', auth.id, auth.created_at)
        {:user => user, :auth_method => :authorization_token, :scopes => auth.scopes_list}
      else
        request_http_bearer_token_authentication(:invalid_token, 'The access token expired')
        log_action("AUTHENTICATE", nil, true, "Access denied by bearer token", {'TOKEN' => auth.token, 'IP' => request.remote_ip, 'FORBID' => 'expired'})
        false
      end
    else
      request_http_bearer_token_authentication(:invalid_token, 'The access token is not recognized')
      log_action("AUTHENTICATE", nil, true, "Access denied by bearer token", {'TOKEN' => token, 'IP' => request.remote_ip, 'FORBID' => 'does_not_exist'})
      false
    end
  end
end
authenticate_broker_key() click to toggle source

If broker key authentication was requested, validate it.

# File lib/openshift/controller/authentication.rb, line 157
def authenticate_broker_key
  broker_key_auth.authenticate_request(self)
rescue OpenShift::AccessDeniedException => e
  log_action("AUTHENTICATE", nil, false, "Access denied by broker key", {'IP' => request.remote_ip, 'ERROR' => e.message})
  false
end
authenticate_request_via_service() click to toggle source

If the authentication service supports full request authentication, invoke it.

# File lib/openshift/controller/authentication.rb, line 192
def authenticate_request_via_service
  return unless auth_service.respond_to? :authenticate_request

  auth_service.authenticate_request(self).tap do |info|
    if info == false || response_body
      log_action("AUTHENTICATE", nil, true, "Access denied by authenticate_request", {'IP' => request.remote_ip})
      return false
    end
  end
rescue OpenShift::AccessDeniedException => e
  logger.debug "Service rejected request #{e.message} (#{e.class})\n  #{e.backtrace.join("\n  ")}"
  log_action("AUTHENTICATE", nil, true, "Access denied by authenticate_request", {'IP' => request.remote_ip, 'ERROR' => e.message})
  false
end
authentication_types() click to toggle source

Lazily evaluate the authentication types on this class

# File lib/openshift/controller/authentication.rb, line 143
def authentication_types
  Enumerator.new do |y|
    [
      :authenticate_broker_key,
      :authenticate_bearer_token,
      :authenticate_request_via_service,
      :authenticate_basic_via_service,
    ].each{ |sym| y.yield send(sym) }
  end
end
impersonate(user) click to toggle source

Given a user and a request, have the current user impersonate another.

# File lib/openshift/controller/authentication.rb, line 238
def impersonate(user)
  other = request.headers["X-Impersonate-User"]
  return user unless other.present?

  unless user.capabilities['subaccounts'] == true
    log_action_for(user.login, user.id, "IMPERSONATE", nil, true, "Failed to impersonate", {'SUBJECT' => other, 'IP' => request.remote_ip, 'FORBID' => 'no_subaccount_capability'})
    raise OpenShift::AccessDeniedException, "Insufficient privileges to access user #{other}"
  end

  CloudUser.find_or_create_by_identity("impersonation/#{user.id}", other, parent_user_id: user.id) do |existing_user, existing_identity|
    if existing_user.parent_user_id != user.id
      log_action_for(user.login, user.id, "IMPERSONATE", nil, true, "Failed to impersonate", {'SUBJECT' => other, 'IP' => request.remote_ip, 'FORBID' => 'not_child_account'})
      raise OpenShift::AccessDeniedException, "Account is not associated with impersonate account #{other}"
    end
  end.tap do |other_user|
    log_action_for(user.login, user.id, "IMPERSONATE", nil, true, "Impersonation successful", {'SUBJECT_ID' => other_user.id})
  end
end