Skip to content

Commit

Permalink
Enabling support for the caching_sha2_password auth plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
fulghum committed Dec 9, 2024
1 parent d644619 commit fa42af6
Show file tree
Hide file tree
Showing 5 changed files with 410 additions and 51 deletions.
64 changes: 43 additions & 21 deletions enginetest/enginetests.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package enginetest

import (
"context"
"crypto/tls"
dsql "database/sql"
"fmt"
"io"
Expand Down Expand Up @@ -2010,11 +2011,19 @@ func TestUserAuthentication(t *testing.T, h Harness) {
User: "root",
Address: "localhost",
})

tlsCert, err := tls.LoadX509KeyPair("./testdata/selfsigned_cert.pem", "./testdata/selfsigned_key.pem")
require.NoError(t, err)
tlsConfig := tls.Config{
Certificates: []tls.Certificate{tlsCert},
}

serverConfig := server.Config{
Protocol: "tcp",
Address: fmt.Sprintf("localhost:%d", port),
MaxConnections: 1000,
AllowClearTextWithoutTLS: true,
Protocol: "tcp",
Address: fmt.Sprintf("localhost:%d", port),
MaxConnections: 1000,
TLSConfig: &tlsConfig,
RequireSecureTransport: true,
}

e := mustNewEngine(t, clientHarness)
Expand Down Expand Up @@ -2055,24 +2064,37 @@ func TestUserAuthentication(t *testing.T, h Harness) {
}()

for _, assertion := range script.Assertions {
conn, err := dbr.Open("mysql", fmt.Sprintf("%s:%s@tcp(localhost:%d)/?allowCleartextPasswords=true",
assertion.Username, assertion.Password, port), nil)
require.NoError(t, err)
r, err := conn.Query(assertion.Query)
if assertion.ExpectedErr || len(assertion.ExpectedErrStr) > 0 || assertion.ExpectedErrKind != nil {
if !assert.Error(t, err) {
require.NoError(t, r.Close())
} else if len(assertion.ExpectedErrStr) > 0 {
assert.Equal(t, assertion.ExpectedErrStr, err.Error())
} else if assertion.ExpectedErrKind != nil {
assert.True(t, assertion.ExpectedErrKind.Is(err))
}
} else {
if assert.NoError(t, err) {
require.NoError(t, r.Close())
t.Run(assertion.Query, func(t *testing.T) {
conn, err := dbr.Open("mysql", fmt.Sprintf("%s:%s@tcp(localhost:%d)/?allowCleartextPasswords=true&tls=skip-verify",
assertion.Username, assertion.Password, port), nil)
require.NoError(t, err)
r, err := conn.Query(assertion.Query)
if assertion.ExpectedErr || len(assertion.ExpectedErrStr) > 0 || assertion.ExpectedErrKind != nil {
if !assert.Error(t, err) {
require.NoError(t, r.Close())
} else if len(assertion.ExpectedErrStr) > 0 {
assert.Equal(t, assertion.ExpectedErrStr, err.Error())
} else if assertion.ExpectedErrKind != nil {
assert.True(t, assertion.ExpectedErrKind.Is(err))
}
} else {
if assert.NoError(t, err) {
require.NoError(t, r.Close())
}
if assertion.ExpectedAuthPlugin != "" {
// NOTE: This query works as long as there is only one account configured for the current user
r, err := conn.Query("SELECT plugin FROM mysql.user WHERE user=SUBSTRING_INDEX(USER(),'@',1);")
require.NoError(t, err)
require.True(t, r.Next())
var authPlugin string
err = r.Scan(&authPlugin)
require.False(t, r.Next())
require.NoError(t, err)
require.Equal(t, assertion.ExpectedAuthPlugin, authPlugin)
}
}
}
require.NoError(t, conn.Close())
require.NoError(t, conn.Close())
})
}
})
}
Expand Down
187 changes: 159 additions & 28 deletions enginetest/queries/priv_auth_queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,13 @@ type ServerAuthenticationTest struct {

// ServerAuthenticationTestAssertion is within a ServerAuthenticationTest to assert functionality.
type ServerAuthenticationTestAssertion struct {
Username string
Password string
Query string
ExpectedErr bool
ExpectedErrKind *errors.Kind
ExpectedErrStr string
Username string
Password string
Query string
ExpectedErr bool
ExpectedErrKind *errors.Kind
ExpectedErrStr string
ExpectedAuthPlugin string
}

// UserPrivTests test the user and privilege systems. These tests always have the root account available, and the root
Expand Down Expand Up @@ -740,7 +741,37 @@ var UserPrivTests = []UserPrivilegeTest{
},
},
},

