class AuthenticationStrategies::KeystoneStrategy

Constants

ALLOWED_MAPPING_TYPES
SUPPORTED_CERT_SOURCES

Public Instance Methods

allowed_access?(tenant_name) click to toggle source
# File lib/authentication_strategies/keystone_strategy.rb, line 201
def allowed_access?(tenant_name)
  return false if tenant_name.blank?

  case OPTIONS.access_policy
  when 'blacklist'
    !blacklisted_tenant?(tenant_name)
  when 'whitelist'
    whitelisted_tenant?(tenant_name)
  else
    raise Errors::ConfigurationParsingError,
          "Unsupported Keystone access policy #{OPTIONS.access_policy.inspect}!"
  end
end
auth_request() click to toggle source
# File lib/authentication_strategies/keystone_strategy.rb, line 15
def auth_request
  @auth_request ||= ::ActionDispatch::Request.new(env)
end
authenticate!() click to toggle source

@see AuthenticationStrategies::DummyStrategy

# File lib/authentication_strategies/keystone_strategy.rb, line 34
def authenticate!
  Rails.logger.debug "[AuthN] [#{self.class}] Authenticating with X-Auth-Token"

  if self.class.revoked_token?(auth_request.headers['X-Auth-Token'])
    fail! 'Your Keystone token has been revoked!'
    return
  end

  store = self.class.init_x509_store(
    OPTIONS.keystone_pki_trust.ca_cert,
    OPTIONS.keystone_pki_trust.ca_path
  )

  crt = OpenSSL::X509::Certificate.new(File.read(OPTIONS.keystone_pki_trust.signing_cert))

  verified = begin
    cms_token = OpenSSL::CMS.read_cms(self.class.keystone2cms(auth_request.headers['X-Auth-Token']))
    cms_token.verify([crt], store, nil, nil)
  rescue => e
    Rails.logger.warn "[AuthN] [#{self.class}] OpenSSL::CMS validation "                            "failed with #{e.message.inspect} on: #{auth_request.headers['X-Auth-Token']}"
    fail! 'Your Keystone token is invalid!'
    return
  end

  unless verified
    fail! 'Failed to verify your Keystone token!'
    return
  end

  extracted_token = self.class.extract_token(cms_token)
  unless extracted_token
    fail! 'Your Keystone token is expired or malformed!'
    return
  end

  unless self.class.allowed_access?(extracted_token.access_.token_.tenant_.name)
    fail! "Tenant #{extracted_token.access_.token_.tenant_.name.inspect} is not allowed!"
    return
  end

  user = self.class.user_factory(extracted_token)
  unless user
    fail! 'Couldn\t retrieve user and tenant information from the token!'
    return
  end

  Rails.logger.debug "[AuthN] [#{self.class}] Authenticated #{user.to_hash.inspect}"
  success! user.deep_freeze
end
blacklisted_tenant?(tenant_name) click to toggle source
# File lib/authentication_strategies/keystone_strategy.rb, line 215
def blacklisted_tenant?(tenant_name)
  blacklist = AuthenticationStrategies::Helpers::YamlHelper.read_yaml(OPTIONS.blacklist) || []
  blacklist.include?(tenant_name)
end
expired_token?(extracted_token) click to toggle source
# File lib/authentication_strategies/keystone_strategy.rb, line 145
def expired_token?(extracted_token)
  exp_time = extracted_token.access_.token_.expires
  return true unless exp_time
  DateTime.iso8601(exp_time) < DateTime.now
end
extract_token(cms_token) click to toggle source
# File lib/authentication_strategies/keystone_strategy.rb, line 125
def extract_token(cms_token)
  begin
    data = Hashie::Mash.new(JSON.parse(cms_token.data))
    expired_token?(data) ? nil : data
  rescue => e
    Rails.logger.error "[AuthN] [#{self.name}] Failed to "                               "extract data from CMS token! #{e.message}"
    raise e
  end
end
file_marcopolo?(path) click to toggle source
# File lib/authentication_strategies/keystone_strategy.rb, line 196
def file_marcopolo?(path)
  return false if path.blank?
  File.readable?(path) && !File.zero?(path)
end
get_memcaches() click to toggle source
# File lib/authentication_strategies/keystone_strategy.rb, line 176
def get_memcaches
  return unless OPTIONS.memcaches
  OPTIONS.memcaches.respond_to?(:each) ? OPTIONS.memcaches : OPTIONS.memcaches.split
end
get_trl(url) click to toggle source
# File lib/authentication_strategies/keystone_strategy.rb, line 151
def get_trl(url)
  dalli = Backend.dalli_instance_factory(
    "keystone_strategy_trl_cache",
    get_memcaches, { expire_after: 2.minutes },
    url.gsub(/\W+/, '_')
  )

  trl_parsed = nil
  begin
    unless trl = dalli.get('trl')
      trl = open(url).read
      dalli.set('trl', trl)
    end

    trl_parsed = Hashie::Mash.new(JSON.parse(trl))
  rescue => e
    Rails.logger.error "[AuthN] [#{self.name}] Failed to "                               "retrieve and parse TRL from #{url.inspect}! #{e.message}"
    dalli.delete('trl')
    raise e
  end

  trl_parsed
