ruby-mysql 2.10.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '03296aa0e624e599242e8efc5d3011b4337e161c3b7bf1c9cc81e98fa99f5052'
4
- data.tar.gz: c7391d48dab125e86b97b8130556e69ceadf503e11884744b7fdf19af3b58cf1
3
+ metadata.gz: 40bdb4e1eba4eca2ed1e93524260bd9407f91ee5f65b8c32b8c38524ef05ebdd
4
+ data.tar.gz: 224ad0bdf2050dca6e5d7a603bd64cb44a5f414270aa06b9dd89690cd2665a32
5
5
  SHA512:
6
- metadata.gz: ba1a7bad362dc1523de97aa487ee8c571649854a2bb8a9342ecd525dbda0441c9562639556dcd933f7e7c643373907144329e97c2600af30a5ae6b1cc5fb5606
7
- data.tar.gz: bdea4b3d97980fea097d59525b327c275374d006ddb699efda0c359608d8588a8beba742f713f0b2721433ea5efbf9eac5db8d7f4e6d7ddbe9eeb050220e6a3d
6
+ metadata.gz: a73e1ab248a44935cd4115c0d2de16f491d51b2eb7ab7a9b33185a0f478be8753cc226ea78d645a2bb1ae0b09cb15a7432f9e8d5a1dfa82af3ef016783689544
7
+ data.tar.gz: 37db0a8b13b5a543cf9d552801aa0419c4ccce9fbe287d8df0335002df728e5e5ab5a9d82c873c174dec547b1077730cd265a4a6ace1714c88cb984b3d5f4134
data/README.rdoc CHANGED
@@ -20,8 +20,6 @@ MySQL connector for Ruby.
20
20
 
21
21
  * MySQL/Ruby 2.8.x とほぼ互換があります。
22
22
 
23
- * MySQL 8.0 には対応していません。
24
-
25
23
  == Synopsis
26
24
 
27
25
  使用例:
@@ -35,17 +33,17 @@ MySQL connector for Ruby.
35
33
 
36
34
  == Incompatible with MySQL/Ruby 2.8.x
37
35
 
38
- * Ruby 1.8.x ではシフトJISのような安全でないマルチバイト文字セットに対して Mysql#escape_string を使用すると例外が発生します。
39
-
40
36
  * いくつかのメソッドがありません: Mysql#debug, Mysql#change_user,
41
37
  Mysql#create_db, Mysql#drop_db, Mysql#dump_debug_info,
42
38
  Mysql#ssl_set, Mysql#reconnect
43
39
 
44
40
  * Mysql#options でサポートしているオプションは次のものだけです:
45
41
  Mysql::INIT_COMMAND, Mysql::OPT_CONNECT_TIMEOUT,
42
+ Mysql::OPT_GET_SERVER_PUBLIC_KEY,
43
+ Mysql::OPT_LOAD_DATA_LOCAL_DIR,
46
44
  Mysql::OPT_LOCAL_INFILE, Mysql::OPT_READ_TIMEOUT,
47
- Mysql::OPT_WRITE_TIMEOUT, Mysql::SET_CHARSET_NAME,
48
- Mysql::OPT_LOAD_DATA_LOCAL_DIR.
45
+ Mysql::OPT_SSL_MODE,
46
+ Mysql::OPT_WRITE_TIMEOUT, Mysql::SET_CHARSET_NAME.
49
47
  これら以外を指定すると "option not implementted" という warning が標準エラー出力に出力されます。
50
48
 
51
49
  * Mysql#use_result は Mysql#store_result と同じです。つまりサーバーから一気に結果セットを読み込みます。
