From 367fcf06af3fd760f63b91115d4dd79d8e2e9bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Eeden?= Date: Sun, 11 Jan 2015 11:07:37 +0100 Subject: [PATCH] Support authentication plugins This uses CREATE USER xxx IDENTIFIED WITH yyy For tests: unix_socket is not loaded by default, so this might require: install plugin unix_socket soname 'auth_socket.so'; The mysql_native_password plugin is available by default and allows you to also set a password. Try to make it compatible with MySQL < 5.5.7 it uses version specific code with "/*!50508 stmt */" --- README.md | 8 +++++++ lib/puppet/provider/mysql_user/mysql.rb | 24 +++++++++++++------ lib/puppet/type/mysql_user.rb | 5 ++++ .../puppet/provider/mysql_user/mysql_spec.rb | 7 +++--- tests/mysql_user.pp | 11 +++++++++ 5 files changed, 45 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 9115eff..d8007d3 100644 --- a/README.md +++ b/README.md @@ -539,6 +539,14 @@ mysql_user { 'root@127.0.0.1': } ``` +It is also possible to specify an authentication plugin. +``` +mysql_user{ 'myuser'@'localhost': + ensure => 'present', + plugin => 'unix_socket', +} +``` + ####mysql_grant `mysql_grant` can be used to create grant permissions to access databases within diff --git a/lib/puppet/provider/mysql_user/mysql.rb b/lib/puppet/provider/mysql_user/mysql.rb index 066ea0b..c545fba 100644 --- a/lib/puppet/provider/mysql_user/mysql.rb +++ b/lib/puppet/provider/mysql_user/mysql.rb @@ -12,13 +12,14 @@ Puppet::Type.type(:mysql_user).provide(:mysql, :parent => Puppet::Provider::Mysq # To reduce the number of calls to MySQL we collect all the properties in # one big swoop. users.collect do |name| - query = "SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, PASSWORD FROM mysql.user WHERE CONCAT(user, '@', host) = '#{name}'" + query = "SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = '#{name}'" @max_user_connections, @max_connections_per_hour, @max_queries_per_hour, - @max_updates_per_hour, @password = mysql([defaults_file, "-NBe", query].compact).split(/\s/) + @max_updates_per_hour, @password, @plugin = mysql([defaults_file, "-NBe", query].compact).split(/\s/) new(:name => name, :ensure => :present, :password_hash => @password, + :plugin => @plugin, :max_user_connections => @max_user_connections, :max_connections_per_hour => @max_connections_per_hour, :max_queries_per_hour => @max_queries_per_hour, @@ -39,17 +40,26 @@ Puppet::Type.type(:mysql_user).provide(:mysql, :parent => Puppet::Provider::Mysq end def create - merged_name = @resource[:name].sub('@', "'@'") + merged_name = @resource[:name].sub('@', "'@'") password_hash = @resource.value(:password_hash) + plugin = @resource.value(:plugin) max_user_connections = @resource.value(:max_user_connections) || 0 max_connections_per_hour = @resource.value(:max_connections_per_hour) || 0 max_queries_per_hour = @resource.value(:max_queries_per_hour) || 0 max_updates_per_hour = @resource.value(:max_updates_per_hour) || 0 - mysql([defaults_file, '-e', "GRANT USAGE ON *.* TO '#{merged_name}' IDENTIFIED BY PASSWORD '#{password_hash}' WITH MAX_USER_CONNECTIONS #{max_user_connections} MAX_CONNECTIONS_PER_HOUR #{max_connections_per_hour} MAX_QUERIES_PER_HOUR #{max_queries_per_hour} MAX_UPDATES_PER_HOUR #{max_updates_per_hour}"].compact) - - @property_hash[:ensure] = :present - @property_hash[:password_hash] = password_hash + # Use CREATE USER to be compatible with NO_AUTO_CREATE_USER sql_mode + # This is also required if you want to specify a authentication plugin + if !plugin.nil? + mysql([defaults_file, '-e', "CREATE USER '#{merged_name}' IDENTIFIED WITH '#{plugin}'"].compact) + @property_hash[:ensure] = :present + @property_hash[:plugin] = plugin + else + mysql([defaults_file, '-e', "CREATE USER '#{merged_name}' IDENTIFIED BY PASSWORD '#{password_hash}'"].compact) + @property_hash[:ensure] = :present + @property_hash[:password_hash] = password_hash + end + mysql([defaults_file, '-e', "GRANT USAGE ON *.* TO '#{merged_name}' WITH MAX_USER_CONNECTIONS #{max_user_connections} MAX_CONNECTIONS_PER_HOUR #{max_connections_per_hour} MAX_QUERIES_PER_HOUR #{max_queries_per_hour} MAX_UPDATES_PER_HOUR #{max_updates_per_hour}"].compact) @property_hash[:max_user_connections] = max_user_connections @property_hash[:max_connections_per_hour] = max_connections_per_hour @property_hash[:max_queries_per_hour] = max_queries_per_hour diff --git a/lib/puppet/type/mysql_user.rb b/lib/puppet/type/mysql_user.rb index c408ccd..bac32d5 100644 --- a/lib/puppet/type/mysql_user.rb +++ b/lib/puppet/type/mysql_user.rb @@ -40,6 +40,11 @@ Puppet::Type.newtype(:mysql_user) do newvalue(/\w+/) end + newproperty(:plugin) do + desc 'The authentication plugin of the user.' + newvalue(/\w+/) + end + newproperty(:max_user_connections) do desc "Max concurrent connections for the user. 0 means no (or global) limit." newvalue(/\d+/) diff --git a/spec/unit/puppet/provider/mysql_user/mysql_spec.rb b/spec/unit/puppet/provider/mysql_user/mysql_spec.rb index dacbae4..a76ce70 100644 --- a/spec/unit/puppet/provider/mysql_user/mysql_spec.rb +++ b/spec/unit/puppet/provider/mysql_user/mysql_spec.rb @@ -37,7 +37,7 @@ usvn_user@localhost Puppet::Util.stubs(:which).with('mysql').returns('/usr/bin/mysql') File.stubs(:file?).with('/root/.my.cnf').returns(true) provider.class.stubs(:mysql).with([defaults_file, '-NBe', "SELECT CONCAT(User, '@',Host) AS User FROM mysql.user"]).returns('joe@localhost') - provider.class.stubs(:mysql).with([defaults_file, '-NBe', "SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, PASSWORD FROM mysql.user WHERE CONCAT(user, '@', host) = 'joe@localhost'"]).returns('10 10 10 10 *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4') + provider.class.stubs(:mysql).with([defaults_file, '-NBe', "SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = 'joe@localhost'"]).returns('10 10 10 10 *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4') end let(:instance) { provider.class.instances.first } @@ -46,7 +46,7 @@ usvn_user@localhost it 'returns an array of users' do provider.class.stubs(:mysql).with([defaults_file, '-NBe', "SELECT CONCAT(User, '@',Host) AS User FROM mysql.user"]).returns(raw_users) parsed_users.each do |user| - provider.class.stubs(:mysql).with([defaults_file, '-NBe', "SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, PASSWORD FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'"]).returns('10 10 10 10 ') + provider.class.stubs(:mysql).with([defaults_file, '-NBe', "SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'"]).returns('10 10 10 10 ') end usernames = provider.class.instances.collect {|x| x.name } @@ -63,7 +63,8 @@ usvn_user@localhost describe 'create' do it 'makes a user' do - provider.expects(:mysql).with([defaults_file, '-e', "GRANT USAGE ON *.* TO 'joe'@'localhost' IDENTIFIED BY PASSWORD '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4' WITH MAX_USER_CONNECTIONS 10 MAX_CONNECTIONS_PER_HOUR 10 MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 10"]) + provider.expects(:mysql).with([defaults_file, '-e', "CREATE USER 'joe'@'localhost' IDENTIFIED BY PASSWORD '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4'"]) + provider.expects(:mysql).with([defaults_file, '-e', "GRANT USAGE ON *.* TO 'joe'@'localhost' WITH MAX_USER_CONNECTIONS 10 MAX_CONNECTIONS_PER_HOUR 10 MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 10"]) provider.expects(:exists?).returns(true) expect(provider.create).to be_truthy end diff --git a/tests/mysql_user.pp b/tests/mysql_user.pp index 7490d33..893a03e 100644 --- a/tests/mysql_user.pp +++ b/tests/mysql_user.pp @@ -19,3 +19,14 @@ mysql_user{ 'dan@%': ensure => present, password_hash => mysql_password('blah'), } + +mysql_user{ 'socketplugin@%': + ensure => present, + plugin => 'unix_socket', +} + +mysql_user{ 'socketplugin@%': + ensure => present, + password_hash => mysql_password('blah'), + plugin => 'mysql_native_password', +}