提交 384b18f5 authored 作者: noelgrandin's avatar noelgrandin

Adding support for "GRANT ALTER ANY SCHEMA TO <user>", patch by John Yates

上级 e67580d7
...@@ -902,6 +902,17 @@ This command commits an open transaction. ...@@ -902,6 +902,17 @@ This command commits an open transaction.
GRANT SELECT ON TEST TO READONLY GRANT SELECT ON TEST TO READONLY
" "
"Commands (Other)","GRANT ALTER ANY SCHEMA","
GRANT ALTER ANY SCHEMA TO userName
","
Grant schema altering rights to a user.
Admin rights are required to execute this command.
This command commits an open transaction.
","
GRANT ALTER ANY SCHEMA TO Bob
"
"Commands (Other)","GRANT ROLE"," "Commands (Other)","GRANT ROLE","
GRANT roleName TO { PUBLIC | userName | roleName } GRANT roleName TO { PUBLIC | userName | roleName }
"," ","
......
...@@ -88,6 +88,7 @@ Change Log ...@@ -88,6 +88,7 @@ Change Log
Add optional export to MANIFEST.MF for JTS Geometry classes Add optional export to MANIFEST.MF for JTS Geometry classes
Validate that geometry values can be represented in WKB. Validate that geometry values can be represented in WKB.
</li><li>Issue 506: RFE: Include Thread.getName() in case of a deadlock </li><li>Issue 506: RFE: Include Thread.getName() in case of a deadlock
</li><li>Adding support for "GRANT ALTER ANY SCHEMA TO <user>", patch by John Yates
</li></ul> </li></ul>
<h2>Version 1.3.173 (2013-07-28)</h2> <h2>Version 1.3.173 (2013-07-28)</h2>
......
...@@ -3942,45 +3942,54 @@ public class Parser { ...@@ -3942,45 +3942,54 @@ public class Parser {
} }
} }
/**
* @return true if we expect to see a TABLE clause
*/
private boolean addRoleOrRight(GrantRevoke command) { private boolean addRoleOrRight(GrantRevoke command) {
if (readIf("SELECT")) { if (readIf("SELECT")) {
command.addRight(Right.SELECT); command.addRight(Right.SELECT);
return false; return true;
} else if (readIf("DELETE")) { } else if (readIf("DELETE")) {
command.addRight(Right.DELETE); command.addRight(Right.DELETE);
return false; return true;
} else if (readIf("INSERT")) { } else if (readIf("INSERT")) {
command.addRight(Right.INSERT); command.addRight(Right.INSERT);
return false; return true;
} else if (readIf("UPDATE")) { } else if (readIf("UPDATE")) {
command.addRight(Right.UPDATE); command.addRight(Right.UPDATE);
return false; return true;
} else if (readIf("ALL")) { } else if (readIf("ALL")) {
command.addRight(Right.ALL); command.addRight(Right.ALL);
return true;
} else if (readIf("ALTER")) {
read("ANY");
read("SCHEMA");
command.addRight(Right.ALTER_ANY_SCHEMA);
command.addTable(null);
return false; return false;
} else if (readIf("CONNECT")) { } else if (readIf("CONNECT")) {
// ignore this right // ignore this right
return false; return true;
} else if (readIf("RESOURCE")) { } else if (readIf("RESOURCE")) {
// ignore this right // ignore this right
return false; return true;
} else { } else {
command.addRoleName(readUniqueIdentifier()); command.addRoleName(readUniqueIdentifier());
return true; return false;
} }
} }
private GrantRevoke parseGrantRevoke(int operationType) { private GrantRevoke parseGrantRevoke(int operationType) {
GrantRevoke command = new GrantRevoke(session); GrantRevoke command = new GrantRevoke(session);
command.setOperationType(operationType); command.setOperationType(operationType);
boolean isRoleBased = addRoleOrRight(command); boolean tableClauseExpected = addRoleOrRight(command);
while (readIf(",")) { while (readIf(",")) {
boolean next = addRoleOrRight(command); addRoleOrRight(command);
if (next != isRoleBased) { if (command.isRightMode() && command.isRoleMode()) {
throw DbException.get(ErrorCode.ROLES_AND_RIGHT_CANNOT_BE_MIXED); throw DbException.get(ErrorCode.ROLES_AND_RIGHT_CANNOT_BE_MIXED);
} }
} }
if (!isRoleBased) { if (tableClauseExpected) {
if (readIf("ON")) { if (readIf("ON")) {
do { do {
Table table = readTableOrView(); Table table = readTableOrView();
......
...@@ -47,7 +47,7 @@ public class AlterSchemaRename extends DefineCommand { ...@@ -47,7 +47,7 @@ public class AlterSchemaRename extends DefineCommand {
if (db.findSchema(newSchemaName) != null || newSchemaName.equals(oldSchema.getName())) { if (db.findSchema(newSchemaName) != null || newSchemaName.equals(oldSchema.getName())) {
throw DbException.get(ErrorCode.SCHEMA_ALREADY_EXISTS_1, newSchemaName); throw DbException.get(ErrorCode.SCHEMA_ALREADY_EXISTS_1, newSchemaName);
} }
session.getUser().checkAdmin(); session.getUser().checkSchemaAdmin();
db.renameDatabaseObject(session, oldSchema, newSchemaName); db.renameDatabaseObject(session, oldSchema, newSchemaName);
ArrayList<SchemaObject> all = db.getAllSchemaObjects(); ArrayList<SchemaObject> all = db.getAllSchemaObjects();
for (SchemaObject schemaObject : all) { for (SchemaObject schemaObject : all) {
......
...@@ -34,11 +34,14 @@ public class CreateSchema extends DefineCommand { ...@@ -34,11 +34,14 @@ public class CreateSchema extends DefineCommand {
@Override @Override
public int update() { public int update() {
session.getUser().checkAdmin(); session.getUser().checkSchemaAdmin();
session.commit(true); session.commit(true);
Database db = session.getDatabase(); Database db = session.getDatabase();
User user = db.getUser(authorization); User user = db.getUser(authorization);
user.checkAdmin(); // during DB startup, the Right/Role records have not yet been loaded
if (!db.isStarting()) {
user.checkSchemaAdmin();
}
if (db.findSchema(schemaName) != null) { if (db.findSchema(schemaName) != null) {
if (ifNotExists) { if (ifNotExists) {
return 0; return 0;
......
...@@ -32,7 +32,7 @@ public class DropSchema extends DefineCommand { ...@@ -32,7 +32,7 @@ public class DropSchema extends DefineCommand {
@Override @Override
public int update() { public int update() {
session.getUser().checkAdmin(); session.getUser().checkSchemaAdmin();
session.commit(true); session.commit(true);
Database db = session.getDatabase(); Database db = session.getDatabase();
Schema schema = db.findSchema(schemaName); Schema schema = db.findSchema(schemaName);
......
...@@ -183,4 +183,17 @@ public class GrantRevoke extends DefineCommand { ...@@ -183,4 +183,17 @@ public class GrantRevoke extends DefineCommand {
return operationType; return operationType;
} }
/**
* @return true if this command is using Roles
*/
public boolean isRoleMode() {
return roleNames != null;
}
/**
* @return true if this command is using Rights
*/
public boolean isRightMode() {
return rightMask != 0;
}
} }
...@@ -36,6 +36,11 @@ public class Right extends DbObjectBase { ...@@ -36,6 +36,11 @@ public class Right extends DbObjectBase {
*/ */
public static final int UPDATE = 8; public static final int UPDATE = 8;
/**
* The right bit mask that means: create/alter/drop schema is allowed.
*/
public static final int ALTER_ANY_SCHEMA = 16;
/** /**
* The right bit mask that means: select, insert, update, delete, and update * The right bit mask that means: select, insert, update, delete, and update
* for this object is allowed. * for this object is allowed.
...@@ -77,9 +82,10 @@ public class Right extends DbObjectBase { ...@@ -77,9 +82,10 @@ public class Right extends DbObjectBase {
buff.append("ALL"); buff.append("ALL");
} else { } else {
boolean comma = false; boolean comma = false;
comma = appendRight(buff, grantedRight, SELECT, "SELECT", comma); comma = appendRight(buff, grantedRight, SELECT, "SELECT", comma);
comma = appendRight(buff, grantedRight, DELETE, "DELETE", comma); comma = appendRight(buff, grantedRight, DELETE, "DELETE", comma);
comma = appendRight(buff, grantedRight, INSERT, "INSERT", comma); comma = appendRight(buff, grantedRight, INSERT, "INSERT", comma);
comma = appendRight(buff, grantedRight, ALTER_ANY_SCHEMA, "ALTER ANY SCHEMA", comma);
appendRight(buff, grantedRight, UPDATE, "UPDATE", comma); appendRight(buff, grantedRight, UPDATE, "UPDATE", comma);
} }
return buff.toString(); return buff.toString();
...@@ -109,7 +115,10 @@ public class Right extends DbObjectBase { ...@@ -109,7 +115,10 @@ public class Right extends DbObjectBase {
if (grantedRole != null) { if (grantedRole != null) {
buff.append(grantedRole.getSQL()); buff.append(grantedRole.getSQL());
} else { } else {
buff.append(getRights()).append(" ON ").append(table.getSQL()); buff.append(getRights());
if (table != null) {
buff.append(" ON ").append(table.getSQL());
}
} }
buff.append(" TO ").append(grantee.getSQL()); buff.append(" TO ").append(grantee.getSQL());
return buff.toString(); return buff.toString();
......
...@@ -105,12 +105,12 @@ public class User extends RightOwner { ...@@ -105,12 +105,12 @@ public class User extends RightOwner {
/** /**
* See if this user has the given rights for this database object. * See if this user has the given rights for this database object.
* *
* @param table the database object * @param table the database object, or null for schema-only check
* @param rightMask the rights required * @param rightMask the rights required
* @return true if the user has the rights * @return true if the user has the rights
*/ */
public boolean hasRight(Table table, int rightMask) { public boolean hasRight(Table table, int rightMask) {
if (rightMask != Right.SELECT && !systemUser) { if (rightMask != Right.SELECT && !systemUser && table != null) {
table.checkWritingAllowed(); table.checkWritingAllowed();
} }
if (admin) { if (admin) {
...@@ -124,21 +124,23 @@ public class User extends RightOwner { ...@@ -124,21 +124,23 @@ public class User extends RightOwner {
// everybody has access to the metadata information // everybody has access to the metadata information
return true; return true;
} }
String tableType = table.getTableType(); if (table != null) {
if (Table.VIEW.equals(tableType)) { String tableType = table.getTableType();
TableView v = (TableView) table; if (Table.VIEW.equals(tableType)) {
if (v.getOwner() == this) { TableView v = (TableView) table;
// the owner of a view has access: if (v.getOwner() == this) {
// SELECT * FROM (SELECT * FROM ...) // the owner of a view has access:
// SELECT * FROM (SELECT * FROM ...)
return true;
}
} else if (tableType == null) {
// function table
return true;
}
if (table.isTemporary() && !table.isGlobalTemporary()) {
// the owner has all rights on local temporary tables
return true; return true;
} }
} else if (tableType == null) {
// function table
return true;
}
if (table.isTemporary() && !table.isGlobalTemporary()) {
// the owner has all rights on local temporary tables
return true;
} }
if (isRightGrantedRecursive(table, rightMask)) { if (isRightGrantedRecursive(table, rightMask)) {
return true; return true;
...@@ -202,6 +204,18 @@ public class User extends RightOwner { ...@@ -202,6 +204,18 @@ public class User extends RightOwner {
throw DbException.get(ErrorCode.ADMIN_RIGHTS_REQUIRED); throw DbException.get(ErrorCode.ADMIN_RIGHTS_REQUIRED);
} }
} }
/**
* Check if this user has schema admin rights. An exception is thrown if he does
* not have them.
*
* @throws DbException if this user is not a schema admin
*/
public void checkSchemaAdmin() {
if (!admin && !hasRight(null, Right.ALTER_ANY_SCHEMA)) {
throw DbException.get(ErrorCode.ADMIN_RIGHTS_REQUIRED);
}
}
@Override @Override
public int getType() { public int getType() {
......
...@@ -42,7 +42,8 @@ public class TestRights extends TestBase { ...@@ -42,7 +42,8 @@ public class TestRights extends TestBase {
testDropTempTables(); testDropTempTables();
// testLowerCaseUser(); // testLowerCaseUser();
testSchemaRenameUser(); testSchemaRenameUser();
testAccessRights(); testAccessRights();
testSchemaAdminRole();
deleteDb("rights"); deleteDb("rights");
} }
...@@ -198,6 +199,71 @@ public class TestRights extends TestBase { ...@@ -198,6 +199,71 @@ public class TestRights extends TestBase {
stat.execute("drop user test1"); stat.execute("drop user test1");
conn.close(); conn.close();
} }
private void testSchemaAdminRole() throws SQLException {
if (config.memory) {
return;
}
deleteDb("rights");
Connection conn = getConnection("rights");
stat = conn.createStatement();
// default table type
testTableType(conn, "MEMORY");
testTableType(conn, "CACHED");
executeSuccess("CREATE USER SCHEMA_CREATOR PASSWORD 'xyz'");
executeSuccess("CREATE SCHEMA SCHEMA_RIGHT_TEST");
executeSuccess("ALTER SCHEMA SCHEMA_RIGHT_TEST RENAME TO SCHEMA_RIGHT_TEST_RENAMED");
executeSuccess("DROP SCHEMA SCHEMA_RIGHT_TEST_RENAMED");
executeSuccess("CREATE SCHEMA SCHEMA_RIGHT_TEST_EXISTS");
conn.close();
/* try and fail */
conn = getConnection("rights;LOG=2", "SCHEMA_CREATOR", getPassword("xyz"));
stat = conn.createStatement();
assertThrows(ErrorCode.ADMIN_RIGHTS_REQUIRED, stat).
execute("CREATE SCHEMA SCHEMA_RIGHT_TEST_WILL_FAIL");
assertThrows(ErrorCode.ADMIN_RIGHTS_REQUIRED, stat).
execute("ALTER SCHEMA SCHEMA_RIGHT_TEST_EXISTS RENAME TO SCHEMA_RIGHT_TEST_WILL_FAIL");
assertThrows(ErrorCode.ADMIN_RIGHTS_REQUIRED, stat).
execute("DROP SCHEMA SCHEMA_RIGHT_TEST_EXISTS");
conn.close();
/* give them */
conn = getConnection("rights");
stat = conn.createStatement();
executeSuccess("DROP SCHEMA SCHEMA_RIGHT_TEST_EXISTS");
executeSuccess("GRANT ALTER ANY SCHEMA TO SCHEMA_CREATOR");
conn.close();
/* try and succeed */
conn = getConnection("rights;LOG=2", "SCHEMA_CREATOR", getPassword("xyz"));
stat = conn.createStatement();
executeSuccess("CREATE SCHEMA SCHEMA_RIGHT_TEST");
executeSuccess("ALTER SCHEMA SCHEMA_RIGHT_TEST RENAME TO SCHEMA_RIGHT_TEST_RENAMED");
executeSuccess("DROP SCHEMA SCHEMA_RIGHT_TEST_RENAMED");
executeSuccess("CREATE SCHEMA SCHEMA_RIGHT_TEST_EXISTS");
conn.close();
/* revoke them */
conn = getConnection("rights");
stat = conn.createStatement();
executeSuccess("REVOKE ALTER ANY SCHEMA FROM SCHEMA_CREATOR");
conn.close();
/* try and fail */
conn = getConnection("rights;LOG=2", "SCHEMA_CREATOR", getPassword("xyz"));
stat = conn.createStatement();
assertThrows(ErrorCode.ADMIN_RIGHTS_REQUIRED, stat).
execute("CREATE SCHEMA SCHEMA_RIGHT_TEST");
assertThrows(ErrorCode.ADMIN_RIGHTS_REQUIRED, stat).
execute("ALTER SCHEMA SCHEMA_RIGHT_TEST_EXISTS RENAME TO SCHEMA_RIGHT_TEST_RENAMED");
assertThrows(ErrorCode.ADMIN_RIGHTS_REQUIRED, stat).
execute("DROP SCHEMA SCHEMA_RIGHT_TEST_EXISTS");
conn.close();
}
private void testAccessRights() throws SQLException { private void testAccessRights() throws SQLException {
if (config.memory) { if (config.memory) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论