diff --git a/source/lib/vagrant-openstack-provider/client/keystone.rb b/source/lib/vagrant-openstack-provider/client/keystone.rb index 7c16f89..615b910 100644 --- a/source/lib/vagrant-openstack-provider/client/keystone.rb +++ b/source/lib/vagrant-openstack-provider/client/keystone.rb @@ -23,7 +23,12 @@ def authenticate(env) post_body = get_body_2 config auth_url = get_auth_url_2 env elsif config.identity_api_version == '3' - post_body = get_body_3 config + if config.auth_type == 'token' + @logger.info('Using token credentials.') + post_body = get_body_3_token config + else + post_body = get_body_3 config + end auth_url = get_auth_url_3 env end @@ -37,7 +42,11 @@ def authenticate(env) if config.identity_api_version == '2' post_body[:auth][:passwordCredentials][:password] = config.password elsif config.identity_api_version == '3' - post_body[:auth][:identity][:password][:user][:password] = config.password + if config.auth_type == 'token' + post_body[:auth][:identity][:token][:id] = config.auth_token + else + post_body[:auth][:identity][:password][:user][:password] = config.password + end end authentication = RestUtils.post(env, auth_url, post_body.to_json, headers) do |response| @@ -112,6 +121,20 @@ def get_body_3(config) } end + def get_body_3_token(_config) + { + auth: + { + identity: { + methods: ['token'], + token: { + id: '****' + } + } + } + } + end + def get_auth_url_3(env) url = env[:machine].provider_config.openstack_auth_url return url if url.match(%r{/tokens/*$}) diff --git a/source/lib/vagrant-openstack-provider/config.rb b/source/lib/vagrant-openstack-provider/config.rb index 2d267ec..e63d08e 100644 --- a/source/lib/vagrant-openstack-provider/config.rb +++ b/source/lib/vagrant-openstack-provider/config.rb @@ -9,6 +9,12 @@ class Config < Vagrant.plugin('2', :config) # attr_accessor :password + # Auth type for Openstack. Either 'token' or 'password' + attr_accessor :auth_type + + # Auth token to use instead of user/pass, if auth_type is 'token' + attr_accessor :auth_token + # The compute service url to access Openstack. If nil, it will read from hypermedia catalog form REST API # attr_accessor :openstack_compute_url @@ -265,6 +271,8 @@ class Config < Vagrant.plugin('2', :config) def initialize @password = UNSET_VALUE + @auth_type = UNSET_VALUE + @auth_token = UNSET_VALUE @openstack_compute_url = UNSET_VALUE @openstack_network_url = UNSET_VALUE @openstack_volume_url = UNSET_VALUE @@ -356,6 +364,8 @@ def merge(other) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity def finalize! @password = nil if @password == UNSET_VALUE + @auth_type = nil if @auth_type == UNSET_VALUE + @auth_token = nil if @auth_token == UNSET_VALUE @openstack_compute_url = nil if @openstack_compute_url == UNSET_VALUE @openstack_network_url = nil if @openstack_network_url == UNSET_VALUE @openstack_orchestration_url = nil if @openstack_orchestration_url == UNSET_VALUE @@ -440,8 +450,13 @@ def rsync_include(inc) def validate(machine) errors = _detected_errors - errors << I18n.t('vagrant_openstack.config.password_required') if @password.nil? || @password.empty? - errors << I18n.t('vagrant_openstack.config.username_required') if @username.nil? || @username.empty? + if @auth_type == 'token' + errors << I18n.t('vagrant_openstack.config.auth_token_required') if @auth_token.nil? || @auth_token.empty? + else + errors << I18n.t('vagrant_openstack.config.password_required') if @password.nil? || @password.empty? + errors << I18n.t('vagrant_openstack.config.username_required') if @username.nil? || @username.empty? + end + errors << I18n.t('vagrant_openstack.config.invalid_api_version') unless %w(2 3).include?(@identity_api_version) validate_api_version(errors) @@ -478,7 +493,7 @@ def validate_api_version(errors) errors << I18n.t('vagrant_openstack.config.tenant_name_required') if @tenant_name.nil? || @tenant_name.empty? errors << I18n.t('vagrant_openstack.config.invalid_endpoint_type') unless %w(publicURL adminURL internalURL).include?(@endpoint_type) elsif @identity_api_version == '3' - if @domain_name == UNSET_VALUE || @domain_name.nil? || @domain_name.empty? + if (@domain_name == UNSET_VALUE || @domain_name.nil? || @domain_name.empty?) && @auth_type != 'token' if (@user_domain_name.nil? || @user_domain_name.empty?) && (@project_domain_name_name.nil? || @project_domain_name.empty?) errors << I18n.t('vagrant_openstack.config.domain_required') elsif @user_domain_name.nil? || @user_domain_name.empty? @@ -487,7 +502,7 @@ def validate_api_version(errors) errors << I18n.t('vagrant_openstack.config.project_domain_required') end end - errors << I18n.t('vagrant_openstack.config.project_name_required') if @project_name.nil? || @project_name.empty? + errors << I18n.t('vagrant_openstack.config.project_name_required') if (@project_name.nil? || @project_name.empty?) && @auth_type != 'token' errors << I18n.t('vagrant_openstack.config.invalid_interface_type') unless %w(public admin internal).include?(@interface_type) end end diff --git a/source/locales/en.yml b/source/locales/en.yml index 933ed5c..b694566 100644 --- a/source/locales/en.yml +++ b/source/locales/en.yml @@ -101,6 +101,8 @@ en: Sync folders are disabled in the provider configuration config: + auth_token_required: |- + An auth_token is required when auth_type is 'token' (Password and username are ignored). password_required: |- A password is required. username_required: |-