Class: Occi::Api::Client::Http::AuthnPlugins::Keystone

Inherits:
Base
  • Object
show all
Defined in:
lib/occi/api/client/http/authn_plugins/keystone.rb

Constant Summary collapse

KEYSTONE_URI_REGEXP =
/^(Keystone|snf-auth) uri='(.+)'$/

Instance Attribute Summary

Attributes inherited from Base

#env_ref, #fallbacks, #options

Instance Method Summary collapse

Methods inherited from Base

#initialize

Constructor Details

This class inherits a constructor from Occi::Api::Client::Http::AuthnPlugins::Base

Instance Method Details

#authenticate(options = {}) ⇒ Object


27
28
29
30
31
32
# File 'lib/occi/api/client/http/authn_plugins/keystone.rb', line 27

def authenticate(options = {})
  # OCCI-OS doesn't support HEAD method!
  response = @env_ref.class.get "#{@env_ref.endpoint.to_s}/-/"
  raise ::Occi::Api::Client::Errors::AuthnError,
        "Authentication failed with code #{response.code.to_s}!" unless response.success?
end

#get_first_working_tenantObject (private)


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/occi/api/client/http/authn_plugins/keystone.rb', line 100

def get_first_working_tenant
  response = @env_ref.class.get(
    "#{@keystone_url}/v2.0/tenants",
    :headers => get_req_headers
  )
  Occi::Api::Log.debug response.inspect

  raise ::Occi::Api::Client::Errors::AuthnError,
        "Keystone didn't return any tenants, fallback failed!" if response['tenants'].blank?

  response['tenants'].each do |tenant|
    begin
      Occi::Api::Log.debug "Authenticating for tenant #{tenant['name'].inspect}"
      set_auth_token(tenant['name'])

      # found a working tenant, stop looking
      break
    rescue ::Occi::Api::Client::Errors::AuthnError
      # ignoring and trying the next tenant
    end
  end
end

#get_keystone_req(tenant = nil) ⇒ Object (private)


78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/occi/api/client/http/authn_plugins/keystone.rb', line 78

def get_keystone_req(tenant = nil)
  if @options[:original_type] == "x509"
    body = { "auth" => { "voms" => true } }
  elsif @options[:username] && @options[:password]
    body = {
      "auth" => {
        "passwordCredentials" => {
          "username" => @options[:username],
          "password" => @options[:password]
        }
      }
    }
  else
    raise ::Occi::Api::Client::Errors::AuthnError,
          "Unable to request a token from Keystone! Chosen " \
          "AuthN is not supported, fallback failed!"
  end

  body['auth']['tenantName'] = tenant unless tenant.blank?
  body.to_json
end

#get_req_headersObject (private)


123
124
125
126
127
128
129
# File 'lib/occi/api/client/http/authn_plugins/keystone.rb', line 123

def get_req_headers
  headers = @env_ref.class.headers.clone
  headers['Content-Type'] = "application/json"
  headers['Accept'] = headers['Content-Type']

  headers
end

#process_headers(response) ⇒ Object (private)


47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/occi/api/client/http/authn_plugins/keystone.rb', line 47

def process_headers(response)
  authN_header = response.headers['www-authenticate']

  if authN_header.blank?
    raise ::Occi::Api::Client::Errors::AuthnError,
          "Response does not contain the www-authenticate header, fallback failed!"
  end

  match = KEYSTONE_URI_REGEXP.match(authN_header)
  raise ::Occi::Api::Client::Errors::AuthnError,
        "Unable to get Keystone's URL from the response, fallback failed!" unless match && match[2]

  @keystone_url = match[2].chomp('/').chomp('/v2.0')
end

#set_auth_token(tenant = nil) ⇒ Object (private)


62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/occi/api/client/http/authn_plugins/keystone.rb', line 62

def set_auth_token(tenant = nil)
  response = @env_ref.class.post(
    "#{@keystone_url}/v2.0/tokens",
    :body => get_keystone_req(tenant),
    :headers => get_req_headers
  )
  Occi::Api::Log.debug response.inspect

  if response.success?
    @env_ref.class.headers['X-Auth-Token'] = response['access']['token']['id']
  else
    raise ::Occi::Api::Client::Errors::AuthnError,
          "Unable to get a token from Keystone, fallback failed!"
  end
end

#set_keystone_base_urlObject (private)


36
37
38
39
40
41
42
43
44
45
# File 'lib/occi/api/client/http/authn_plugins/keystone.rb', line 36

def set_keystone_base_url
  response = @env_ref.class.head "#{@env_ref.endpoint.to_s}/-/"
  Occi::Api::Log.debug response.inspect

  return if response.success?
  raise ::Occi::Api::Client::Errors::AuthnError,
        "Keystone AuthN failed with #{response.code.to_s}!" unless response.code == 401

  process_headers(response)
end

#setup(options = {}) ⇒ Object


9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/occi/api/client/http/authn_plugins/keystone.rb', line 9

def setup(options = {})
  # get Keystone URL if possible
  set_keystone_base_url

  if !ENV['ROCCI_CLIENT_KEYSTONE_TENANT'].blank?
    # get a scoped token for the specified tenant directly
    set_auth_token ENV['ROCCI_CLIENT_KEYSTONE_TENANT']
  else
    # get an unscoped token, use the unscoped token
    # for tenant discovery and get a scoped token
    set_auth_token
    get_first_working_tenant
  end

  raise ::Occi::Api::Client::Errors::AuthnError,
        "Unable to get a tenant from Keystone, fallback failed!" if @env_ref.class.headers['X-Auth-Token'].blank?
end