end
have_certs?() click to toggle source
# File lib/authentication_strategies/keystone_strategy.rb, line 86
def have_certs?
  # TODO: implement 'url' as a cert_source option
  unless SUPPORTED_CERT_SOURCES.include?(OPTIONS.keystone_pki_trust_.cert_source)
    raise Errors::ConfigurationParsingError,
          "Unsupported cert_source #{OPTIONS.keystone_pki_trust_.cert_source.inspect} for Keystone! "                  "Only #{SUPPORTED_CERT_SOURCES.join(', ').inspect} are allowed!"
  end

  result = file_marcopolo?(OPTIONS.keystone_pki_trust_.ca_cert) && file_marcopolo?(OPTIONS.keystone_pki_trust_.signing_cert)

  Rails.logger.warn "[AuthN] [#{self.name}] Certificates are not present or empty! Bailing out ..." unless result
  result
end
init_x509_store(ca_cert, ca_path = nil) click to toggle source
# File lib/authentication_strategies/keystone_strategy.rb, line 115
def init_x509_store(ca_cert, ca_path = nil)
  store = OpenSSL::X509::Store.new
  store.add_path(ca_path) unless ca_path.blank?

  ca_crt = OpenSSL::X509::Certificate.new(File.read(ca_cert))
  store.add_cert ca_crt

  store
end
keystone2cms(token) click to toggle source
# File lib/authentication_strategies/keystone_strategy.rb, line 100
def keystone2cms(token)
  # Wrap lines to 64 chars per line
  ary = token.scan(/.{64}/)
  size_rest = token.length - (ary.length*64)
  ary << token.scan(Regexp.new(".{#{size_rest}}$"))
  ary.flatten!
  token = ary.join("\n")

  # Fix some OS Keystone stuff (why??)
  token.gsub!('-', '/')

  # Add CMS header & trailer
  "-----BEGIN CMS-----\n#{token}\n-----END CMS-----\n"
end
mapped_name(name, type) click to toggle source
# File lib/authentication_strategies/keystone_strategy.rb, line 225
def mapped_name(name, type)
  raise "Unsupported mapping request! Type #{type.to_s.inspect} "                "is not registered!" unless ALLOWED_MAPPING_TYPES.include?(type)
  return name unless OPTIONS.send("#{type.to_s}_mapping")

  map = AuthenticationStrategies::Helpers::YamlHelper.read_yaml(OPTIONS.send("#{type.to_s}_mapfile")) || {}
  new_name = map[name] || name

  Rails.logger.debug "[AuthN] [#{self}] #{type.to_s.capitalize} name mapped #{name.inspect} -> #{new_name.inspect}"
  new_name
end
revoked_token?(original_token) click to toggle source
# File lib/authentication_strategies/keystone_strategy.rb, line 136
def revoked_token?(original_token)
  return false unless OPTIONS.keystone_pki_trust.trl_url
  trl = get_trl(OPTIONS.keystone_pki_trust.trl_url)
  return true unless trl && trl.revoked

  token_md5 = Digest::MD5.digest(original_token)
  trl.revoked.select { |rev| rev.id == token_md5 }.any?
end
store?() click to toggle source

@see AuthenticationStrategies::DummyStrategy

# File lib/authentication_strategies/keystone_strategy.rb, line 20
def store?
  false
end
user_factory(extracted_token) click to toggle source
# File lib/authentication_strategies/keystone_strategy.rb, line 181
def user_factory(extracted_token)
  return if extracted_token.access_.token_.tenant_.name.blank?
  return if extracted_token.access_.user_.username.blank?

  user = Hashie::Mash.new
  user.auth!.type = 'keystone'
  user.auth!.credentials!.tenant = mapped_name(extracted_token.access.token.tenant.name, :tenant)
  user.auth!.credentials!.username = mapped_name(extracted_token.access.user.username, :user)
  user.auth!.credentials!.token = extracted_token
  user.auth!.credentials!.verification_status = 'SUCCESS'
  user.identity = user.auth.credentials.username

  user
end
valid?() click to toggle source

@see AuthenticationStrategies::DummyStrategy

# File lib/authentication_strategies/keystone_strategy.rb, line 25
def valid?
  Rails.logger.debug "[AuthN] [#{self.class}] Checking for applicability"
  result = !auth_request.headers['X-Auth-Token'].blank? && self.class.have_certs?

  Rails.logger.debug "[AuthN] [#{self.class}] Strategy is #{result ? '' : 'not '}applicable!"
  result
end
whitelisted_tenant?(tenant_name) click to toggle source
# File lib/authentication_strategies/keystone_strategy.rb, line 220
def whitelisted_tenant?(tenant_name)
  whitelist = AuthenticationStrategies::Helpers::YamlHelper.read_yaml(OPTIONS.whitelist) || []
  whitelist.include?(tenant_name)
end