X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=3rdparty%2Fmodules%2Fkeystone%2Flib%2Fpuppet%2Fprovider%2Fkeystone_user%2Fopenstack.rb;h=98a34cd3cd05a8d5ab4a5f506ebe2de4348a3c6b;hb=71a236efd9f45828d875a0a5fd025108bb1dcada;hp=6c8d04aa292f7d9bee447cba712986590921abd2;hpb=4631045ebb77ee8622f6fa09277a50c372bcc02e;p=dsa-puppet.git diff --git a/3rdparty/modules/keystone/lib/puppet/provider/keystone_user/openstack.rb b/3rdparty/modules/keystone/lib/puppet/provider/keystone_user/openstack.rb index 6c8d04aa..98a34cd3 100644 --- a/3rdparty/modules/keystone/lib/puppet/provider/keystone_user/openstack.rb +++ b/3rdparty/modules/keystone/lib/puppet/provider/keystone_user/openstack.rb @@ -1,6 +1,5 @@ -require 'net/http' -require 'json' require 'puppet/provider/keystone' + Puppet::Type.type(:keystone_user).provide( :openstack, :parent => Puppet::Provider::Keystone @@ -8,137 +7,120 @@ Puppet::Type.type(:keystone_user).provide( desc "Provider to manage keystone users." + @credentials = Puppet::Provider::Openstack::CredentialsV2_0.new + def initialize(value={}) super(value) @property_flush = {} end def create - properties = [] + properties = [resource[:name]] if resource[:enabled] == :true properties << '--enable' elsif resource[:enabled] == :false properties << '--disable' end if resource[:password] - properties << '--password' - properties << resource[:password] + properties << '--password' << resource[:password] end if resource[:tenant] - properties << '--project' - properties << resource[:tenant] + properties << '--project' << resource[:tenant] end if resource[:email] - properties << '--email' - properties << resource[:email] + properties << '--email' << resource[:email] end - @instance = request('user', 'create', resource[:name], resource[:auth], properties) + self.class.request('user', 'create', properties) + @property_hash[:ensure] = :present end - def exists? - ! instance(resource[:name]).empty? + def destroy + self.class.request('user', 'delete', @property_hash[:id]) + @property_hash.clear end - def destroy - request('user', 'delete', resource[:name], resource[:auth]) + def flush + options = [] + if @property_flush && !@property_flush.empty? + options << '--enable' if @property_flush[:enabled] == :true + options << '--disable' if @property_flush[:enabled] == :false + # There is a --description flag for the set command, but it does not work if the value is empty + options << '--password' << resource[:password] if @property_flush[:password] + options << '--email' << resource[:email] if @property_flush[:email] + # project handled in tenant= separately + unless options.empty? + options << @property_hash[:id] + self.class.request('user', 'set', options) + end + @property_flush.clear + end end + def exists? + @property_hash[:ensure] == :present + end + + # Types properties + def enabled + bool_to_sym(@property_hash[:enabled]) + end def enabled=(value) @property_flush[:enabled] = value end - def enabled - bool_to_sym(instance(resource[:name])[:enabled]) + def email + @property_hash[:email] end + def email=(value) + @property_flush[:email] = value + end - def password=(value) - @property_flush[:password] = value + def id + @property_hash[:id] end def password - # if we don't know a password we can't test it - return nil if resource[:password] == nil - # if the user is disabled then the password can't be changed - return resource[:password] if resource[:enabled] == :false - # if replacing password is disabled, then don't change it - return resource[:password] if resource[:replace_password] == :false - # we can't get the value of the password but we can test to see if the one we know - # about works, if it doesn't then return nil, causing it to be reset - endpoint = nil - if password_credentials_set?(resource[:auth]) || service_credentials_set?(resource[:auth]) - endpoint = (resource[:auth])['auth_url'] - elsif openrc_set?(resource[:auth]) - endpoint = get_credentials_from_openrc(resource[:auth])['auth_url'] - elsif env_vars_set? - endpoint = ENV['OS_AUTH_URL'] - else - # try to get endpoint from keystone.conf - endpoint = get_admin_endpoint - end - if endpoint == nil - raise(Puppet::Error::OpenstackAuthInputError, 'Could not find auth url to check user password.') + res = nil + return res if resource[:password] == nil + if resource[:enabled] == :false || resource[:replace_password] == :false + # Unchanged password + res = resource[:password] else - auth_params = { - 'username' => resource[:name], - 'password' => resource[:password], - 'tenant_name' => resource[:tenant], - 'auth_url' => endpoint, - } - # LP#1408754 - # Ideally this would be checked with the `openstack token issue` command, - # but that command is not available with version 0.3.0 of openstackclient - # which is what ships on Ubuntu during Juno. - # Instead we'll check whether the user can authenticate with curl. - creds_hash = { - :auth => { - :passwordCredentials => { - :username => auth_params['username'], - :password => auth_params['password'], - } - } - } - url = URI.parse(endpoint) - # There is issue with ipv6 where address has to be in brackets, this causes the - # underlying ruby TCPSocket to fail. Net::HTTP.new will fail without brackets on - # joining the ipv6 address with :port or passing brackets to TCPSocket. It was - # found that if we use Net::HTTP.start with url.hostname the incriminated code - # won't be hit. - use_ssl = url.scheme == "https" ? true : false - http = Net::HTTP.start(url.hostname, url.port, {:use_ssl => use_ssl}) - request = Net::HTTP::Post.new('/v2.0/tokens') - request.body = creds_hash.to_json - request.content_type = 'application/json' - response = http.request(request) - if response.code.to_i == 401 || response.code.to_i == 403 # 401 => unauthorized, 403 => userDisabled - return nil - elsif ! (response.code == 200 || response.code == 203) - return resource[:password] + # Password validation + credentials = Puppet::Provider::Openstack::CredentialsV2_0.new + credentials.auth_url = self.class.get_endpoint + credentials.password = resource[:password] + credentials.project_name = resource[:tenant] + credentials.username = resource[:name] + begin + token = Puppet::Provider::Openstack.request('token', 'issue', ['--format', 'value'], credentials) + rescue Puppet::Error::OpenstackUnauthorizedError + # password is invalid else - raise(Puppet::Error, "Received bad response while trying to authenticate user: #{response.body}") + res = resource[:password] unless token.empty? end end + return res end - def tenant=(value) - begin - request('user', 'set', resource[:name], resource[:auth], '--project', value) - rescue Puppet::ExecutionFailure => e - if e.message =~ /You are not authorized to perform the requested action: LDAP user update/ - # read-only LDAP identity backend - just fall through - else - raise e - end - # note: read-write ldap will silently fail, not raise an exception - end - set_project(value) + def password=(value) + @property_flush[:password] = value + end + + def replace_password + @property_hash[:replace_password] + end + + def replace_password=(value) + @property_flush[:replace_password] = value end def tenant return resource[:tenant] if sym_to_bool(resource[:ignore_default_tenant]) # use the one returned from instances - tenant_name = instance(resource[:name])[:project] + tenant_name = @property_hash[:project] if tenant_name.nil? or tenant_name.empty? # if none (i.e. ldap backend) use the given one tenant_name = resource[:tenant] @@ -151,7 +133,7 @@ Puppet::Type.type(:keystone_user).provide( # If the user list command doesn't report the project, it might still be there # We don't need to know exactly what it is, we just need to know whether it's # the one we're trying to set. - roles = request('user role', 'list', resource[:name], resource[:auth], ['--project', tenant_name]) + roles = self.class.request('user role', 'list', [resource[:name], '--project', tenant_name]) if roles.empty? return nil else @@ -159,64 +141,48 @@ Puppet::Type.type(:keystone_user).provide( end end - def replace_password - instance(resource[:name])[:replace_password] - end - - def replace_password=(value) - @property_flush[:replace_password] = value - end - - def email=(value) - @property_flush[:email] = value - end - - def email - instance(resource[:name])[:email] - end - - def id - instance(resource[:name])[:id] + def tenant=(value) + self.class.request('user', 'set', [resource[:name], '--project', value]) + rescue Puppet::ExecutionFailure => e + if e.message =~ /You are not authorized to perform the requested action: LDAP user update/ + # read-only LDAP identity backend - just fall through + else + raise e + end + # note: read-write ldap will silently fail, not raise an exception + else + @property_hash[:tenant] = self.class.set_project(value, resource[:name]) end def self.instances - list = request('user', 'list', nil, nil, '--long') + list = request('user', 'list', '--long') list.collect do |user| new( :name => user[:name], :ensure => :present, :enabled => user[:enabled].downcase.chomp == 'true' ? true : false, :password => user[:password], - :tenant => user[:project], + :project => user[:project], :email => user[:email], :id => user[:id] ) end end - def instances - instances = request('user', 'list', nil, resource[:auth], '--long') - instances.collect do |user| - { - :name => user[:name], - :enabled => user[:enabled].downcase.chomp == 'true' ? true : false, - :password => user[:password], - :project => user[:project], - :email => user[:email], - :id => user[:id] - } + def self.prefetch(resources) + users = instances + resources.keys.each do |name| + if provider = users.find{ |user| user.name == name } + resources[name].provider = provider + end end end - def instance(name) - @instance ||= instances.select { |instance| instance[:name] == name }.first || {} - end - - def set_project(newproject) + def self.set_project(newproject, name) # some backends do not store the project/tenant in the user object, so we have to # to modify the project/tenant instead # First, see if the project actually needs to change - roles = request('user role', 'list', resource[:name], resource[:auth], ['--project', newproject]) + roles = request('user role', 'list', [name, '--project', newproject]) unless roles.empty? return # if already set, just skip end @@ -228,26 +194,11 @@ Puppet::Type.type(:keystone_user).provide( # ok for a user to have the _member_ role and another role. default_role = "_member_" begin - request('role', 'show', default_role, resource[:auth]) + request('role', 'show', [default_role]) rescue debug("Keystone role #{default_role} does not exist - creating") - request('role', 'create', default_role, resource[:auth]) - end - request('role', 'add', default_role, resource[:auth], - '--project', newproject, '--user', resource[:name]) - end - - def flush - options = [] - if @property_flush - (options << '--enable') if @property_flush[:enabled] == :true - (options << '--disable') if @property_flush[:enabled] == :false - # There is a --description flag for the set command, but it does not work if the value is empty - (options << '--password' << resource[:password]) if @property_flush[:password] - (options << '--email' << resource[:email]) if @property_flush[:email] - # project handled in tenant= separately - request('user', 'set', resource[:name], resource[:auth], options) unless options.empty? + request('role', 'create', [default_role]) end + request('role', 'add', [default_role, '--project', newproject, '--user', name]) end - end