forked from yugabyte/yugabyte-db
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[YSQL] yugabyte#869 Add Role-based Authorization
Summary: This diff adds support for postgres roles, object ownership, and permissions, which makes up the majority of RBAC. The two remaining steps are to add support for customizing config files (e.g. `pg_hba.conf`), and to add row-level security. For the time being, roles can be used in the database to their full extent, however our default `pg_hba.conf` allows any connection from any host to connect as any role, without having to provide a password. To make roles practical, one must manually change the `pg_hba.conf` on all nodes to enforce more strict authentication methods (e.g. see the hba supplied in `TestPgAuthorization`). This can be done by either editing the default generated hba created by initdb, or by passing the relative file path to a custom hba file using the `pgsql_hba_conf_file` gflag in tests. Test Plan: Enabled the `roleattributes`, `rolenames`, `password`, `privileges`, and `init_privs` postgres regression tests with some minor modifications (and some unsupported features commented out). Added `TestPgRegressAuthorization.java` to test these pg regress tests. Added `TestPgAuthorization` with java tests, covering areas which were not tested sufficiently by the above regress tests (especially multi-node tests, and tests related to login/connection). Added `ClusterCleaner` interface, along with several implementations, as a faster and simpler way to clean up postgres between tests. Added `ConnectionBuilder` to simplify connection creation with many parameters. Reviewers: mihnea, neha Reviewed By: neha Subscribers: yql Differential Revision: https://phabricator.dev.yugabyte.com/D6776
- Loading branch information
Showing
55 changed files
with
9,681 additions
and
320 deletions.
There are no files selected for viewing
381 changes: 239 additions & 142 deletions
381
java/yb-pgsql/src/test/java/org/yb/pgsql/BasePgSQLTest.java
Large diffs are not rendered by default.
Oops, something went wrong.
3,271 changes: 3,271 additions & 0 deletions
3,271
java/yb-pgsql/src/test/java/org/yb/pgsql/TestPgAuthorization.java
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
java/yb-pgsql/src/test/java/org/yb/pgsql/TestPgRegressAuthorization.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// Copyright (c) YugaByte, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except | ||
// in compliance with the License. You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software distributed under the License | ||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express | ||
// or implied. See the License for the specific language governing permissions and limitations | ||
// under the License. | ||
// | ||
package org.yb.pgsql; | ||
|
||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
import org.yb.util.YBTestRunnerNonTsanOnly; | ||
|
||
/** | ||
* Runs the pg_regress authorization-related tests on YB code. | ||
*/ | ||
@RunWith(value = YBTestRunnerNonTsanOnly.class) | ||
public class TestPgRegressAuthorization extends BasePgSQLTest { | ||
@Override | ||
public int getTestMethodTimeoutSec() { | ||
return 1800; | ||
} | ||
|
||
@Test | ||
public void testPgRegressAuthorization() throws Exception { | ||
runPgRegressTest("yb_pg_auth_serial_schedule"); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
java/yb-pgsql/src/test/java/org/yb/pgsql/cleaners/ClusterCleaner.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// Copyright (c) YugaByte, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except | ||
// in compliance with the License. You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software distributed under the License | ||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express | ||
// or implied. See the License for the specific language governing permissions and limitations | ||
// under the License. | ||
// | ||
|
||
package org.yb.pgsql.cleaners; | ||
|
||
import java.sql.Connection; | ||
|
||
/** | ||
* Interface representing an object which can perform some post-test cleanup | ||
* on a postgres cluster. | ||
*/ | ||
public interface ClusterCleaner { | ||
void clean(Connection connection) throws Exception; | ||
} |
82 changes: 82 additions & 0 deletions
82
java/yb-pgsql/src/test/java/org/yb/pgsql/cleaners/ConnectionCleaner.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// Copyright (c) YugaByte, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except | ||
// in compliance with the License. You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software distributed under the License | ||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express | ||
// or implied. See the License for the specific language governing permissions and limitations | ||
// under the License. | ||
// | ||
|
||
package org.yb.pgsql.cleaners; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.sql.Connection; | ||
import java.sql.ResultSet; | ||
import java.sql.SQLException; | ||
import java.sql.Statement; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
/** | ||
* Closes all registered postgres connections, except the connection passed | ||
* to {@link ConnectionCleaner#clean(Connection)}. | ||
*/ | ||
public class ConnectionCleaner implements ClusterCleaner { | ||
private static final Logger LOG = LoggerFactory.getLogger(ConnectionCleaner.class); | ||
|
||
private static List<Connection> connectionsToClose = new ArrayList<>(); | ||
|
||
public static void register(Connection connection) { | ||
connectionsToClose.add(connection); | ||
} | ||
|
||
@Override | ||
public void clean(Connection rootConnection) throws Exception { | ||
LOG.info("Cleaning-up postgres connections"); | ||
|
||
if (rootConnection != null) { | ||
try (Statement statement = rootConnection.createStatement()) { | ||
try (ResultSet resultSet = statement.executeQuery( | ||
"SELECT client_hostname, client_port, state, query, pid FROM pg_stat_activity")) { | ||
while (resultSet.next()) { | ||
int backendPid = resultSet.getInt(5); | ||
LOG.info(String.format( | ||
"Found connection: hostname=%s, port=%s, state=%s, query=%s, backend_pid=%s", | ||
resultSet.getString(1), resultSet.getInt(2), | ||
resultSet.getString(3), resultSet.getString(4), backendPid)); | ||
} | ||
} | ||
} catch (SQLException e) { | ||
LOG.info("Exception when trying to list PostgreSQL connections", e); | ||
} | ||
|
||
LOG.info("Closing connections."); | ||
for (Connection connection : connectionsToClose) { | ||
// Keep the main connection alive between tests. | ||
if (connection == rootConnection) continue; | ||
|
||
try { | ||
if (connection == null) { | ||
LOG.error("connectionsToClose contains a null connection!"); | ||
} else { | ||
connection.close(); | ||
} | ||
} catch (SQLException ex) { | ||
LOG.error("Exception while trying to close connection"); | ||
throw ex; | ||
} | ||
} | ||
} else { | ||
LOG.info("Connection is already null, nothing to close"); | ||
} | ||
LOG.info("Finished closing connection."); | ||
|
||
connectionsToClose.clear(); | ||
} | ||
} |
57 changes: 57 additions & 0 deletions
57
java/yb-pgsql/src/test/java/org/yb/pgsql/cleaners/DatabaseCleaner.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// Copyright (c) YugaByte, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except | ||
// in compliance with the License. You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software distributed under the License | ||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express | ||
// or implied. See the License for the specific language governing permissions and limitations | ||
// under the License. | ||
// | ||
|
||
package org.yb.pgsql.cleaners; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.sql.Connection; | ||
import java.sql.ResultSet; | ||
import java.sql.Statement; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
/** | ||
* Removes all databases excluding `postgres`, `template1`, and `template2`. | ||
* Any lower-priority cleaners should only clean objects in one of the remaining | ||
* three databases, or cluster-wide objects (e.g. roles). | ||
* The passed connection must be open in one of the three databases listed above. | ||
*/ | ||
public class DatabaseCleaner implements ClusterCleaner { | ||
private static final Logger LOG = LoggerFactory.getLogger(DatabaseCleaner.class); | ||
|
||
@Override | ||
public void clean(Connection connection) throws Exception { | ||
LOG.info("Cleaning-up non-standard postgres databases"); | ||
|
||
try (Statement statement = connection.createStatement()) { | ||
statement.execute("RESET SESSION AUTHORIZATION"); | ||
|
||
ResultSet resultSet = statement.executeQuery( | ||
"SELECT datname FROM pg_database" + | ||
" WHERE datname <> 'template0'" + | ||
" AND datname <> 'template1'" + | ||
" AND datname <> 'postgres'"); | ||
|
||
List<String> databases = new ArrayList<>(); | ||
while (resultSet.next()) { | ||
databases.add(resultSet.getString(1)); | ||
} | ||
|
||
for (String database : databases) { | ||
statement.execute("DROP DATABASE " + database); | ||
} | ||
} | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
java/yb-pgsql/src/test/java/org/yb/pgsql/cleaners/RoleCleaner.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// Copyright (c) YugaByte, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except | ||
// in compliance with the License. You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software distributed under the License | ||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express | ||
// or implied. See the License for the specific language governing permissions and limitations | ||
// under the License. | ||
// | ||
|
||
package org.yb.pgsql.cleaners; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.yb.pgsql.BasePgSQLTest; | ||
|
||
import java.sql.Connection; | ||
import java.sql.ResultSet; | ||
import java.sql.Statement; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
/** | ||
* Removes all non-standard roles (except the test role), as well as their permissions and | ||
* owned objects. | ||
* NOTE: This will fail if any roles own objects in databases other than `postgres`, | ||
* so {@link DatabaseCleaner} must be run first. | ||
*/ | ||
public class RoleCleaner implements ClusterCleaner { | ||
private static final Logger LOG = LoggerFactory.getLogger(RoleCleaner.class); | ||
|
||
@Override | ||
public void clean(Connection connection) throws Exception { | ||
LOG.info("Cleaning-up postgres roles and permissions"); | ||
|
||
try (Statement statement = connection.createStatement()) { | ||
statement.execute("RESET SESSION AUTHORIZATION"); | ||
|
||
ResultSet resultSet = statement.executeQuery( | ||
"SELECT rolname FROM pg_roles" + | ||
" WHERE rolname <> 'postgres'" + | ||
" AND rolname <> '" + BasePgSQLTest.TEST_PG_USER + "'" + | ||
" AND rolname NOT LIKE 'pg_%'"); | ||
|
||
List<String> roles = new ArrayList<>(); | ||
while (resultSet.next()) { | ||
roles.add(resultSet.getString(1)); | ||
} | ||
|
||
for (String role : roles) { | ||
statement.execute("DROP OWNED BY " + role + " CASCADE"); | ||
statement.execute("DROP ROLE " + role); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.