From b6a687934ddd36103155494a1eb12a196fc11c94 Mon Sep 17 00:00:00 2001
From: Kosh <koshatul@users.noreply.github.com>
Date: Tue, 24 Sep 2019 15:08:48 +1000
Subject: [PATCH 1/5] changed split on whitespace to split on tab

---
 lib/puppet/provider/mysql_user/mysql.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/puppet/provider/mysql_user/mysql.rb b/lib/puppet/provider/mysql_user/mysql.rb
index 7d8f43c68..37ef661ff 100644
--- a/lib/puppet/provider/mysql_user/mysql.rb
+++ b/lib/puppet/provider/mysql_user/mysql.rb
@@ -24,7 +24,7 @@ def self.instances
       end
       @max_user_connections, @max_connections_per_hour, @max_queries_per_hour,
       @max_updates_per_hour, ssl_type, ssl_cipher, x509_issuer, x509_subject,
-      @password, @plugin = mysql_caller(query, 'regular').split(%r{\s})
+      @password, @plugin = mysql_caller(query, 'regular').split(%r{\t})
       @tls_options = parse_tls_options(ssl_type, ssl_cipher, x509_issuer, x509_subject)
       # rubocop:enable Metrics/LineLength
       new(name: name,

From 7feb8354bc73e4c3f0dbd4430279e6b39072c000 Mon Sep 17 00:00:00 2001
From: Daniel Carabas <daniel.carabas@puppet.com>
Date: Thu, 13 Feb 2020 15:38:51 +0200
Subject: [PATCH 2/5] Fix unit test mocking for tab separation

---
 spec/unit/puppet/provider/mysql_user/mysql_spec.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/spec/unit/puppet/provider/mysql_user/mysql_spec.rb b/spec/unit/puppet/provider/mysql_user/mysql_spec.rb
index 61af6f8c3..4c34a6ad7 100644
--- a/spec/unit/puppet/provider/mysql_user/mysql_spec.rb
+++ b/spec/unit/puppet/provider/mysql_user/mysql_spec.rb
@@ -89,7 +89,7 @@
     Puppet::Util.stubs(:which).with('mysqld').returns('/usr/sbin/mysqld')
     File.stubs(:file?).with('/root/.my.cnf').returns(true)
     provider.class.stubs(:mysql_caller).with("SELECT CONCAT(User, '@',Host) AS User FROM mysql.user", 'regular').returns('joe@localhost')
-    provider.class.stubs(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = 'joe@localhost'", 'regular').returns('10 10 10 10     *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4') # rubocop:disable Metrics/LineLength
+    provider.class.stubs(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = 'joe@localhost'", 'regular').returns('10	10	10	10					*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4') # rubocop:disable Metrics/LineLength
   end
 
   describe 'self.instances' do

From 9b767183e7b78017b97287cadf772de77119830c Mon Sep 17 00:00:00 2001
From: Kosh <koshatul@users.noreply.github.com>
Date: Tue, 2 Jun 2020 17:22:49 +1000
Subject: [PATCH 3/5] strip trailing newline on mysql_caller return

---
 lib/puppet/provider/mysql_user/mysql.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/puppet/provider/mysql_user/mysql.rb b/lib/puppet/provider/mysql_user/mysql.rb
index befb9571e..58be686ae 100644
--- a/lib/puppet/provider/mysql_user/mysql.rb
+++ b/lib/puppet/provider/mysql_user/mysql.rb
@@ -23,7 +23,7 @@ def self.instances
       end
       @max_user_connections, @max_connections_per_hour, @max_queries_per_hour,
       @max_updates_per_hour, ssl_type, ssl_cipher, x509_issuer, x509_subject,
-      @password, @plugin, @authentication_string = mysql_caller(query, 'regular').split(%r{\t})
+      @password, @plugin, @authentication_string = mysql_caller(query, 'regular').chomp.split(%r{\t})
       @tls_options = parse_tls_options(ssl_type, ssl_cipher, x509_issuer, x509_subject)
       if newer_than('mariadb' => '10.1.21') && @plugin == 'ed25519'
         # Some auth plugins (e.g. ed25519) use authentication_string

From 89d96bc97420ff4d4d032689a64629da9a347d03 Mon Sep 17 00:00:00 2001
From: Kosh <koshatul@users.noreply.github.com>
Date: Wed, 3 Jun 2020 10:04:46 +1000
Subject: [PATCH 4/5] updated tls_options return to match MySQL documentation,
 added spec and acceptance tests

---
 lib/puppet/provider/mysql_user/mysql.rb       |  6 +--
 spec/acceptance/types/mysql_user_spec.rb      | 54 +++++++++++++++++++
 .../puppet/provider/mysql_user/mysql_spec.rb  | 38 +++++++++++++
 3 files changed, 95 insertions(+), 3 deletions(-)

diff --git a/lib/puppet/provider/mysql_user/mysql.rb b/lib/puppet/provider/mysql_user/mysql.rb
index 58be686ae..eafc71492 100644
--- a/lib/puppet/provider/mysql_user/mysql.rb
+++ b/lib/puppet/provider/mysql_user/mysql.rb
@@ -244,9 +244,9 @@ def self.parse_tls_options(ssl_type, ssl_cipher, x509_issuer, x509_subject)
       ['X509']
     elsif ssl_type == 'SPECIFIED'
       options = []
-      options << "CIPHER #{ssl_cipher}" if !ssl_cipher.nil? && !ssl_cipher.empty?
-      options << "ISSUER #{x509_issuer}" if !x509_issuer.nil? && !x509_issuer.empty?
-      options << "SUBJECT #{x509_subject}" if !x509_subject.nil? && !x509_subject.empty?
+      options << "CIPHER '#{ssl_cipher}'" if !ssl_cipher.nil? && !ssl_cipher.empty?
+      options << "ISSUER '#{x509_issuer}'" if !x509_issuer.nil? && !x509_issuer.empty?
+      options << "SUBJECT '#{x509_subject}'" if !x509_subject.nil? && !x509_subject.empty?
       options
     else
       ['NONE']
diff --git a/spec/acceptance/types/mysql_user_spec.rb b/spec/acceptance/types/mysql_user_spec.rb
index 88eb79833..cd75d31cc 100644
--- a/spec/acceptance/types/mysql_user_spec.rb
+++ b/spec/acceptance/types/mysql_user_spec.rb
@@ -199,4 +199,58 @@ class { 'mysql::server': * => $ed25519_opts }
       end
     end
   end
+  context 'using user-w-subject@localhost with ISSUER and SUBJECT' do
+    describe 'adding user' do
+      pp_six = <<-MANIFEST
+          mysql_user { 'user-w-subject@localhost':
+            plugin        => 'mysql_native_password',
+            password_hash => '',
+            tls_options   => [
+              "SUBJECT '/OU=MySQL Users/CN=username'",
+              "ISSUER '/CN=Certificate Authority'",
+              "CIPHER 'EDH-RSA-DES-CBC3-SHA'",
+            ],
+          }
+      MANIFEST
+
+      it 'works without errors' do
+        idempotent_apply(pp_six)
+      end
+
+      it 'finds the user #stdout' do
+        run_shell("mysql -NBe \"select '1' from mysql.user where CONCAT(user, '@', host) = 'user-w-subject@localhost'\"") do |r|
+          expect(r.stdout).to match(%r{^1$})
+          expect(r.stderr).to be_empty
+        end
+      end
+
+      it 'shows correct ssl_type #stdout' do
+        run_shell("mysql -NBe \"select SSL_TYPE from mysql.user where CONCAT(user, '@', host) = 'user-w-subject@localhost'\"") do |r|
+          expect(r.stdout).to match(%r{^SPECIFIED$})
+          expect(r.stderr).to be_empty
+        end
+      end
+
+      it 'shows correct x509_issuer #stdout' do
+        run_shell("mysql -NBe \"select X509_ISSUER from mysql.user where CONCAT(user, '@', host) = 'user-w-subject@localhost'\"") do |r|
+          expect(r.stdout).to match(%r{^/CN=Certificate Authority$})
+          expect(r.stderr).to be_empty
+        end
+      end
+
+      it 'shows correct x509_subject #stdout' do
+        run_shell("mysql -NBe \"select X509_SUBJECT from mysql.user where CONCAT(user, '@', host) = 'user-w-subject@localhost'\"") do |r|
+          expect(r.stdout).to match(%r{^/OU=MySQL Users/CN=username$})
+          expect(r.stderr).to be_empty
+        end
+      end
+
+      it 'shows correct ssl_cipher #stdout' do
+        run_shell("mysql -NBe \"select SSL_CIPHER from mysql.user where CONCAT(user, '@', host) = 'user-w-subject@localhost'\"") do |r|
+          expect(r.stdout).to match(%r{^EDH-RSA-DES-CBC3-SHA$})
+          expect(r.stderr).to be_empty
+        end
+      end
+    end
+  end
 end
diff --git a/spec/unit/puppet/provider/mysql_user/mysql_spec.rb b/spec/unit/puppet/provider/mysql_user/mysql_spec.rb
index 5f5f6b3c9..5c8d1dc0a 100644
--- a/spec/unit/puppet/provider/mysql_user/mysql_spec.rb
+++ b/spec/unit/puppet/provider/mysql_user/mysql_spec.rb
@@ -439,6 +439,44 @@
     end
   end
 
+  describe 'tls_options=required' do
+    it 'adds mTLS option grant in mysql 5.5' do
+      provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.5'][:string])
+      provider.class.expects(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE ISSUER '/CN=Certificate Authority' AND SUBJECT '/OU=MySQL Users/CN=Username'", 'system').returns('0')
+
+      provider.expects(:tls_options).returns(['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\''])
+      provider.tls_options = ['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\'']
+    end
+    it 'adds mTLS option grant in mysql 5.6' do
+      provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.6'][:string])
+      provider.class.expects(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE ISSUER '/CN=Certificate Authority' AND SUBJECT '/OU=MySQL Users/CN=Username'", 'system').returns('0')
+
+      provider.expects(:tls_options).returns(['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\''])
+      provider.tls_options = ['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\'']
+    end
+    it 'adds mTLS option grant in mysql < 5.7.6' do
+      provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.7.1'][:string])
+      provider.class.expects(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE ISSUER '/CN=Certificate Authority' AND SUBJECT '/OU=MySQL Users/CN=Username'", 'system').returns('0')
+
+      provider.expects(:tls_options).returns(['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\''])
+      provider.tls_options = ['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\'']
+    end
+    it 'adds mTLS option grant in mysql >= 5.7.6' do
+      provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.7.6'][:string])
+      provider.class.expects(:mysql_caller).with("ALTER USER 'joe'@'localhost' REQUIRE ISSUER '/CN=Certificate Authority' AND SUBJECT '/OU=MySQL Users/CN=Username'", 'system').returns('0')
+
+      provider.expects(:tls_options).returns(['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\''])
+      provider.tls_options = ['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\'']
+    end
+    it 'adds mTLS option grant in mariadb-10.0' do
+      provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mariadb-10.0'][:string])
+      provider.class.expects(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE ISSUER '/CN=Certificate Authority' AND SUBJECT '/OU=MySQL Users/CN=Username'", 'system').returns('0')
+
+      provider.expects(:tls_options).returns(['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\''])
+      provider.tls_options = ['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\'']
+    end
+  end
+
   ['max_user_connections', 'max_connections_per_hour', 'max_queries_per_hour', 'max_updates_per_hour'].each do |property|
     describe property do
       it "returns #{property}" do

From 698c21ce1fe4b66dd25fbdac40d5398636a3bb0d Mon Sep 17 00:00:00 2001
From: Kosh <koshatul@users.noreply.github.com>
Date: Wed, 3 Jun 2020 18:33:55 +1000
Subject: [PATCH 5/5] fix for litmus acceptance failures

---
 spec/acceptance/types/mysql_user_spec.rb | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/spec/acceptance/types/mysql_user_spec.rb b/spec/acceptance/types/mysql_user_spec.rb
index cd75d31cc..8129a8908 100644
--- a/spec/acceptance/types/mysql_user_spec.rb
+++ b/spec/acceptance/types/mysql_user_spec.rb
@@ -201,20 +201,17 @@ class { 'mysql::server': * => $ed25519_opts }
   end
   context 'using user-w-subject@localhost with ISSUER and SUBJECT' do
     describe 'adding user' do
-      pp_six = <<-MANIFEST
-          mysql_user { 'user-w-subject@localhost':
-            plugin        => 'mysql_native_password',
-            password_hash => '',
-            tls_options   => [
-              "SUBJECT '/OU=MySQL Users/CN=username'",
-              "ISSUER '/CN=Certificate Authority'",
-              "CIPHER 'EDH-RSA-DES-CBC3-SHA'",
-            ],
-          }
-      MANIFEST
-
       it 'works without errors' do
-        idempotent_apply(pp_six)
+        pp = <<-MANIFEST
+        mysql_user { 'user-w-subject@localhost':
+          tls_options   => [
+            "SUBJECT '/OU=MySQL Users/CN=username'",
+            "ISSUER '/CN=Certificate Authority'",
+            "CIPHER 'EDH-RSA-DES-CBC3-SHA'",
+          ],
+        }
+        MANIFEST
+        idempotent_apply(pp)
       end
 
       it 'finds the user #stdout' do