@@ -0,0 +1,62 @@
1
+ require 'digest/sha2'
2
+
3
+ class Mysql
4
+ class Authenticator
5
+ class CachingSha2Password
6
+ # @param protocol [Mysql::Protocol]
7
+ def initialize(protocol)
8
+ @protocol = protocol
9
+ end
10
+
11
+ # @return [String]
12
+ def name
13
+ 'caching_sha2_password'
14
+ end
15
+
16
+ # @param passwd [String]
17
+ # @param scramble [String]
18
+ # @yield [String] hashed password
19
+ # @return [Mysql::Packet]
20
+ def authenticate(passwd, scramble)
21
+ yield hash_password(passwd, scramble)
22
+ pkt = @protocol.read
23
+ data = pkt.to_s
24
+ if data.size == 2 && data[0] == "\x01"
25
+ case data[1]
26
+ when "\x03" # fast_auth_success
27
+ # OK
28
+ when "\x04" # perform_full_authentication
29
+ if @protocol.client_flags & CLIENT_SSL != 0
30
+ @protocol.write passwd+"\0"
31
+ elsif !@protocol.get_server_public_key
32
+ raise 'Authentication requires secure connection'
33
+ else
34
+ @protocol.write "\2" # request public key
35
+ pkt = @protocol.read
36
+ pkt.utiny # skip
37
+ pubkey = pkt.to_s
38
+ hash = (passwd+"\0").unpack("C*").zip(scramble.unpack("C*")).map{|a, b| a ^ b}.pack("C*")
39
+ enc = OpenSSL::PKey::RSA.new(pubkey).public_encrypt(hash, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
40
+ @protocol.write enc
41
+ end
42
+ else
43
+ raise "invalid auth reply packet: #{data.inspect}"
44
+ end
45
+ pkt = @protocol.read
46
+ end
47
+ return pkt
48
+ end
49
+
50
+ # @param passwd [String]
51
+ # @param scramble [String]
52
+ # @return [String] hashed password
53
+ def hash_password(passwd, scramble)
54
+ return '' if passwd.nil? or passwd.empty?
55
+ hash1 = Digest::SHA256.digest(passwd)
56
+ hash2 = Digest::SHA256.digest(hash1)
57
+ hash3 = Digest::SHA256.digest(hash2 + scramble)
58
+ hash1.unpack("C*").zip(hash3.unpack("C*")).map{|a, b| a ^ b}.pack("C*")
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,37 @@
1
+ require 'digest/sha1'
2
+
3
+ class Mysql
4
+ class Authenticator
5
+ class MysqlNativePassword
6
+ # @param protocol [Mysql::Protocol]
7
+ def initialize(protocol)
8
+ @protocol = protocol
9
+ end
10
+
11
+ # @return [String]
12
+ def name
13
+ 'mysql_native_password'
14
+ end
15
+
16
+ # @param passwd [String]
17
+ # @param scramble [String]
18
+ # @yield [String] hashed password
19
+ # @return [Mysql::Packet]
20
+ def authenticate(passwd, scramble)
21
+ yield hash_password(passwd, scramble)
22
+ @protocol.read
23
+ end
24
+
25
+ # @param passwd [String]
26
+ # @param scramble [String]
27
+ # @return [String] hashed password
28
+ def hash_password(passwd, scramble)
29
+ return '' if passwd.nil? or passwd.empty?
30
+ hash1 = Digest::SHA1.digest(passwd)
31
+ hash2 = Digest::SHA1.digest(hash1)
32
+ hash3 = Digest::SHA1.digest(scramble + hash2)
33
+ hash1.unpack("C*").zip(hash3.unpack("C*")).map{|a, b| a ^ b}.pack("C*")
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,40 @@
1
+ require 'openssl'
2
+
3
+ class Mysql
4
+ class Authenticator
5
+ class Sha256Password
6
+ # @param protocol [Mysql::Protocol]
7
+ def initialize(protocol)
8
+ @protocol = protocol
9
+ end
10
+
11
+ # @return [String]
12
+ def name
13
+ 'sha256_password'
14
+ end
15
+
16
+ # @param passwd [String]
17
+ # @param scramble [String]
18
+ # @yield [String] hashed password
19
+ # @return [Mysql::Packet]
20
+ def authenticate(passwd, scramble)
21
+ if @protocol.client_flags & CLIENT_SSL != 0
22
+ yield passwd+"\0"
23
+ return @protocol.read
24
+ end
25
+ yield "\x01" # request public key
26
+ pkt = @protocol.read
27
+ data = pkt.to_s
28
+ if data[0] == "\x01"
29
+ pkt.utiny # skip
30
+ pubkey = pkt.to_s
31
+ hash = (passwd+"\0").unpack("C*").zip(scramble.unpack("C*")).map{|a, b| a ^ b}.pack("C*")
32
+ enc = OpenSSL::PKey::RSA.new(pubkey).public_encrypt(hash, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
33
+ @protocol.write enc
34
+ pkt = @protocol.read
35
+ end
36
+ return pkt
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,84 @@
1
+ class Mysql
2
+ class Authenticator
3
+ @plugins = {}
4
+
5
+ # @param plugin [String]
6
+ def self.plugin_class(plugin)
7
+ return @plugins[plugin] if @plugins[plugin]
8
+
9
+ raise ClientError, "invalid plugin name: #{plugin}" unless plugin.match?(/\A\w+\z/)
10
+ begin
11
+ require_relative "authenticator/#{plugin}"
12
+ rescue LoadError
13
+ return nil
14
+ end
15
+ class_name = plugin.gsub(/(?:^|_)(.)/){$1.upcase}
16
+ raise ClientError, "#{class_name} is undefined" unless self.const_defined? class_name
17
+ klass = self.const_get(class_name)
18
+ @plugins[plugin] = klass
19
+ return klass
20
+ end
21
+
22
+ def initialize(protocol)
23
+ @protocol = protocol
24
+ end
25
+
26
+ # @param plugin [String]
27
+ def get(plugin)
28
+ self.class.plugin_class(plugin)
29
+ end
30
+
31
+ # @param plugin [String]
32
+ def get!(plugin)
33
+ get(plugin) or raise ClientError, "unknown plugin: #{plugin}"
34
+ end
35
+
36
+ def authenticate(user, passwd, db, scramble, plugin_name)
37
+ plugin = (get(plugin_name) || DummyPlugin).new(@protocol)
38
+ pkt = plugin.authenticate(passwd, scramble) do |hashed|
39
+ @protocol.write Protocol::AuthenticationPacket.serialize(@protocol.client_flags, 1024**3, @protocol.charset.number, user, hashed, db, plugin.name)
40
+ end
41
+ while true
42
+ res = Protocol::AuthenticationResultPacket.parse(pkt)
43
+ case res.result
44
+ when 0 # OK
45
+ break
46
+ when 2 # multi factor auth
47
+ raise ClientError, 'multi factor authentication is not supported'
48
+ when 254 # change auth plugin
49
+ plugin = get!(res.auth_plugin).new(@protocol)
50
+ pkt = plugin.authenticate(passwd, res.scramble) do |hashed|
51
+ if passwd.nil? || passwd.empty?
52
+ @protocol.write "\0"
53
+ else
54
+ @protocol.write hashed
55
+ end
56
+ end
57
+ else
58
+ raise ClientError, "invalid packet: #{pkt.to_s}"
59
+ end
60
+ end
61
+ end
62
+
63
+ class DummyPlugin
64
+ # @param protocol [Mysql::Protocol]
65
+ def initialize(protocol)
66
+ @protocol = protocol
67
+ end
68
+
69
+ # @return [String]
70
+ def name
71
+ ''
72
+ end
73
+
74
+ # @param passwd [String]
75
+ # @param scramble [String]
76
+ # @yield [String] hashed password
77
+ # @return [Mysql::Packet]
78
+ def authenticate(passwd, scramble)
79
+ yield ''
80
+ @protocol.read
81
+ end
82
+ end
83
+ end
84
+ end