Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion jobs/pxc-mysql/templates/db_init.erb
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ ALTER USER #{user['username']}@#{user['host']}

def grant_admin_privs(user)
%{
#{revoke_all_privileges(user)}
GRANT ALL PRIVILEGES ON *.* TO #{user['username']}@#{user['host']} WITH GRANT OPTION;
GRANT PROXY ON ''@'' TO #{user['username']}@#{user['host']} WITH GRANT OPTION;
}.strip
Expand Down Expand Up @@ -123,7 +124,8 @@ GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO #{user['username']}@#{user['

def revoke_all_privileges(user)
%{
REVOKE ALL PRIVILEGES ON *.* FROM #{user['username']}@#{user['host']};
REVOKE ALL PRIVILEGES, GRANT OPTION FROM #{user['username']}@#{user['host']};
REVOKE IF EXISTS PROXY ON ''@'' FROM #{user['username']}@#{user['host']};
}.strip
end

Expand Down
4 changes: 0 additions & 4 deletions spec/integration/internal/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,6 @@ func RunContainer(spec ContainerSpec) string {
Expect(err).NotTo(HaveOccurred(),
`Failed to create docker container: %s`, err)

DeferCleanup(func() {
_ = RemoveContainer(containerID)
})

StartContainer(containerID)

return containerID
Expand Down
101 changes: 93 additions & 8 deletions spec/integration/user_management_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ var _ = Describe("UserManagement", Ordered, func() {
dbUsers = MySQLJobSpec{}
})

JustBeforeEach(func() {
doc, err := json.Marshal(dbUsers)
renderDBInit := func(spec MySQLJobSpec) string {
GinkgoHelper()

doc, err := json.Marshal(spec)
Expect(err).NotTo(HaveOccurred())

f, err := os.CreateTemp("", "db_init_")
Expand All @@ -64,20 +66,26 @@ var _ = Describe("UserManagement", Ordered, func() {
GinkgoWriter.Println("$ ./scripts/render-db_init")
Expect(cmd.Run()).To(Succeed())

return f.Name()
}

applyDBInit := func(tag, initFilePath string) string {
GinkgoHelper()
return startMySQL(tag, []string{"--init-file=/db_init"}, []string{initFilePath + ":/db_init"})
}

JustBeforeEach(func() {
initFilePath := renderDBInit(dbUsers)

// Initialize the data volume first, so our db_init does not interfere with percona's entrypoint bootstrapping
resource = startMySQL(mysqlVersionTag, nil, nil)
Expect(docker.RemoveContainer(resource)).To(Succeed())

resource = startMySQL(
mysqlVersionTag,
[]string{"--init-file=/db_init"},
[]string{f.Name() + ":/db_init"},
)
resource = applyDBInit(mysqlVersionTag, initFilePath)
DeferCleanup(func() {
if CurrentSpecReport().Failed() {
return
}

Expect(docker.RemoveContainer(resource)).To(Succeed())
Expect(docker.RemoveVolume(volumeID)).To(Succeed())
})
Expand Down Expand Up @@ -367,4 +375,81 @@ var _ = Describe("UserManagement", Ordered, func() {
verifyMaxUserConnections("mysql-backup", "localhost", 0)
})
})

When("a seeded user's role is downgraded across redeploys", func() {
var (
testUser = "role-downgrade-user"
initialPassword string
)

BeforeEach(func() {
initialPassword = uuid.NewString()
dbUsers = MySQLJobSpec{
SeededUsers: map[string]UserRole{
testUser: {
Role: "admin",
Password: initialPassword,
Host: "any",
},
},
}
})

It("removes the PROXY privilege when downgraded from admin to minimal", func() {
// First deploy: verify the admin user has a PROXY grant.
By("verifying the admin user has a PROXY grant after initial deploy")
db := docker.MySQLDB(resource)
db.SetMaxIdleConns(0)
Expect(showGrants(db, testUser, "%")).To(
ContainElement(ContainSubstring("PROXY ON")),
)
Expect(db.Close()).To(Succeed())

// Second deploy: downgrade the same user@host to minimal role.
By("redeploying with the user downgraded to minimal role")
Expect(docker.RemoveContainer(resource)).To(Succeed())
resource = applyDBInit(mysqlVersionTag, renderDBInit(MySQLJobSpec{
SeededUsers: map[string]UserRole{
testUser: {
Role: "minimal",
Password: uuid.NewString(),
Host: "any",
},
},
}))

db = docker.MySQLDB(resource)
defer func() { _ = db.Close() }()
// After downgrade the user should hold only the baseline USAGE grant —
// no PROXY, no data privileges, no GRANT OPTION.
By("verifying no PROXY grant remains after downgrade")
Expect(showGrants(db, testUser, "%")).To(ConsistOf(
"GRANT USAGE ON *.* TO `role-downgrade-user`@`%`",
))
})

It("removing a non-existent PROXY grant is idempotent (minimal -> minimal)", func() {
// First deploy is already minimal here because BeforeEach configures admin,
// but we override to minimal to prove the PROXY revoke SQL does not error
// when the grant was never issued.
By("applying minimal role on a user that never had admin")
Expect(docker.RemoveContainer(resource)).To(Succeed())
resource = applyDBInit(mysqlVersionTag, renderDBInit(MySQLJobSpec{
SeededUsers: map[string]UserRole{
testUser: {
Role: "minimal",
Password: uuid.NewString(),
Host: "any",
},
},
}))

By("verifying the user has only USAGE with no PROXY")
db := docker.MySQLDB(resource)
defer func() { _ = db.Close() }()
Expect(showGrants(db, testUser, "%")).To(ConsistOf(
"GRANT USAGE ON *.* TO `role-downgrade-user`@`%`",
))
})
})
})
14 changes: 10 additions & 4 deletions spec/pxc-mysql/golden/db_init_all_features
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ CREATE USER IF NOT EXISTS 'basic-user'@'localhost'
ALTER USER 'basic-user'@'localhost'
IDENTIFIED WITH caching_sha2_password BY 'secret-basic-user-db-pw'
/*!80001 ATTRIBUTE '{ "pxc-release-seeded-user": true }'*/;
REVOKE ALL PRIVILEGES ON *.* FROM 'basic-user'@'localhost';
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'basic-user'@'localhost';
REVOKE IF EXISTS PROXY ON ''@'' FROM 'basic-user'@'localhost';

-- user: 'cloud_controller'@'%' role: schema-admin
CREATE USER IF NOT EXISTS 'cloud_controller'@'%'
Expand All @@ -46,7 +47,8 @@ CREATE USER IF NOT EXISTS 'cloud_controller'@'%'
ALTER USER 'cloud_controller'@'%'
IDENTIFIED WITH caching_sha2_password BY 'secret-ccdb-pw'
/*!80001 ATTRIBUTE '{ "pxc-release-seeded-user": true }'*/;
REVOKE ALL PRIVILEGES ON *.* FROM 'cloud_controller'@'%';
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'cloud_controller'@'%';
REVOKE IF EXISTS PROXY ON ''@'' FROM 'cloud_controller'@'%';
GRANT ALL PRIVILEGES ON `cloud\_controller`.* TO 'cloud_controller'@'%';
REVOKE LOCK TABLES ON `cloud\_controller`.* FROM 'cloud_controller'@'%';
SELECT COUNT(*) INTO @_schema_exists FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = 'cloud_controller';
Expand All @@ -62,7 +64,8 @@ CREATE USER IF NOT EXISTS 'multi-schema-admin-user'@'%'
ALTER USER 'multi-schema-admin-user'@'%'
IDENTIFIED WITH caching_sha2_password BY 'secret-multi-schema-admin-db-pw'
/*!80001 ATTRIBUTE '{ "pxc-release-seeded-user": true }'*/;
REVOKE ALL PRIVILEGES ON *.* FROM 'multi-schema-admin-user'@'%';
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'multi-schema-admin-user'@'%';
REVOKE IF EXISTS PROXY ON ''@'' FROM 'multi-schema-admin-user'@'%';
GRANT ALL PRIVILEGES ON `multi_schemas_%`.* TO 'multi-schema-admin-user'@'%';
REVOKE LOCK TABLES ON `multi_schemas_%`.* FROM 'multi-schema-admin-user'@'%';

Expand All @@ -73,7 +76,8 @@ CREATE USER IF NOT EXISTS 'mysql-metrics'@'%'
ALTER USER 'mysql-metrics'@'%'
IDENTIFIED WITH caching_sha2_password BY 'secret-mysql-metrics-db-pw'
WITH MAX_USER_CONNECTIONS 3/*!80001 ATTRIBUTE '{ "pxc-release-seeded-user": true }'*/;
REVOKE ALL PRIVILEGES ON *.* FROM 'mysql-metrics'@'%';
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'mysql-metrics'@'%';
REVOKE IF EXISTS PROXY ON ''@'' FROM 'mysql-metrics'@'%';
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'mysql-metrics'@'%';
SELECT COUNT(*) INTO @_schema_exists FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = 'metrics_db';
SET @_sql = IF(@_schema_exists, 'DO 1', 'CREATE SCHEMA `metrics_db` CHARACTER SET ''utf8mb4''');
Expand All @@ -88,6 +92,8 @@ CREATE USER IF NOT EXISTS 'special-admin-user'@'%'
ALTER USER 'special-admin-user'@'%'
IDENTIFIED WITH caching_sha2_password BY 'secret-seeded-admin-pw'
/*!80001 ATTRIBUTE '{ "pxc-release-seeded-user": true }'*/;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'special-admin-user'@'%';
REVOKE IF EXISTS PROXY ON ''@'' FROM 'special-admin-user'@'%';
GRANT ALL PRIVILEGES ON *.* TO 'special-admin-user'@'%' WITH GRANT OPTION;
GRANT PROXY ON ''@'' TO 'special-admin-user'@'%' WITH GRANT OPTION;

Expand Down
14 changes: 10 additions & 4 deletions spec/pxc-mysql/golden/db_init_no_mysqlbackup
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ CREATE USER IF NOT EXISTS 'basic-user'@'localhost'
ALTER USER 'basic-user'@'localhost'
IDENTIFIED WITH caching_sha2_password BY 'secret-basic-user-db-pw'
/*!80001 ATTRIBUTE '{ "pxc-release-seeded-user": true }'*/;
REVOKE ALL PRIVILEGES ON *.* FROM 'basic-user'@'localhost';
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'basic-user'@'localhost';
REVOKE IF EXISTS PROXY ON ''@'' FROM 'basic-user'@'localhost';

-- user: 'cloud_controller'@'%' role: schema-admin
CREATE USER IF NOT EXISTS 'cloud_controller'@'%'
Expand All @@ -40,7 +41,8 @@ CREATE USER IF NOT EXISTS 'cloud_controller'@'%'
ALTER USER 'cloud_controller'@'%'
IDENTIFIED WITH caching_sha2_password BY 'secret-ccdb-pw'
/*!80001 ATTRIBUTE '{ "pxc-release-seeded-user": true }'*/;
REVOKE ALL PRIVILEGES ON *.* FROM 'cloud_controller'@'%';
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'cloud_controller'@'%';
REVOKE IF EXISTS PROXY ON ''@'' FROM 'cloud_controller'@'%';
GRANT ALL PRIVILEGES ON `cloud\_controller`.* TO 'cloud_controller'@'%';
REVOKE LOCK TABLES ON `cloud\_controller`.* FROM 'cloud_controller'@'%';
SELECT COUNT(*) INTO @_schema_exists FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = 'cloud_controller';
Expand All @@ -56,7 +58,8 @@ CREATE USER IF NOT EXISTS 'multi-schema-admin-user'@'%'
ALTER USER 'multi-schema-admin-user'@'%'
IDENTIFIED WITH caching_sha2_password BY 'secret-multi-schema-admin-db-pw'
/*!80001 ATTRIBUTE '{ "pxc-release-seeded-user": true }'*/;
REVOKE ALL PRIVILEGES ON *.* FROM 'multi-schema-admin-user'@'%';
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'multi-schema-admin-user'@'%';
REVOKE IF EXISTS PROXY ON ''@'' FROM 'multi-schema-admin-user'@'%';
GRANT ALL PRIVILEGES ON `multi_schemas_%`.* TO 'multi-schema-admin-user'@'%';
REVOKE LOCK TABLES ON `multi_schemas_%`.* FROM 'multi-schema-admin-user'@'%';

Expand All @@ -67,7 +70,8 @@ CREATE USER IF NOT EXISTS 'mysql-metrics'@'%'
ALTER USER 'mysql-metrics'@'%'
IDENTIFIED WITH caching_sha2_password BY 'secret-mysql-metrics-db-pw'
WITH MAX_USER_CONNECTIONS 3/*!80001 ATTRIBUTE '{ "pxc-release-seeded-user": true }'*/;
REVOKE ALL PRIVILEGES ON *.* FROM 'mysql-metrics'@'%';
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'mysql-metrics'@'%';
REVOKE IF EXISTS PROXY ON ''@'' FROM 'mysql-metrics'@'%';
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'mysql-metrics'@'%';
SELECT COUNT(*) INTO @_schema_exists FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = 'metrics_db';
SET @_sql = IF(@_schema_exists, 'DO 1', 'CREATE SCHEMA `metrics_db` CHARACTER SET ''utf8mb4''');
Expand All @@ -82,6 +86,8 @@ CREATE USER IF NOT EXISTS 'special-admin-user'@'%'
ALTER USER 'special-admin-user'@'%'
IDENTIFIED WITH caching_sha2_password BY 'secret-seeded-admin-pw'
/*!80001 ATTRIBUTE '{ "pxc-release-seeded-user": true }'*/;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'special-admin-user'@'%';
REVOKE IF EXISTS PROXY ON ''@'' FROM 'special-admin-user'@'%';
GRANT ALL PRIVILEGES ON *.* TO 'special-admin-user'@'%' WITH GRANT OPTION;
GRANT PROXY ON ''@'' TO 'special-admin-user'@'%' WITH GRANT OPTION;

Expand Down
Loading