提交 7761843c authored 作者: Thomas Mueller's avatar Thomas Mueller

Granting a schema is now supported.

上级 4d1e5e1e
......@@ -912,7 +912,8 @@ COMMIT TRANSACTION XID_TEST
"Commands (Other)","GRANT RIGHT","
GRANT { SELECT | INSERT | UPDATE | DELETE | ALL } [,...] ON
tableName [,...] TO { PUBLIC | userName | roleName }
{ { SCHEMA schemaName } | { tableName [,...] } }
TO { PUBLIC | userName | roleName }
","
Grants rights for a table to a user or role.
......@@ -963,7 +964,8 @@ PREPARE COMMIT XID_TEST
"Commands (Other)","REVOKE RIGHT","
REVOKE { SELECT | INSERT | UPDATE | DELETE | ALL } [,...] ON
tableName [,...] FROM { PUBLIC | userName | roleName }
{ { SCHEMA schemaName } | { tableName [,...] } }
FROM { PUBLIC | userName | roleName }
","
Removes rights for a table from a user or role.
......
......@@ -20,7 +20,8 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>Linked tables did not work when a function-based index is present (Oracle).
<ul><li>Granting a schema is now supported.
</li><li>Linked tables did not work when a function-based index is present (Oracle).
</li><li>Creating a user with a null password, salt, or hash threw a NullPointerException.
</li><li>Foreign key: don't add a single column index if column
is leading key of existing index.
......@@ -30,7 +31,7 @@ Change Log
</li><li>Issue 609: the spatial index did not support NULL with update and delete operations.
</li><li>Pull request #2: Add external metadata type support (table type "external")
</li><li>MS SQL Server: the CONVERT method did not work in views
and derrived tables.
and derived tables.
</li><li>Java 8 compatibility for "regexp_replace".
</li><li>When in cluster mode, and one of the nodes goes down,
we need to log the problem with priority "error", not "debug"
......
......@@ -4262,12 +4262,17 @@ public class Parser {
}
if (tableClauseExpected) {
if (readIf("ON")) {
if (readIf("SCHEMA")) {
Schema schema = database.getSchema(readAliasIdentifier());
command.setSchema(schema);
} else {
do {
Table table = readTableOrView();
command.addTable(table);
} while (readIf(","));
}
}
}
if (operationType == CommandInterface.GRANT) {
read("TO");
} else {
......
......@@ -45,6 +45,14 @@ public class CreateUser extends DefineCommand {
this.password = password;
}
/**
* Set the salt and hash for the given user.
*
* @param user the user
* @param session the session
* @param salt the salt
* @param hash the hash
*/
static void setSaltAndHash(User user, Session session, Expression salt, Expression hash) {
user.setSaltAndHash(getByteArray(session, salt), getByteArray(session, hash));
}
......@@ -54,6 +62,13 @@ public class CreateUser extends DefineCommand {
return s == null ? new byte[0] : StringUtils.convertHexToBytes(s);
}
/**
* Set the password for the given user.
*
* @param user the user
* @param session the session
* @param password the password
*/
static void setPassword(User user, Session session, Expression password) {
String pwd = password.optimize(session).getValue(session).getString();
char[] passwordChars = pwd == null ? new char[0] : pwd.toCharArray();
......
......@@ -10,11 +10,13 @@ import java.util.ArrayList;
import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface;
import org.h2.engine.Database;
import org.h2.engine.DbObject;
import org.h2.engine.Right;
import org.h2.engine.RightOwner;
import org.h2.engine.Role;
import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.schema.Schema;
import org.h2.table.Table;
import org.h2.util.New;
......@@ -31,6 +33,7 @@ public class GrantRevoke extends DefineCommand {
private int operationType;
private int rightMask;
private final ArrayList<Table> tables = New.arrayList();
private Schema schema;
private RightOwner grantee;
public GrantRevoke(Session session) {
......@@ -105,20 +108,27 @@ public class GrantRevoke extends DefineCommand {
}
private void grantRight() {
Database db = session.getDatabase();
if (schema != null) {
grantRight(schema);
}
for (Table table : tables) {
Right right = grantee.getRightForTable(table);
grantRight(table);
}
}
private void grantRight(DbObject object) {
Database db = session.getDatabase();
Right right = grantee.getRightForObject(object);
if (right == null) {
int id = getObjectId();
right = new Right(db, id, grantee, rightMask, table);
grantee.grantRight(table, right);
right = new Right(db, id, grantee, rightMask, object);
grantee.grantRight(object, right);
db.addDatabaseObject(session, right);
} else {
right.setRightMask(right.getRightMask() | rightMask);
db.updateMeta(session, right);
}
}
}
private void grantRole(Role grantedRole) {
if (grantedRole != grantee && grantee.isRoleGranted(grantedRole)) {
......@@ -139,10 +149,18 @@ public class GrantRevoke extends DefineCommand {
}
private void revokeRight() {
if (schema != null) {
revokeRight(schema);
}
for (Table table : tables) {
Right right = grantee.getRightForTable(table);
revokeRight(table);
}
}
private void revokeRight(DbObject object) {
Right right = grantee.getRightForObject(object);
if (right == null) {
continue;
return;
}
int mask = right.getRightMask();
int newRight = mask & ~rightMask;
......@@ -154,7 +172,7 @@ public class GrantRevoke extends DefineCommand {
db.updateMeta(session, right);
}
}
}
private void revokeRole(Role grantedRole) {
Right right = grantee.getRightForRole(grantedRole);
......@@ -179,6 +197,15 @@ public class GrantRevoke extends DefineCommand {
tables.add(table);
}
/**
* Set the specified schema
*
* @param schema the schema
*/
public void setSchema(Schema schema) {
this.schema = schema;
}
@Override
public int getType() {
return operationType;
......
......@@ -352,8 +352,14 @@ public class ScriptCommand extends ScriptBase {
}
// Generate GRANT ...
for (Right right : db.getAllRights()) {
Table table = right.getGrantedTable();
if (table != null) {
DbObject object = right.getGrantedObject();
if (object != null) {
if (object instanceof Schema) {
if (excludeSchema((Schema) object)) {
continue;
}
} else if (object instanceof Table) {
Table table = (Table) object;
if (excludeSchema(table.getSchema())) {
continue;
}
......@@ -361,6 +367,7 @@ public class ScriptCommand extends ScriptBase {
continue;
}
}
}
add(right.getCreateSQL(), false);
}
// Generate COMMENT ON ...
......
......@@ -7,6 +7,7 @@ package org.h2.engine;
import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.schema.Schema;
import org.h2.table.Table;
/**
......@@ -46,10 +47,25 @@ public class Right extends DbObjectBase {
*/
public static final int ALL = SELECT | DELETE | INSERT | UPDATE;
/**
* To whom the right is granted.
*/
private RightOwner grantee;
/**
* The granted role, or null if a right was granted.
*/
private Role grantedRole;
/**
* The granted right.
*/
private int grantedRight;
private Table grantedTable;
private RightOwner grantee;
/**
* The object. If the right is global, this is null.
*/
private DbObject grantedObject;
public Right(Database db, int id, RightOwner grantee, Role grantedRole) {
initDbObjectBase(db, id, "RIGHT_" + id, Trace.USER);
......@@ -58,11 +74,11 @@ public class Right extends DbObjectBase {
}
public Right(Database db, int id, RightOwner grantee, int grantedRight,
Table grantedRightOnTable) {
DbObject grantedObject) {
initDbObjectBase(db, id, "" + id, Trace.USER);
this.grantee = grantee;
this.grantedRight = grantedRight;
this.grantedTable = grantedRightOnTable;
this.grantedObject = grantedObject;
}
private static boolean appendRight(StringBuilder buff, int right, int mask,
......@@ -97,8 +113,8 @@ public class Right extends DbObjectBase {
return grantedRole;
}
public Table getGrantedTable() {
return grantedTable;
public DbObject getGrantedObject() {
return grantedObject;
}
public DbObject getGrantee() {
......@@ -112,14 +128,22 @@ public class Right extends DbObjectBase {
@Override
public String getCreateSQLForCopy(Table table, String quotedName) {
return getCreateSQLForCopy(table);
}
private String getCreateSQLForCopy(DbObject object) {
StringBuilder buff = new StringBuilder();
buff.append("GRANT ");
if (grantedRole != null) {
buff.append(grantedRole.getSQL());
} else {
buff.append(getRights());
if (table != null) {
buff.append(" ON ").append(table.getSQL());
if (object != null) {
if (object instanceof Schema) {
buff.append(" ON SCHEMA ").append(object.getSQL());
} else if (object instanceof Table) {
buff.append(" ON ").append(object.getSQL());
}
}
}
buff.append(" TO ").append(grantee.getSQL());
......@@ -128,7 +152,7 @@ public class Right extends DbObjectBase {
@Override
public String getCreateSQL() {
return getCreateSQLForCopy(grantedTable, null);
return getCreateSQLForCopy(grantedObject);
}
@Override
......@@ -138,14 +162,14 @@ public class Right extends DbObjectBase {
@Override
public void removeChildrenAndResources(Session session) {
if (grantedTable != null) {
grantee.revokeRight(grantedTable);
} else {
if (grantedRole != null) {
grantee.revokeRole(grantedRole);
} else {
grantee.revokeRight(grantedObject);
}
database.removeMeta(session, getId());
grantedRole = null;
grantedTable = null;
grantedObject = null;
grantee = null;
invalidate();
}
......
......@@ -23,7 +23,7 @@ public abstract class RightOwner extends DbObjectBase {
/**
* The map of granted rights.
*/
private HashMap<Table, Right> grantedRights;
private HashMap<DbObject, Right> grantedRights;
protected RightOwner(Database database, int id, String name,
String traceModule) {
......@@ -55,7 +55,9 @@ public abstract class RightOwner extends DbObjectBase {
/**
* Check if a right is already granted to this object or to objects that
* were granted to this object.
* were granted to this object. The rights for schemas takes
* precedence over rights of tables, in other words, the rights of schemas
* will be valid for every each table in the related schema.
*
* @param table the table to check
* @param rightMask the right mask to check
......@@ -64,6 +66,14 @@ public abstract class RightOwner extends DbObjectBase {
boolean isRightGrantedRecursive(Table table, int rightMask) {
Right right;
if (grantedRights != null) {
if (table != null) {
right = grantedRights.get(table.getSchema());
if (right != null) {
if ((right.getRightMask() & rightMask) == rightMask) {
return true;
}
}
}
right = grantedRights.get(table);
if (right != null) {
if ((right.getRightMask() & rightMask) == rightMask) {
......@@ -85,26 +95,26 @@ public abstract class RightOwner extends DbObjectBase {
* Grant a right for the given table. Only one right object per table is
* supported.
*
* @param table the table
* @param object the object (table or schema)
* @param right the right
*/
public void grantRight(Table table, Right right) {
public void grantRight(DbObject object, Right right) {
if (grantedRights == null) {
grantedRights = New.hashMap();
}
grantedRights.put(table, right);
grantedRights.put(object, right);
}
/**
* Revoke the right for the given table.
* Revoke the right for the given object (table or schema).
*
* @param table the table
* @param object the object
*/
void revokeRight(Table table) {
void revokeRight(DbObject object) {
if (grantedRights == null) {
return;
}
grantedRights.remove(table);
grantedRights.remove(object);
if (grantedRights.size() == 0) {
grantedRights = null;
}
......@@ -143,16 +153,16 @@ public abstract class RightOwner extends DbObjectBase {
}
/**
* Get the 'grant table' right of this object.
* Get the 'grant schema' right of this object.
*
* @param table the granted table
* @param object the granted object (table or schema)
* @return the right or null if the right has not been granted
*/
public Right getRightForTable(Table table) {
public Right getRightForObject(DbObject object) {
if (grantedRights == null) {
return null;
}
return grantedRights.get(table);
return grantedRights.get(object);
}
/**
......
......@@ -299,7 +299,8 @@ COMMIT TRANSACTION transactionName
Sets the resolution of an in-doubt transaction to 'commit'."
"Commands (Other)","GRANT RIGHT","
GRANT { SELECT | INSERT | UPDATE | DELETE | ALL } [,...] ON
tableName [,...] TO { PUBLIC | userName | roleName }
{ { SCHEMA schemaName } | { tableName [,...] } }
TO { PUBLIC | userName | roleName }
","
Grants rights for a table to a user or role."
"Commands (Other)","GRANT ALTER ANY SCHEMA","
......@@ -320,7 +321,8 @@ PREPARE COMMIT newTransactionName
Prepares committing a transaction."
"Commands (Other)","REVOKE RIGHT","
REVOKE { SELECT | INSERT | UPDATE | DELETE | ALL } [,...] ON
tableName [,...] FROM { PUBLIC | userName | roleName }
{ { SCHEMA schemaName } | { tableName [,...] } }
FROM { PUBLIC | userName | roleName }
","
Removes rights for a table from a user or role."
"Commands (Other)","REVOKE ROLE","
......
......@@ -1128,8 +1128,19 @@ public class MetaTable extends Table {
String rightType = grantee.getType() == DbObject.USER ?
"USER" : "ROLE";
if (role == null) {
Table granted = r.getGrantedTable();
String tableName = identifier(granted.getName());
DbObject object = r.getGrantedObject();
Schema schema = null;
Table table = null;
if (object != null) {
if (object instanceof Schema) {
schema = (Schema) object;
} else if (object instanceof Table) {
table = (Table) object;
schema = table.getSchema();
}
}
String tableName = (table != null) ? identifier(table.getName()) : "";
String schemaName = (schema != null) ? identifier(schema.getName()) : "";
if (!checkIndex(session, tableName, indexFrom, indexTo)) {
continue;
}
......@@ -1143,9 +1154,9 @@ public class MetaTable extends Table {
// RIGHTS
r.getRights(),
// TABLE_SCHEMA
identifier(granted.getSchema().getName()),
schemaName,
// TABLE_NAME
identifier(granted.getName()),
tableName,
// ID
"" + r.getId()
);
......@@ -1375,7 +1386,11 @@ public class MetaTable extends Table {
}
case TABLE_PRIVILEGES: {
for (Right r : database.getAllRights()) {
Table table = r.getGrantedTable();
DbObject object = r.getGrantedObject();
if (!(object instanceof Table)) {
continue;
}
Table table = (Table) object;
if (table == null || hideTable(table, session)) {
continue;
}
......@@ -1390,7 +1405,11 @@ public class MetaTable extends Table {
}
case COLUMN_PRIVILEGES: {
for (Right r : database.getAllRights()) {
Table table = r.getGrantedTable();
DbObject object = r.getGrantedObject();
if (!(object instanceof Table)) {
continue;
}
Table table = (Table) object;
if (table == null || hideTable(table, session)) {
continue;
}
......
......@@ -385,7 +385,7 @@ public abstract class Table extends SchemaObjectBase {
}
ArrayList<Right> rights = database.getAllRights();
for (Right right : rights) {
if (right.getGrantedTable() == this) {
if (right.getGrantedObject() == this) {
children.add(right);
}
}
......@@ -510,7 +510,7 @@ public abstract class Table extends SchemaObjectBase {
database.removeSchemaObject(session, constraint);
}
for (Right right : database.getAllRights()) {
if (right.getGrantedTable() == this) {
if (right.getGrantedObject() == this) {
database.removeDatabaseObject(session, right);
}
}
......
......@@ -37,6 +37,8 @@ public class TestRights extends TestBase {
testNullPassword();
testLinkedTableMeta();
testGrantMore();
testGrantSchema();
testRevokeSchema();
testOpenNonAdminWithMode();
testDisallowedTables();
testDropOwnUser();
......@@ -123,6 +125,120 @@ public class TestRights extends TestBase {
conn.close();
}
private void testGrantSchema() throws SQLException {
deleteDb("rights");
Connection connAdmin = getConnection("rights");
// Test with user
Statement statAdmin = connAdmin.createStatement();
statAdmin.execute("create user test_user password 'test'");
statAdmin.execute("create table test1(id int)");
statAdmin.execute("create table test2(id int)");
statAdmin.execute("create table test3(id int)");
statAdmin.execute("grant insert on schema public to test_user");
statAdmin.execute("create table test4(id int)");
Connection conn = getConnection("rights", "test_user", getPassword("test"));
Statement stat = conn.createStatement();
// Must proceed
stat.execute("insert into test1 values (1)");
stat.execute("insert into test2 values (1)");
stat.execute("insert into test3 values (1)");
stat.execute("insert into test4 values (1)");
// Must not proceed
assertThrows("Not enough rights for object \"PUBLIC.TEST1\"", stat, "select * from test1");
assertThrows("Not enough rights for object \"PUBLIC.TEST2\"", stat, "select * from test2");
assertThrows("Not enough rights for object \"PUBLIC.TEST3\"", stat, "select * from test3");
assertThrows("Not enough rights for object \"PUBLIC.TEST4\"", stat, "select * from test4");
// Test with role
statAdmin.execute("create role test_role");
statAdmin.execute("grant test_role to test_user");
statAdmin.execute("grant select on schema public to test_role");
// create the table after grant
statAdmin.execute("create table test5(id int)");
// Must proceed
stat.execute("insert into test1 values (2)");
stat.execute("insert into test2 values (2)");
stat.execute("insert into test3 values (2)");
stat.execute("insert into test4 values (2)");
stat.execute("insert into test5 values (1)");
stat.execute("select * from test1");
stat.execute("select * from test2");
stat.execute("select * from test3");
stat.execute("select * from test4");
stat.execute("select * from test5");
conn.close();
connAdmin.close();
deleteDb("rights");
}
private void testRevokeSchema() throws SQLException {
deleteDb("rights");
Connection connAdmin = getConnection("rights");
Statement statAdmin = connAdmin.createStatement();
// Test with user
statAdmin = connAdmin.createStatement();
statAdmin.execute("create user test_user password 'test'");
statAdmin.execute("create table test1(id int)");
statAdmin.execute("create table test2(id int)");
statAdmin.execute("create table test3(id int)");
statAdmin.execute("grant insert on schema public to test_user");
Connection conn = getConnection("rights", "test_user", getPassword("test"));
Statement stat = conn.createStatement();
// Must proceed
stat.execute("insert into test1 values (1)");
stat.execute("insert into test2 values (1)");
stat.execute("insert into test3 values (1)");
statAdmin.execute("revoke insert on schema public from test_user");
statAdmin.execute("create table test4(id int)");
// Must not proceed
assertThrows("Not enough rights for object \"PUBLIC.TEST1\"",
stat, "insert into test1 values (2)");
assertThrows("Not enough rights for object \"PUBLIC.TEST2\"",
stat, "insert into test2 values (2)");
assertThrows("Not enough rights for object \"PUBLIC.TEST3\"",
stat, "insert into test3 values (2)");
assertThrows("Not enough rights for object \"PUBLIC.TEST4\"",
stat, "insert into test4 values (2)");
// Test with role
statAdmin.execute("create role test_role");
statAdmin.execute("grant test_role to test_user");
statAdmin.execute("grant select on schema public to test_role");
// Must proceed
stat.execute("select * from test1");
stat.execute("select * from test2");
stat.execute("select * from test3");
stat.execute("select * from test4");
statAdmin.execute("revoke select on schema public from test_role");
statAdmin.execute("create table test5(id int)");
// Must not proceed
assertThrows("Not enough rights for object \"PUBLIC.TEST1\"",
stat, "select * from test1");
assertThrows("Not enough rights for object \"PUBLIC.TEST2\"",
stat, "select * from test2");
assertThrows("Not enough rights for object \"PUBLIC.TEST3\"",
stat, "select * from test3");
assertThrows("Not enough rights for object \"PUBLIC.TEST4\"",
stat, "select * from test4");
assertThrows("Not enough rights for object \"PUBLIC.TEST5\"",
stat, "select * from test5");
conn.close();
connAdmin.close();
deleteDb("rights");
}
private void testOpenNonAdminWithMode() throws SQLException {
if (config.memory) {
return;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论