ruby-mysql 2.9.14 → 3.0.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
- SHA1:
3
- metadata.gz: 113de8c9b46c75bf18b833a65205517938daf47d
4
- data.tar.gz: 675d64d8cc1185e7f394c4f201aa18f427b18f11
2
+ SHA256:
3
+ metadata.gz: f95fb29b93c207b23bbc1378ce78b10ab297269f0cf24409116a69502cdf9ae8
4
+ data.tar.gz: 7979ccfd21194e81a4c98f90920b7dc3cdb1165201bdf7bc0d640d16fbe8ebea
5
5
  SHA512:
6
- metadata.gz: 47b396bc40fa36e588d01899569f8b0cca6b256bb2d290fe2ce5ab44dc66a566c2b47d67f64cb8f4d2a8a0a62c352e878da681f88b89bc0a6811b12205754214
7
- data.tar.gz: 2aa55a288ce9c5ca4f22be8b8a2c6750be23592a0e030b0077f622b43195691f81de20831a359098ce8a270050e3ffa4ab794439b039c267d803f0d23ad2330e
6
+ metadata.gz: '068b6f6008f4b77dc6d34b660c8f06b73db2c06e63fc98d69d5455ceb6ab61c3924d158de4398d6b897cc2fa44d460a879b60355dbb16ac90f0af666f750467d'
7
+ data.tar.gz: 769105fbd5f16a4b8ab9c55c4a1243c962db3a238f05baa16d970cb2cbaadba0f0487c2daf194b53ce0bfa48639aac37cb79527da02518607a4a0a861cdd6caf
data/CHANGELOG.md ADDED
@@ -0,0 +1,50 @@
1
+ ## [3.0.0] - 2021-11-16
2
+
3
+ - `Mysql.new` no longer connect. use `Mysql.connect` or `Mysql#connect`.
4
+
5
+ - `Mysql.init` is removed. use `Mysql.new` instead.
6
+
7
+ - `Mysql.new`, `Mysql.conncet` and `Mysql#connect` takes URI object or URI string or Hash object.
8
+ example:
9
+ Mysql.connect('mysql://user:password@hostname:port/dbname?charset=ascii')
10
+ Mysql.connect('mysql://user:password@%2Ftmp%2Fmysql.sock/dbname?charset=ascii') # for UNIX socket
11
+ Mysql.connect('hostname', 'user', 'password', 'dbname')
12
+ Mysql.connect(host: 'hostname', username: 'user', password: 'password', database: 'dbname')
13
+
14
+ - `Mysql.options` is removed. use `Mysql#param = value` instead.
15
+ For example:
16
+ m = Mysql.init
17
+ m.options(Mysql::OPT_LOCAL_INFILE, true)
18
+ m.connect(host, user, passwd)
19
+ change to
20
+ m = Mysql.new
21
+ m.local_infile = true
22
+ m.connect(host, user, passwd)
23
+ or
24
+ m = Mysql.connect(host, user, passwd, local_infile: true)
25
+
26
+ - `Mysql::Time` is removed.
27
+ Instead, `Time` object is returned for the DATE, DATETIME, TIMESTAMP data,
28
+ and `Integer` object is returned for the TIME data.
29
+ If DATE, DATETIME, TIMESTAMP are invalid values for Time, nil is returned.
30
+
31
+ - meaningless methods are removed:
32
+ * `bind_result`
33
+ * `client_info`
34
+ * `client_version`
35
+ * `get_proto_info`
36
+ * `get_server_info`
37
+ * `get_server_version`
38
+ * `proto_info`
39
+ * `query_with_result`
40
+
41
+ - alias method are removed:
42
+ * `get_host_info`: use `host_info`
43
+ * `real_connect`: use `connect`
44
+ * `real_query`: use `query`
45
+
46
+ - methods corresponding to deprecated APIs in MySQL are removed:
47
+ * `list_dbs`: use `SHOW DATABASES`
48
+ * `list_fields`: use `SHOW COLUMNS`
49
+ * `list_processes`: use `SHOW PROCESSLIST`
50
+ * `list_tables`: use `SHOW TABLES`
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # ruby-mysql
2
+
3
+ ## Description
4
+
5
+ MySQL connector for Ruby.
6
+
7
+ ## Installation
8
+
9
+ ```
10
+ gem install ruby-mysql
11
+ ```
12
+
13
+ ## Synopsis
14
+
15
+ ```ruby
16
+ my = Mysql.connect('mysql://username:password@hostname:port/dbname?charset=utf8mb4')
17
+ my.query("select col1, col2 from tblname").each do |col1, col2|
18
+ p col1, col2
19
+ end
20
+ stmt = my.prepare('insert into tblname (col1,col2) values (?,?)')
21
+ stmt.execute 123, 'abc'
22
+ ```
23
+
24
+ ## Copyright
25
+
26
+ * Author: TOMITA Masahiro <tommy@tmtm.org>
27
+ * Copyright: Copyright 2008 TOMITA Masahiro
28
+ * License: MIT
@@ -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