Manage updates to keystone user password with keystone_user

Fix bug 1170380

o Test if the current password works and if not reset it allowing users
  to reset passwords with puppet
o Also move admin token from command line of keystone command in
  favour of using a env variable
o Note : this will break folsom support

Change-Id: I4bae9cad767cdd141ae76343298f7ac43c2a1f90
This commit is contained in:
Derek Higgins
2013-04-18 10:46:56 -04:00
parent f7f2ef7d45
commit 71d2a6c130
5 changed files with 124 additions and 21 deletions

View File

@@ -53,13 +53,33 @@ class Puppet::Provider::Keystone < Puppet::Provider
@keystone_file = nil
end
# the path to withenv changes between versions of puppet, so redefining this function here,
# Run some code with a specific environment. Resets the environment at the end of the code.
def self.withenv(hash, &block)
saved = ENV.to_hash
hash.each do |name, val|
ENV[name.to_s] = val
end
block.call
ensure
ENV.clear
saved.each do |name, val|
ENV[name] = val
end
end
def self.auth_keystone(*args)
authenv = {:OS_SERVICE_TOKEN => admin_token}
begin
remove_warnings(keystone('--token', admin_token, '--endpoint', admin_endpoint, args))
withenv authenv do
remove_warnings(keystone('--endpoint', admin_endpoint, args))
end
rescue Exception => e
if (e.message =~ /\[Errno 111\] Connection refused/) or (e.message =~ /\(HTTP 400\)/)
sleep 10
remove_warnings(keystone('--token', admin_token, '--endpoint', admin_endpoint, args))
sleep 10
withenv authenv do
remove_warnings(keystone('--endpoint', admin_endpoint, args))
end
else
raise(e)
end
@@ -70,6 +90,29 @@ class Puppet::Provider::Keystone < Puppet::Provider
self.class.auth_keystone(args)
end
def self.creds_keystone(name, tenant, password, *args)
authenv = {:OS_USERNAME => name, :OS_TENANT_NAME => tenant, :OS_PASSWORD => password}
begin
withenv authenv do
remove_warnings(keystone('--os-auth-url', admin_endpoint, args))
end
rescue Exception => e
if (e.message =~ /\[Errno 111\] Connection refused/) or (e.message =~ /\(HTTP 400\)/)
sleep 10
withenv authenv do
remove_warnings(keystone('--os-auth-url', admin_endpoint, args))
end
else
raise(e)
end
end
end
def creds_keystone(name, tenant, password, *args)
self.class.creds_keystone(name, tenant, password, args)
end
private
def self.list_keystone_objects(type, number_columns, *args)

View File

@@ -80,20 +80,23 @@ Puppet::Type.type(:keystone_user).provide(
)
end
# def password
# Puppet.warning("Cannot retrieve password")
# user_hash[resource[:name]][:password]
# end
#
# def password=(value)
# Puppet.warning('Cannot update password')
# # user-password-update does not support the ability know what the
# # current value is
# #auth_keystone(
# # 'user-password-update',
# # '--pass', value,
# # user_hash[resource[:name]][:id]
# end
def password
# if we don't know a password we can't test it
return nil if resource[:password] == nil
# 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
begin
token_out = creds_keystone(resource[:name], resource[:tenant], resource[:password], "token-get")
rescue Exception => e
return nil if e.message =~ /Not Authorized/
raise e
end
return resource[:password]
end
def password=(value)
auth_keystone('user-password-update', '--pass', value, user_hash[resource[:name]][:id])
end
def tenant
user_hash[resource[:name]][:tenant]

View File

@@ -24,8 +24,23 @@ Puppet::Type.newtype(:keystone_user) do
end
end
newparam(:password) do
newproperty(:password) do
newvalues(/\S+/)
def change_to_s(currentvalue, newvalue)
if currentvalue == :absent
return "created password"
else
return "changed password"
end
end
def is_to_s( currentvalue )
return '[old password redacted]'
end
def should_to_s( newvalue )
return '[new password redacted]'
end
end
newproperty(:tenant) do

View File

@@ -81,7 +81,7 @@ describe Puppet::Provider::Keystone do
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
mock.expects(:read).with('/etc/keystone/keystone.conf')
klass.expects(:sleep).with(10).returns(nil)
klass.expects(:keystone).twice.with('--token', 'foo', '--endpoint', 'http://127.0.0.1:35357/v2.0/', ['test_retries']).raises(Exception, valid_message).then.returns('')
klass.expects(:keystone).twice.with('--endpoint', 'http://127.0.0.1:35357/v2.0/', ['test_retries']).raises(Exception, valid_message).then.returns('')
klass.auth_keystone('test_retries')
end
end
@@ -97,8 +97,6 @@ describe Puppet::Provider::Keystone do
klass.expects(
:keystone
).with(
'--token',
'foo',
'--endpoint',
'http://127.0.0.1:35357/v2.0/',
['test_retries']

View File

@@ -0,0 +1,44 @@
require 'puppet'
require 'spec_helper'
require 'puppet/provider/keystone_user/keystone'
provider_class = Puppet::Type.type(:keystone_user).provider(:keystone)
describe provider_class do
describe 'when updating a user' do
let :resource do
Puppet::Type::Keystone_user.new(
{
:name => 'foo',
:ensure => 'present',
:enabled => 'True',
:tenant => 'foo2',
:email => 'foo@foo.com',
:password => 'passwd'
}
)
end
let :provider do
provider_class.new(resource)
end
before :each do
provider_class.expects(:build_user_hash).returns(
'foo' => {:id => 'id', :name => 'foo', :tenant => 'foo2', :password => 'passwd'}
)
end
after :each do
# reset global state
provider_class.prefetch(nil)
end
it 'should call user-password-update to change password' do
provider.expects(:auth_keystone).with('user-password-update', '--pass', 'newpassword', 'id')
provider.password=('newpassword')
end
end
end