From 9cc181083162c80b69ffc6ec7ac13e861016c120 Mon Sep 17 00:00:00 2001 From: AlexCatze Date: Sat, 19 Nov 2022 12:01:32 +0100 Subject: [PATCH] [FEATURE] Roles support for MySQL and PostgreSQL (#622) * [FEATURE] Roles support for MySQL and PostgreSQL Roles are stored in separate (name,uuid) table. Role can be given to user by adding "role." permission to user. Permissions can be added to role by adding record with role`s uuid to permissions table. * [REFACTOR] Refactor roles quering Use 2 queries, first to query names of all user`s roles, second to recursively query all permissions. This also allows roles inheritance. All code from previous realisation, that is not used in this one, was commented, so if this realisation will be accepted, that code can be finaly removed. * [REFACTOR] Removed commented code * [REFACTOR] Remover useless catches --- .../auth/core/AbstractSQLCoreProvider.java | 91 +++++++++++++------ 1 file changed, 64 insertions(+), 27 deletions(-) diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AbstractSQLCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AbstractSQLCoreProvider.java index 2cb8aa7e..cd717494 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AbstractSQLCoreProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AbstractSQLCoreProvider.java @@ -24,6 +24,8 @@ import java.sql.SQLException; import java.time.Clock; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; public abstract class AbstractSQLCoreProvider extends AuthCoreProvider { @@ -38,11 +40,17 @@ public abstract class AbstractSQLCoreProvider extends AuthCoreProvider { public String permissionsTable; public String permissionsPermissionColumn; public String permissionsUUIDColumn; + + public String rolesTable; + public String rolesNameColumn; + public String rolesUUIDColumn; + public PasswordVerifier passwordVerifier; public String customQueryByUUIDSQL; public String customQueryByUsernameSQL; public String customQueryByLoginSQL; public String customQueryPermissionsByUUIDSQL; + public String customQueryRolesByUserUUID; public String customUpdateAuthSQL; public String customUpdateServerIdSQL; // Prepared SQL queries @@ -50,6 +58,8 @@ public abstract class AbstractSQLCoreProvider extends AuthCoreProvider { public transient String queryByUsernameSQL; public transient String queryByLoginSQL; public transient String queryPermissionsByUUIDSQL; + public transient String queryRolesByUserUUID; + public transient String updateAuthSQL; public transient String updateServerIDSQL; public transient LaunchServer server; @@ -59,8 +69,8 @@ public abstract class AbstractSQLCoreProvider extends AuthCoreProvider { @Override public User getUserByUsername(String username) { try { - return query(queryByUsernameSQL, username); - } catch (IOException e) { + return queryUser(queryByUsernameSQL, username); + } catch (Exception e) { logger.error("SQL error", e); return null; } @@ -69,8 +79,8 @@ public User getUserByUsername(String username) { @Override public User getUserByUUID(UUID uuid) { try { - return query(queryByUUIDSQL, uuid.toString()); - } catch (IOException e) { + return queryUser(queryByUUIDSQL, uuid.toString()); + } catch (Exception e) { logger.error("SQL error", e); return null; } @@ -79,8 +89,8 @@ public User getUserByUUID(UUID uuid) { @Override public User getUserByLogin(String login) { try { - return query(queryByLoginSQL, login); - } catch (IOException e) { + return queryUser(queryByLoginSQL, login); + } catch (Exception e) { logger.error("SQL error", e); return null; } @@ -165,6 +175,20 @@ public void init(LaunchServer server) { queryByUsernameSQL = customQueryByUsernameSQL != null ? customQueryByUsernameSQL : String.format("SELECT %s FROM %s WHERE %s=? LIMIT 1", userInfoCols, table, usernameColumn); queryByLoginSQL = customQueryByLoginSQL != null ? customQueryByLoginSQL : queryByUsernameSQL; + + queryPermissionsByUUIDSQL = customQueryPermissionsByUUIDSQL != null ? customQueryPermissionsByUUIDSQL : + "WITH RECURSIVE req AS (\n" + + "SELECT p."+permissionsPermissionColumn+" FROM "+permissionsTable+" p WHERE p."+permissionsUUIDColumn+" = ?\n" + + "UNION ALL\n" + + "SELECT p."+permissionsPermissionColumn+" FROM "+permissionsTable+" p\n" + + "INNER JOIN "+rolesTable+" r ON p."+permissionsUUIDColumn+" = r."+rolesUUIDColumn+"\n" + + "INNER JOIN req ON r."+rolesUUIDColumn+"=substring(req.permission from 6) or r.name=substring(req.permission from 6)\n" + + ") SELECT * FROM req"; + + queryRolesByUserUUID = customQueryRolesByUserUUID != null ? customQueryRolesByUserUUID : String.format("SELECT r.%s FROM %s r\n" + + "INNER JOIN %s pr ON r.%s=substring(pr.%s from 6) or r.%s=substring(pr.%s from 6)\n" + + "WHERE pr.%s = ?",rolesNameColumn,rolesTable,permissionsTable,rolesUUIDColumn,permissionsPermissionColumn,rolesNameColumn,permissionsPermissionColumn,permissionsUUIDColumn); + updateAuthSQL = customUpdateAuthSQL != null ? customUpdateAuthSQL : String.format("UPDATE %s SET %s=?, %s=NULL WHERE %s=?", table, accessTokenColumn, serverIDColumn, uuidColumn); updateServerIDSQL = customUpdateServerIdSQL != null ? customUpdateServerIdSQL : String.format("UPDATE %s SET %s=? WHERE %s=?", @@ -214,27 +238,12 @@ private SQLUser constructUser(ResultSet set) throws SQLException { set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), isEnabledPermissions() ? requestPermissions(set.getString(uuidColumn)) : new ClientPermissions()) : null; } - public ClientPermissions requestPermissions(String uuid) throws SQLException { - try (Connection c = getSQLConfig().getConnection()) { - PreparedStatement s = c.prepareStatement(queryPermissionsByUUIDSQL); - s.setString(1, uuid); - s.setQueryTimeout(MySQLSourceConfig.TIMEOUT); - ResultSet set = s.executeQuery(); - ClientPermissions perms = new ClientPermissions(); - while (set.next()) { - perms.addPerm(set.getString(permissionsPermissionColumn)); - } - return perms; - } catch (SQLException e) { - throw new SQLException(e); - } + public ClientPermissions requestPermissions (String uuid) throws SQLException + { + return new ClientPermissions(queryRolesNames(queryRolesByUserUUID,uuid),queryPermissions(queryPermissionsByUUIDSQL,uuid)); } - public boolean isEnabledPermissions() { - return permissionsPermissionColumn != null; - } - - private User query(String sql, String value) throws IOException { + private SQLUser queryUser(String sql, String value) throws SQLException { try (Connection c = getSQLConfig().getConnection()) { PreparedStatement s = c.prepareStatement(sql); s.setString(1, value); @@ -242,8 +251,36 @@ private User query(String sql, String value) throws IOException { try (ResultSet set = s.executeQuery()) { return constructUser(set); } - } catch (SQLException e) { - throw new IOException(e); + } + } + + private List queryPermissions(String sql, String value) throws SQLException { + try (Connection c = getSQLConfig().getConnection()) { + PreparedStatement s = c.prepareStatement(sql); + s.setString(1, value); + s.setQueryTimeout(MySQLSourceConfig.TIMEOUT); + ResultSet set = s.executeQuery(); + List perms = new ArrayList<>(); + while (set.next()) + perms.add(set.getString(permissionsPermissionColumn)); + return perms; + } + } + + public boolean isEnabledPermissions() { + return permissionsPermissionColumn != null; + } + + private List queryRolesNames(String sql, String value) throws SQLException { + try (Connection c = getSQLConfig().getConnection()) { + PreparedStatement s = c.prepareStatement(sql); + s.setString(1, value); + s.setQueryTimeout(MySQLSourceConfig.TIMEOUT); + ResultSet set = s.executeQuery(); + List perms = new ArrayList<>(); + while (set.next()) + perms.add(set.getString(rolesNameColumn)); + return perms; } }