KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mckoi > database > GrantManager


1 /**
2  * com.mckoi.database.GrantManager 23 Aug 2001
3  *
4  * Mckoi SQL Database ( http://www.mckoi.com/database )
5  * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * Version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License Version 2 for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * Version 2 along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  * Change Log:
21  *
22  *
23  */

24
25 package com.mckoi.database;
26
27 import java.sql.*;
28 import com.mckoi.util.IntegerVector;
29 import com.mckoi.util.BigNumber;
30 import com.mckoi.util.Cache;
31
32 /**
33  * A class that manages the grants on a database for a given database
34  * connection and user.
35  *
36  * @author Tobias Downer
37  */

38
39 public class GrantManager {
40
41   // ---------- Statics ----------
42

43   /**
44    * Represents a TABLE object to grant privs over for the user.
45    */

46   public final static int TABLE = 1;
47
48   /**
49    * Represents a DOMAIN object to grant privs over for the user.
50    */

51   public final static int DOMAIN = 2;
52
53 // /**
54
// * Represents a STORED PROCEDURE object to grant privs over for this user.
55
// */
56
// public final static int STORED_PROCEDURE = 16;
57
//
58
// /**
59
// * Represents a TRIGGER object to grant privs over for this user.
60
// */
61
// public final static int TRIGGER = 17;
62
//
63
// /**
64
// * Represents a custom SEQUENCE GENERATOR object to grant privs over.
65
// */
66
// public final static int SEQUENCE_GENERATOR = 18;
67

68   /**
69    * Represents a SCHEMA object to grant privs over for the user.
70    */

71   public final static int SCHEMA = 65;
72
73   /**
74    * Represents a CATALOG object to grant privs over for this user.
75    */

76   public final static int CATALOG = 66;
77
78
79   /**
80    * The string representing the public user (privs granted to all users).
81    */

82   public final static String JavaDoc PUBLIC_USERNAME_STR = "@PUBLIC";
83
84   /**
85    * The name of the 'public' username. If a grant is made on 'public' then
86    * all users are given the grant.
87    */

88   public final static TObject PUBLIC_USERNAME =
89                                        TObject.stringVal(PUBLIC_USERNAME_STR);
90   
91   // ---------- Members ----------
92

93   /**
94    * The DatabaseConnection instance.
95    */

96   private DatabaseConnection connection;
97
98   /**
99    * The QueryContext instance.
100    */

101   private QueryContext context;
102
103   /**
104    * A cache of privileges for the various tables in the database. This cache
105    * is populated as the user 'visits' a table.
106    */

107   private Cache priv_cache;
108
109   /**
110    * Set to true if the grant table is modified in this manager.
111    */

112   private boolean grant_table_changed;
113   
114   
115   /**
116    * Constructs the GrantManager.
117    * Should only be constructed from DatabaseConnection.
118    */

119   GrantManager(DatabaseConnection connection) {
120     this.connection = connection;
121     this.context = new DatabaseQueryContext(connection);
122     this.priv_cache = new Cache(129, 129, 20);
123
124     this.grant_table_changed = false;
125
126     // Attach a cache backed on the GRANTS table which will invalidate the
127
// connection cache whenever the grant table is modified.
128
connection.attachTableBackedCache(new TableBackedCache(Database.SYS_GRANTS) {
129       public void purgeCacheOfInvalidatedEntries(
130                         IntegerVector added_rows, IntegerVector removed_rows) {
131         // If there were changed then invalidate the cache
132
if (grant_table_changed) {
133           invalidateGrantCache();
134           grant_table_changed = false;
135         }
136         // Otherwise, if there were committed added or removed changes also
137
// invalidate the cache,
138
else if ((added_rows != null && added_rows.size() > 0) ||
139                  (removed_rows != null && removed_rows.size() > 0)) {
140           invalidateGrantCache();
141         }
142       }
143     });
144   }
145
146   // ---------- Private priv caching methods ----------
147

148   /**
149    * Flushes any grant information that's being cached.
150    */

151   private void invalidateGrantCache() {
152     priv_cache.removeAll();
153   }
154
155   /**
156    * Inner class that represents a grant query on a particular object, param
157    * and user name.
158    * <p>
159    * This object is designed to be an immutable key in a cache.
160    */

161   private static class GrantQuery {
162     private int object;
163     private String JavaDoc param;
164     private String JavaDoc username;
165     private int flags;
166
167     GrantQuery(int object, String JavaDoc param, String JavaDoc username,
168                boolean flag1, boolean flag2) {
169       this.object = object;
170       this.param = param;
171       this.username = username;
172       this.flags = flag1 ? 1 : 0;
173       this.flags = this.flags | (flag2 ? 2 : 0);
174     }
175     
176     public boolean equals(Object JavaDoc ob) {
177       GrantQuery dest = (GrantQuery) ob;
178       return (object == dest.object &&
179               param.equals(dest.param) &&
180               username.equals(dest.username) &&
181               flags == dest.flags);
182     }
183     
184     public int hashCode() {
185       return object + param.hashCode() + username.hashCode() + flags;
186     }
187     
188   }
189
190
191   
192   private Privileges getPrivs(int object, String JavaDoc param, String JavaDoc username,
193                  boolean only_grant_options,
194                  String JavaDoc granter, boolean include_public_privs)
195                                                      throws DatabaseException {
196
197     // Create the grant query key
198
GrantQuery key = new GrantQuery(object, param, username,
199                                     only_grant_options, include_public_privs);
200     
201     // Is the Privileges object for this query already in the cache?
202
Privileges privs = (Privileges) priv_cache.get(key);
203     if (privs == null) {
204       // Not in cache so we need to ask database for the information.
205

206 // try {
207
// Connection c = connection.getJDBCConnection();
208
// PreparedStatement stmt = c.prepareStatement(
209
// " SELECT \"priv\" FROM \"SYS_INFO.sUSRGrantPriv\" " +
210
// " WHERE \"grant_id\" IN " +
211
// " ( SELECT \"id\" FROM \"SYS_INFO.sUSRGrant\" " +
212
// " WHERE \"param\" = ? " +
213
// " AND \"object\" = ? " +
214
// " AND (\"grantor\" = ? OR (? AND \"grantor\" = '@PUBLIC')) " +
215
// " AND (? OR \"grant_option\" = 'true') " +
216
// " AND (? OR \"granter\" = ?) " +
217
// " )");
218
// stmt.setString(1, param);
219
// stmt.setInt(2, object);
220
// stmt.setString(3, username);
221
// stmt.setBoolean(4, include_public_privs);
222
// stmt.setBoolean(5, !only_grant_options);
223
// stmt.setBoolean(6, (granter == null));
224
// stmt.setString(7, granter);
225
// ResultSet rs = stmt.executeQuery();
226
// privs = Privileges.fromResultSet(rs);
227
// rs.close();
228
// stmt.close();
229
// c.close();
230
// }
231
// catch (SQLException e) {
232
// connection.Debug().writeException(e);
233
// throw new DatabaseException("SQL Error: " + e.getMessage());
234
// }
235

236       // The system grants table.
237
DataTable grant_table = connection.getTable(Database.SYS_GRANTS);
238   
239       Variable object_col = grant_table.getResolvedVariable(1);
240       Variable param_col = grant_table.getResolvedVariable(2);
241       Variable grantee_col = grant_table.getResolvedVariable(3);
242       Variable grant_option_col = grant_table.getResolvedVariable(4);
243       Variable granter_col = grant_table.getResolvedVariable(5);
244       Operator EQUALS = Operator.get("=");
245   
246       Table t1 = grant_table;
247       
248       // All that match the given object parameter
249
// It's most likely this will reduce the search by the most so we do
250
// it first.
251
t1 = t1.simpleSelect(context, param_col, EQUALS,
252                                  new Expression(TObject.stringVal(param)));
253
254       // The next is a single exhaustive select through the remaining records.
255
// It finds all grants that match either public or the grantee is the
256
// username, and that match the object type.
257

258       // Expression: ("grantee_col" = username OR "grantee_col" = 'public')
259
Expression user_check =
260           Expression.simple(grantee_col, EQUALS, TObject.stringVal(username));
261       if (include_public_privs) {
262         user_check = new Expression(
263             user_check, Operator.get("or"),
264             Expression.simple(grantee_col, EQUALS, PUBLIC_USERNAME)
265         );
266       }
267       // Expression: ("object_col" = object AND
268
// ("grantee_col" = username OR "grantee_col" = 'public'))
269
// All that match the given username or public and given object
270
Expression expr = new Expression(
271           Expression.simple(object_col, EQUALS, TObject.intVal(object)),
272           Operator.get("and"),
273           user_check);
274
275       // Are we only searching for grant options?
276
if (only_grant_options) {
277         Expression grant_option_check =
278             Expression.simple(grant_option_col, EQUALS,
279                               TObject.stringVal("true"));
280         expr = new Expression(expr, Operator.get("and"), grant_option_check);
281       }
282
283       // Do we need to check for a granter when we looking for privs?
284
if (granter != null) {
285         Expression granter_check =
286             Expression.simple(granter_col, EQUALS, TObject.stringVal(granter));
287         expr = new Expression(expr, Operator.get("and"), granter_check);
288       }
289
290       t1 = t1.exhaustiveSelect(context, expr);
291
292       // For each grant, merge with the resultant priv object
293
privs = Privileges.EMPTY_PRIVS;
294       RowEnumeration e = t1.rowEnumeration();
295       while (e.hasMoreRows()) {
296         int row_index = e.nextRowIndex();
297         BigNumber priv_bit =
298                       (BigNumber) t1.getCellContents(0, row_index).getObject();
299         privs = privs.add(priv_bit.intValue());
300       }
301
302       // Put the privs object in the cache
303
priv_cache.put(key, privs);
304
305     }
306
307     return privs;
308   }
309
310   /**
311    * Internal method that sets the privs for the given object, param, grantee,
312    * grant option and granter. This first revokes any grants that have been
313    * setup for the object, and adds a new record with the new grants.
314    */

315   private void internalSetPrivs(Privileges new_privs, int object, String JavaDoc param,
316                     String JavaDoc grantee, boolean grant_option, String JavaDoc granter)
317                                                     throws DatabaseException {
318
319     // Revoke existing privs on this object for this grantee
320
revokeAllGrantsOnObject(object, param, grantee, grant_option, granter);
321
322     if (!new_privs.isEmpty()) {
323
324       // The system grants table.
325
DataTable grant_table = connection.getTable(Database.SYS_GRANTS);
326       
327       // Add the grant to the grants table.
328
RowData rdat = new RowData(grant_table);
329       rdat.setColumnDataFromObject(0, BigNumber.fromInt(new_privs.toInt()));
330       rdat.setColumnDataFromObject(1, BigNumber.fromInt(object));
331       rdat.setColumnDataFromObject(2, param);
332       rdat.setColumnDataFromObject(3, grantee);
333       rdat.setColumnDataFromObject(4, grant_option ? "true" : "false");
334       rdat.setColumnDataFromObject(5, granter);
335       grant_table.add(rdat);
336
337       // Invalidate the privilege cache
338
invalidateGrantCache();
339
340       // Notify that the grant table has changed.
341
grant_table_changed = true;
342
343     }
344
345   }
346   
347   // ---------- Public methods ----------
348

349   /**
350    * Adds a grant on the given database object.
351    *
352    * @param privs the privileges to grant.
353    * @param object the object to grant (TABLE, DOMAIN, etc)
354    * @param param the parameter of the object (eg. the table name)
355    * @param grantee the user name to grant the privs to.
356    * @param grant_option if true, allows the user to pass grants to other
357    * users.
358    * @param granter the user granting.
359    */

360   public void addGrant(Privileges privs, int object, String JavaDoc param,
361                        String JavaDoc grantee, boolean grant_option, String JavaDoc granter)
362                                                     throws DatabaseException {
363
364     if (object == TABLE) {
365       // Check that the table exists,
366
if (!connection.tableExists(TableName.resolve(param))) {
367         throw new DatabaseException("Table: " + param + " does not exist.");
368       }
369     }
370     else if (object == SCHEMA) {
371       // Check that the schema exists.
372
if (!connection.schemaExists(param)) {
373         throw new DatabaseException("Schema: " + param + " does not exist.");
374       }
375     }
376
377     // Get any existing grants on this object to this grantee
378
Privileges existing_privs =
379                 getPrivs(object, param, grantee, grant_option, granter, false);
380     // Merge the existing privs with the new privs being added.
381
Privileges new_privs = privs.merge(existing_privs);
382     
383     // If the new_privs are the same as the existing privs, don't bother
384
// changing anything.
385
if (!new_privs.equals(existing_privs)) {
386       internalSetPrivs(new_privs, object, param, grantee,
387                        grant_option, granter);
388     }
389     
390   }
391
392   /**
393    * For all tables in the given schema, this adds the given grant for each
394    * of the tables.
395    */

396   public void addGrantToAllTablesInSchema(String JavaDoc schema, Privileges privs,
397                                        String JavaDoc grantee, boolean grant_option,
398                                      String JavaDoc granter) throws DatabaseException {
399     // The list of all tables
400
TableName[] list = connection.getTableList();
401     for (int i = 0; i < list.length; ++i) {
402       TableName tname = list[i];
403       // If the table is in the given schema,
404
if (tname.getSchema().equals(schema)) {
405         addGrant(privs, TABLE, tname.toString(), grantee,
406                  grant_option, granter);
407       }
408     }
409   }
410   
411   /**
412    * Removes a grant on the given object for the given grantee, grant option
413    * and granter.
414    */

415   public void removeGrant(Privileges privs, int object, String JavaDoc param,
416                           String JavaDoc grantee, boolean grant_option, String JavaDoc granter)
417                                                     throws DatabaseException {
418     
419     // Get any existing grants on this object to this grantee
420
Privileges existing_privs =
421                 getPrivs(object, param, grantee, grant_option, granter, false);
422     // Remove privs from the the existing privs.
423
Privileges new_privs = existing_privs.remove(privs);
424
425     // If the new_privs are the same as the existing privs, don't bother
426
// changing anything.
427
if (!new_privs.equals(existing_privs)) {
428       internalSetPrivs(new_privs, object, param, grantee,
429                        grant_option, granter);
430     }
431     
432   }
433   
434   /**
435    * Removes all privs granted on the given object for the given grantee with
436    * the given grant option.
437    */

438   public void revokeAllGrantsOnObject(int object, String JavaDoc param,
439               String JavaDoc grantee, boolean grant_option, String JavaDoc granter)
440                                                      throws DatabaseException {
441     // The system grants table.
442
DataTable grant_table = connection.getTable(Database.SYS_GRANTS);
443
444     Variable object_col = grant_table.getResolvedVariable(1);
445     Variable param_col = grant_table.getResolvedVariable(2);
446     Variable grantee_col = grant_table.getResolvedVariable(3);
447     Variable grant_option_col = grant_table.getResolvedVariable(4);
448     Variable granter_col = grant_table.getResolvedVariable(5);
449     Operator EQUALS = Operator.get("=");
450
451     Table t1 = grant_table;
452     
453     // All that match the given object parameter
454
// It's most likely this will reduce the search by the most so we do
455
// it first.
456
t1 = t1.simpleSelect(context, param_col, EQUALS,
457                                new Expression(TObject.stringVal(param)));
458
459     // The next is a single exhaustive select through the remaining records.
460
// It finds all grants that match either public or the grantee is the
461
// username, and that match the object type.
462

463     // Expression: ("grantee_col" = username)
464
Expression user_check =
465         Expression.simple(grantee_col, EQUALS, TObject.stringVal(grantee));
466     // Expression: ("object_col" = object AND
467
// "grantee_col" = username)
468
// All that match the given username or public and given object
469
Expression expr = new Expression(
470         Expression.simple(object_col, EQUALS, TObject.intVal(object)),
471         Operator.get("and"),
472         user_check);
473
474     // Are we only searching for grant options?
475
Expression grant_option_check =
476         Expression.simple(grant_option_col, EQUALS,
477                           TObject.stringVal(grant_option ? "true" : "false"));
478     expr = new Expression(expr, Operator.get("and"), grant_option_check);
479
480     // Make sure the granter matches up also
481
Expression granter_check =
482         Expression.simple(granter_col, EQUALS, TObject.stringVal(granter));
483     expr = new Expression(expr, Operator.get("and"), granter_check);
484     
485     t1 = t1.exhaustiveSelect(context, expr);
486
487     // Remove these rows from the table
488
grant_table.delete(t1);
489
490     // Invalidate the privilege cache
491
invalidateGrantCache();
492
493     // Notify that the grant table has changed.
494
grant_table_changed = true;
495
496   }
497   
498   /**
499    * Completely removes all privs granted on the given object for all users.
500    * This would typically be used when the object is dropped from the database.
501    */

502   public void revokeAllGrantsOnObject(int object, String JavaDoc param)
503                                                     throws DatabaseException {
504     // The system grants table.
505
DataTable grant_table = connection.getTable(Database.SYS_GRANTS);
506
507     Variable object_col = grant_table.getResolvedVariable(1);
508     Variable param_col = grant_table.getResolvedVariable(2);
509     // All that match the given object
510
Table t1 = grant_table.simpleSelect(context, object_col,
511                    Operator.get("="), new Expression(TObject.intVal(object)));
512     // All that match the given parameter
513
t1 = t1.simpleSelect(context,
514                          param_col, Operator.get("="),
515                          new Expression(TObject.stringVal(param)));
516
517     // Remove these rows from the table
518
grant_table.delete(t1);
519
520     // Invalidate the privilege cache
521
invalidateGrantCache();
522
523     // Notify that the grant table has changed.
524
grant_table_changed = true;
525     
526   }
527
528   /**
529    * Returns all Privileges for the given object for the given grantee (user).
530    * This would be used to determine the access a user has to a table.
531    * <p>
532    * Note that the Privileges object includes all the grants on the object given
533    * to PUBLIC also.
534    * <p>
535    * This method will concatenate multiple privs granted on the same
536    * object.
537    * <p>
538    * PERFORMANCE: This method is called a lot (at least once on every query).
539    */

540   public Privileges userGrants(int object, String JavaDoc param, String JavaDoc username)
541                                                     throws DatabaseException {
542     return getPrivs(object, param, username, false, null, true);
543   }
544
545   /**
546    * Returns all Privileges for the given object for the given grantee (user)
547    * that the user is allowed to give grant options for. This would be used to
548    * determine if a user has privs to give another user grants on an object.
549    * <p>
550    * Note that the Privileges object includes all the grants on the object given
551    * to PUBLIC also.
552    * <p>
553    * This method will concatenate multiple grant options given on the same
554    * object to the user.
555    */

556   public Privileges userGrantOptions(int object, String JavaDoc param, String JavaDoc username)
557                                                     throws DatabaseException {
558     return getPrivs(object, param, username, true, null, true);
559   }
560   
561 }
562
Popular Tags