{
Name: "Migrate a user from mysql_native_password to caching_sha2_password",
SetUpScript: []string{
"CREATE USER testuser1@`127.0.0.1` identified with mysql_native_password by 'pass1';",
},
Assertions: []UserPrivilegeTestAssertion{
{
Query: "select user, host, plugin, authentication_string from mysql.user where user='testuser1';",
Expected: []sql.Row{{"testuser1", "127.0.0.1", "mysql_native_password", "*22A99BA288DB55E8E230679259740873101CD636"}},
},
{
Query: "ALTER USER testuser1@`127.0.0.1` IDENTIFIED WITH caching_sha2_password BY 'pass1';",
Expected: []sql.Row{{types.NewOkResult(0)}},
},
{
// caching_sha2_password auth uses a random salt to create the authentication
// string. Since it's not a consistent value during each test run, we just sanity
// check the first bytes of metadata (digest type, iterations) in the auth string.
Query: "select user, host, plugin, authentication_string like '$A$005$%' from mysql.user where user='testuser1';",
Expected: []sql.Row{{"testuser1", "127.0.0.1", "caching_sha2_password", true}},
},
{
Query: "ALTER USER testuser1@`127.0.0.1` IDENTIFIED WITH caching_sha2_password;",
Expected: []sql.Row{{types.NewOkResult(0)}},
},
{
Query: "select user, host, plugin, authentication_string from mysql.user where user='testuser1';",
Expected: []sql.Row{{"testuser1", "127.0.0.1", "caching_sha2_password", ""}},
},
},
},
{
Name: "Dynamic privilege support",
SetUpScript: []string{
Expand Down Expand Up @@ -2604,10 +2635,10 @@ var ServerAuthTests = []ServerAuthenticationTest{
},
Assertions: []ServerAuthenticationTestAssertion{
{
Username: "rand_user",
Password: "rand_pass",
Query: "SELECT * FROM mysql.user;",
ExpectedErr: false,
Username: "rand_user",
Password: "rand_pass",
Query: "SELECT * FROM mysql.user;",
ExpectedAuthPlugin: "mysql_native_password",
},
{
Username: "rand_user",
Expand All @@ -2630,17 +2661,17 @@ var ServerAuthTests = []ServerAuthenticationTest{
},
},
{
Name: "Create User with plugin specification",
Name: "Create User explicitly with mysql_native_password plugin",
SetUpScript: []string{
"CREATE USER ranuse@localhost IDENTIFIED WITH mysql_native_password BY 'ranpas';",
"GRANT ALL ON *.* TO ranuse@localhost WITH GRANT OPTION;",
},
Assertions: []ServerAuthenticationTestAssertion{
{
Username: "ranuse",
Password: "ranpas",
Query: "SELECT * FROM mysql.user;",
ExpectedErr: false,
Username: "ranuse",
Password: "ranpas",
Query: "SELECT * FROM mysql.user;",
ExpectedAuthPlugin: "mysql_native_password",
},
{
Username: "ranuse",
Expand All @@ -2656,6 +2687,103 @@ var ServerAuthTests = []ServerAuthenticationTest{
},
},
},
{
Name: "Create User explicitly with caching_sha2_password plugin",
SetUpScript: []string{
// testuser1 is created with a password
"CREATE USER testuser1@localhost IDENTIFIED WITH caching_sha2_password BY 'mypassword3';",
"GRANT ALL ON *.* TO testuser1@localhost WITH GRANT OPTION;",
// testuser2 is created without a password
"CREATE USER testuser2@localhost IDENTIFIED WITH caching_sha2_password;",
"GRANT ALL ON *.* TO testuser2@localhost WITH GRANT OPTION;",
},
Assertions: []ServerAuthenticationTestAssertion{
{
Username: "testuser1",
Password: "mypassword3",
Query: "SELECT * FROM mysql.user;",
ExpectedAuthPlugin: "caching_sha2_password",
},
{
Username: "testuser1",
Password: "what",
Query: "SELECT * FROM mysql.user;",
ExpectedErr: true,
ExpectedErrStr: "Error 1045 (28000): Access denied for user 'testuser1'",
},
{
Username: "testuser1",
Password: "",
Query: "SELECT * FROM mysql.user;",
ExpectedErr: true,
ExpectedErrStr: "Error 1045 (28000): Access denied for user 'testuser1'",
},
{
Username: "testuser2",
Password: "wrong",
Query: "SELECT * FROM mysql.user;",
ExpectedErr: true,
ExpectedErrStr: "Error 1045 (28000): Access denied for user 'testuser2'",
},
{
Username: "testuser2",
Password: "",
Query: "SELECT * FROM mysql.user;",
ExpectedErr: false,
ExpectedAuthPlugin: "caching_sha2_password",
},
},
},
{
Name: "Migrate user from mysql_native_password to caching_sha2_password",
SetUpScript: []string{
// testuser1 is created with a password
"CREATE USER testuser1@localhost IDENTIFIED WITH mysql_native_password BY 'mypassword3';",
"GRANT ALL ON *.* TO testuser1@localhost WITH GRANT OPTION;",
},
Assertions: []ServerAuthenticationTestAssertion{
{
Username: "testuser1",
Password: "mypassword3",
Query: "SELECT * FROM mysql.user;",
ExpectedAuthPlugin: "mysql_native_password",
},
{
Username: "root",
Query: "ALTER USER testuser1@localhost IDENTIFIED WITH caching_sha2_password BY 'pass1';",
},
{
Username: "testuser1",
Password: "pass1",
Query: "SELECT * FROM mysql.user;",
ExpectedAuthPlugin: "caching_sha2_password",
},
{
Username: "testuser1",
Password: "wrong",
Query: "SELECT * FROM mysql.user;",
ExpectedErr: true,
ExpectedErrStr: "Error 1045 (28000): Access denied for user 'testuser1'",
},
{
Username: "root",
Query: "ALTER USER testuser1@localhost IDENTIFIED WITH caching_sha2_password;",
},
{
Username: "testuser1",
Password: "",
Query: "SELECT * FROM mysql.user;",
ExpectedAuthPlugin: "caching_sha2_password",
},
{
Username: "testuser1",
Password: "wrong",
Query: "SELECT * FROM mysql.user;",
ExpectedErr: true,
ExpectedErrStr: "Error 1045 (28000): Access denied for user 'testuser1'",
},
},
},
{
Name: "Create User with jwt plugin specification",
SetUpScript: []string{
Expand All @@ -2668,22 +2796,25 @@ var ServerAuthTests = []ServerAuthenticationTest{
},
Assertions: []ServerAuthenticationTestAssertion{
{
Username: "test-user",
Password: "what",
Query: "SELECT * FROM mysql.user;",
ExpectedErr: true,
Username: "test-user",
Password: "what",
Query: "SELECT * FROM mysql.user;",
ExpectedErr: true,
ExpectedErrStr: "Error 1045 (28000): Access denied for user 'test-user'",
},
{
Username: "test-user",
Password: "",
Query: "SELECT * FROM mysql.user;",
ExpectedErr: true,
Username: "test-user",
Password: "",
Query: "SELECT * FROM mysql.user;",
ExpectedErr: true,
ExpectedErrStr: "Error 1045 (28000): Access denied for user 'test-user'",
},
{
Username: "test-user",
Password: "right-password",
Query: "SELECT * FROM mysql.user;",
ExpectedErr: false,

Username: "test-user",
Password: "right-password",
Query: "SELECT * FROM mysql.user;",
ExpectedAuthPlugin: "authentication_dolt_jwt",
},
},
},
Expand Down
27 changes: 27 additions & 0 deletions enginetest/testdata/selfsigned_cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIErDCCApQCCQCP9IKGyBYVUDANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQKDA1E
b2x0SHViLCBJbmMuMB4XDTIyMDcyMTE5NTUyN1oXDTI2MDcyMDE5NTUyN1owGDEW
MBQGA1UECgwNRG9sdEh1YiwgSW5jLjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
AgoCggIBAMRNMquL4n2bAMQmoedUFcT/zI42ThW5o9qxh9U8MhsDqKBet7JL7ruh
3FRj/yaU+3ax2lGgapcTnSJHWHPUX6MzUpqkwrLzKxqMrKQYnU2F96n3GOflllsB
8ISKBy0TIipLWKupndc893qk+j6HUQ9OrtFHwroZ6/Tg32h4LcJvhkwILb8EHZpI
fSSzi637msnXWqln1WYmX+v4ARUVVSwl5kkPGZ4PxMDQRX3ioqy2d27GbHYSo01K
tSScQvnFJeddu09l7hNTSFixRz+XU+F+jYna8xUtqHMawEpfGw8Gsu8sMV9PUfUx
6WrMb1tcyWEDvHwH6RUlhNa085eCRFznHLDZNxrgBwrslII9d9xW/JaRq43d4M0q
e7F+BazB3rImMo2QHFvtvq55N7mvp+LprMjoEneBboiz1I6M0rq7Sn58FxazgQvz
mCZmNYIrznx+S1mlJjxtC7xgDPZn5Z/68TsmT26j5UNLou7i/yb7XFdMVY6kAnoZ
BAf4+rAUEDV85OLz0kWZSJj+DdlPOl/gIhTgrgJev2Cc7THUTnuk+nGETG49DITS
ySWYLsMYFHweSTm3rG6sMyN7mdbRrUSM1bqbARm46mt89+0HGsFX1QLe3v4yda67
ic93J5mkfNPZ96K9Hth3SBbZt43DvB56ZqsFQlzBZeQ3hkk7ooulAgMBAAEwDQYJ
KoZIhvcNAQELBQADggIBAJgopHEYmETWhH06T72EpgLr3xqckCP9QZ6/UBN8eDt0
rRqMCfM5H33qpe2wojjKwFDkR8XpwF/80VflfFBt9hc1c1fuKmyQSES9gUw10uSL
Z54MCPOa+c8+hslkmJR8Na0QyWN5unnozVHf4XIChsgL1/FblXcOgQLPZygzMNM2
IjdT9XpEHiZTDZDp1NmM7rkRkcpgF2J8G2dcRjo/OGpnhH7wHgxm7hS6yWLW8xPP
8M2/8LPYA7H6HMGYeyrYuDPeVzfaHrECTft+4cjHLu/jYnVLukMMSuI9v5FjtxtX
PYtxnLv8hnbParjSnzK8cOlGdfJDRPMUw9/tvZ4bTeyTtJQYgl/jtdaU4mbdWlge
XMzkZGH3kKpsV2rPZXKJuqRq3vzfr51cQhEcnbJ5H8BsDUQADS3ind37guqoKhuZ
6vFUBTKLeYK5VZ4J18ztXhEynAf7kdROKP6XbE53qtH8qQujmOMWliSFQFdidYsj
eItzGQ4M/ZqI84UnPRL3WPdfPkWqa0x1k6PHFRcFJPp8nhl3O5V4ZdyVC3pKhzUJ
Y8sMit5RH1K8ZTYUVtEKwMX9wRMEkbfE4u/P+yItKw7QgYRdKerlDfCGZP8JY9+k
wqYmF56EVGQFaJdJ1ublVEHQkAVHOBowzccwWOV/OPi1sL+cf4RxAaY7gJpuNEIk
-----END CERTIFICATE-----
Loading

0 comments on commit fa42af6

Please sign in to comment.