Project

General

Profile

1 1425 tao
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A class that handles checking permssision for a document
4 2245 sgarg
               and subtree in a document
5
 *
6 1425 tao
 *  Copyright: 2000 Regents of the University of California and the
7
 *             National Center for Ecological Analysis and Synthesis
8
 *    Authors: Chad Berkley
9
 *
10
 *   '$Author$'
11
 *     '$Date$'
12
 * '$Revision$'
13
 *
14
 * This program is free software; you can redistribute it and/or modify
15
 * it under the terms of the GNU General Public License as published by
16
 * the Free Software Foundation; either version 2 of the License, or
17
 * (at your option) any later version.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 * GNU General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU General Public License
25
 * along with this program; if not, write to the Free Software
26
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27
 */
28
package edu.ucsb.nceas.metacat;
29 2245 sgarg
30 7475 leinfelder
import java.sql.PreparedStatement;
31
import java.sql.ResultSet;
32
import java.sql.SQLException;
33 9705 leinfelder
import java.util.Arrays;
34 1485 tao
import java.util.Enumeration;
35 1434 tao
import java.util.Hashtable;
36 1425 tao
import java.util.Vector;
37
38 2663 sgarg
import org.apache.log4j.Logger;
39 9452 leinfelder
import org.dataone.service.types.v1.Identifier;
40
import org.dataone.service.types.v1.Permission;
41
import org.dataone.service.types.v1.Session;
42
import org.dataone.service.types.v1.Subject;
43 9455 leinfelder
import org.dataone.service.types.v2.SystemMetadata;
44 2663 sgarg
45 5090 daigle
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlList;
46 5015 daigle
import edu.ucsb.nceas.metacat.database.DBConnection;
47
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
48 9452 leinfelder
import edu.ucsb.nceas.metacat.dataone.D1NodeService;
49 9455 leinfelder
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
50 5030 daigle
import edu.ucsb.nceas.metacat.properties.PropertyService;
51 5072 daigle
import edu.ucsb.nceas.metacat.service.SessionService;
52 7475 leinfelder
import edu.ucsb.nceas.metacat.shared.MetacatUtilException;
53 6675 leinfelder
import edu.ucsb.nceas.metacat.util.AuthUtil;
54 5025 daigle
import edu.ucsb.nceas.metacat.util.DocumentUtil;
55 4698 daigle
import edu.ucsb.nceas.metacat.util.MetacatUtil;
56 5072 daigle
import edu.ucsb.nceas.metacat.util.SessionData;
57 4080 daigle
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
58 7475 leinfelder
import edu.ucsb.nceas.utilities.access.AccessControlInterface;
59 4080 daigle
60 1425 tao
public class PermissionController
61
{
62 6744 leinfelder
//   private String docId = null;
63
//   private int rev = -1;
64
65
   private String guid = null;
66
67 1425 tao
   private boolean hasSubTreeAccessControl = false; // flag if has a subtree
68
                                                    // access for this docid
69 4861 daigle
   private Vector subTreeList = new Vector();
70 2245 sgarg
71 5098 daigle
   private static final long TOPLEVELSTARTNODEID = 0; //if start node is 0, means it is top
72 1527 tao
                                         //level document
73 2245 sgarg
74 2663 sgarg
   private static Logger logMetacat = Logger.getLogger(PermissionController.class);
75 2245 sgarg
76 1425 tao
   /**
77 6744 leinfelder
	 * Constructor for PermissionController
78
	 *
79
	 * @param myDocid the docid need to access
80
	 */
81
	public PermissionController(String myDocid) throws McdbException {
82 2245 sgarg
83 6744 leinfelder
		// find the guid if we can
84
		String docId = null;
85
		int rev = -1;
86 2245 sgarg
87 6744 leinfelder
		// get the parts
88
		docId = DocumentUtil.getSmartDocId(myDocid);
89
		rev = DocumentUtil.getRevisionFromAccessionNumber(myDocid);
90
91
		// this is what we really want
92
		guid = IdentifierManager.getInstance().getGUID(docId, rev);
93
94
	}
95
96 2245 sgarg
   /**
97 1485 tao
    * Return if a document has subtree access control
98
    */
99
   public boolean hasSubTreeAccessControl()
100
   {
101
     return hasSubTreeAccessControl;
102
   }
103 2245 sgarg
104 5072 daigle
   public boolean hasPermission(String sessionId, String myPermission) throws SQLException {
105 5374 berkley
       SessionData sessionData = null;
106
       sessionData = SessionService.getInstance().getRegisteredSession(sessionId);
107
       if (sessionData == null) {
108
           return false;
109
       }
110 5072 daigle
111
	   return hasPermission(sessionData.getUserName(), sessionData.getGroupNames(), myPermission);
112
   }
113
114 2245 sgarg
115 1425 tao
  /**
116
    * Check from db connection if at least one of the list of @principals
117 6675 leinfelder
    * Administrators are allowed all permission
118 1425 tao
    * @param user  the user name
119
    * @param groups  the groups which the use is in
120
    * @param myPermission  permission type to check for
121
    */
122 2245 sgarg
  public boolean hasPermission(String user, String[]groups, String myPermission)
123 4950 daigle
                              throws SQLException //, Exception
124 1425 tao
  {
125
    boolean hasPermission=false;
126
    String [] userPackage=null;
127 5742 berkley
    int permission = AccessControlList.intValue(myPermission);
128 2245 sgarg
129 6675 leinfelder
    //for the command line invocation and replication
130 1425 tao
    if ((user==null) && (groups==null || groups.length==0))
131
    {
132
      return true;
133
    }
134 6675 leinfelder
135
    // for administrators
136
    //see http://bugzilla.ecoinformatics.org/show_bug.cgi?id=4728
137
    try {
138
		if (AuthUtil.isAdministrator(user, groups)) {
139
			return true;
140
		}
141
	} catch (MetacatUtilException e) {
142
		// not much we can do here, except treat them as normal
143
		logMetacat.warn("Error checking for administrator: " + e.getMessage(), e);
144
	}
145 9452 leinfelder
146
    // for DataONE rightsHolder permission
147
    boolean isOwner = false;
148
    try {
149
		Session userSession = new Session();
150
		Subject subject = new Subject();
151
		subject.setValue(user);
152
		userSession.setSubject(subject);
153
		Identifier pid = new Identifier();
154
		pid.setValue(guid);
155 9455 leinfelder
		//isOwner = D1NodeService.userHasPermission(userSession, pid, Permission.CHANGE_PERMISSION);
156
		SystemMetadata sysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
157
		isOwner = (sysMeta.getRightsHolder().equals(subject));
158 9452 leinfelder
    } catch (Exception e) {
159
		logMetacat.warn("Error checking for DataONE permissions: " + e.getMessage(), e);
160
		isOwner = false;
161
    }
162
    if (isOwner) {
163
    	return true;
164
    }
165 2245 sgarg
166 9705 leinfelder
    logMetacat.debug("Checking permission on " + this.guid + " for user: " + user + " and groups: " + Arrays.toString(groups));
167
168 1425 tao
    //create a userpackage including user, public and group member
169
    userPackage=createUsersPackage(user, groups);
170 2245 sgarg
171 1425 tao
    //if the requested document is access documents and requested permission
172
    //is "write", the user should have "all" right
173 2245 sgarg
174 6744 leinfelder
    if (isAccessDocument() && (permission == AccessControlInterface.WRITE))
175 1425 tao
    {
176 2245 sgarg
177 6744 leinfelder
      hasPermission = hasPermission(userPackage, 7);// 7 is all permission
178 1425 tao
    }//if
179
    else //in other situation, just check the request permission
180
    {
181
      // Check for @permission on @docid for @user and/or @groups
182 6744 leinfelder
      hasPermission = hasPermission(userPackage, permission);
183 1425 tao
    }//else
184 2245 sgarg
185 1425 tao
    return hasPermission;
186
  }
187 2245 sgarg
188
189 1425 tao
  /**
190
    * Check from db connection if the users in String array @principals has
191 2245 sgarg
    * @permission on @docid*
192 1425 tao
    * @param principals, names in userPakcage need to check for @permission
193
    * @param docid, document identifier to check on
194 2245 sgarg
    * @param permission, permission (write or all...) to check for
195 1425 tao
    */
196 6744 leinfelder
  private boolean hasPermission(String [] principals, int permission)
197 1425 tao
                         throws SQLException
198
  {
199 1527 tao
    long startId = TOPLEVELSTARTNODEID;// this is for top level, so startid is 0
200 2245 sgarg
    try
201 1425 tao
    {
202
      //first, if there is a docid owner in user package, return true
203 2245 sgarg
      //because doc owner has all permssion
204 6744 leinfelder
      if (containDocumentOwner(principals))
205 1425 tao
      {
206 2245 sgarg
207 1425 tao
          return true;
208
      }
209 2245 sgarg
210 1425 tao
      //If there is no owner in user package, checking the table
211
      //check perm_order
212 6744 leinfelder
      if (isAllowFirst(principals, startId))
213 1425 tao
      {
214 2245 sgarg
215 6744 leinfelder
        if (hasExplicitDenyRule(principals, permission, startId))
216 1425 tao
        {
217
          //if it is allowfirst and has deny rule(either explicit )
218
          //deny access
219 2245 sgarg
220 1425 tao
          return false;
221
        }//if
222 6744 leinfelder
        else if ( hasAllowRule(principals, permission, startId))
223 1425 tao
        {
224
          //if it is allowfirst and hasn't deny rule and has allow rule
225
          //allow access
226 2245 sgarg
227 1425 tao
          return true;
228
        }//else if
229
        else
230
        {
231
          //other situation deny access
232 2245 sgarg
233 1425 tao
          return false;
234
        }//else
235
     }//if isAllowFirst
236
     else //denyFirst
237
     {
238 6744 leinfelder
       if (hasAllowRule(principals, permission, startId))
239 1425 tao
       {
240
         //if it is denyFirst and has allow rule, allow access
241
         return true;
242
       }
243
       else
244
       {
245
         //if it is denyfirst but no allow rule, deny access
246
         return false;
247
       }
248
     }//else denyfirst
249
    }//try
250
    catch (Exception e)
251
    {
252 5311 daigle
      logMetacat.warn("PermissionController.hasPermission - There is a exception in hasPermission method: "
253 2663 sgarg
                         +e.getMessage());
254 1425 tao
    }
255 2245 sgarg
256 1425 tao
    return false;
257
  }//hasPermission
258 2245 sgarg
259 1425 tao
  /**
260 2245 sgarg
   *  Method to check if a person has permission to a inline data file
261
   * @param user String
262
   * @param groups String[]
263
   * @param myPermission String
264
   * @param inlineDataId String
265
   * @throws McdbException
266
   * @return boolean
267
   */
268
  private boolean hasPermissionForInlineData(String user, String[] groups,
269
                                      String myPermission, String inlineDataId)
270 5298 jones
      throws McdbException
271 2245 sgarg
  {
272
     // this method can call the public method - hasPermission(...)
273
     // the only difference is about the ownership, you couldn't find the owner
274
     // from inlinedataId directly. You should get it from eml document itself
275
     String []userPackage = createUsersPackage(user, groups);
276 5298 jones
     try {
277 6744 leinfelder
        if (containDocumentOwner(userPackage))
278 5298 jones
         {
279
           return true;
280
         }
281
         else
282
         {
283 6744 leinfelder
        	 // is a funky inline data id with the extra part, so we set it manually
284
             PermissionController controller = new PermissionController(guid);
285
             controller.guid = inlineDataId;
286 5298 jones
             return controller.hasPermission(user, groups, myPermission);
287
         }
288
    } catch (SQLException e) {
289
        throw new McdbException(e.getMessage());
290
    }
291 2245 sgarg
  }
292
293
  /**
294 1485 tao
   * The method to determine of a node can be access by a user just by subtree
295
   * access control
296
   */
297
  public boolean hasPermissionForSubTreeNode(String user, String[] groups,
298
                                             String myPermission, long nodeId)
299
                                             throws McdbException
300
  {
301 1492 tao
    boolean flag = true;
302 1485 tao
    // Get unaccessble subtree for this user
303 4861 daigle
    Hashtable unaccessableSubTree = hasUnaccessableSubTree(user, groups,
304 1485 tao
                                                           myPermission);
305 4861 daigle
    Enumeration en = unaccessableSubTree.elements();
306 1485 tao
    while (en.hasMoreElements())
307
    {
308
      SubTree tree = (SubTree)en.nextElement();
309
      long start = tree.getStartNodeId();
310
      long stop  = tree.getEndNodeId();
311 1492 tao
      // nodeid in unaccessablesubtree, return false
312 1485 tao
      if ( nodeId >= start && nodeId <= stop)
313
      {
314 1492 tao
        flag = false;
315 1485 tao
        break;
316
      }
317
    }
318
    return flag;
319
  }
320
  /**
321 2245 sgarg
   * This method will return a hasTable of subtree which user doesn't has the
322 1434 tao
   * permssion to access
323
   * @param user  the user name
324
   * @param groups  the groups which the use is in
325
   * @param myPermission  permission type to check for
326
   */
327 4861 daigle
  public Hashtable hasUnaccessableSubTree(String user, String[] groups,
328 1434 tao
                                       String myPermission) throws McdbException
329
  {
330 4861 daigle
    Hashtable resultUnaccessableSubTree = new Hashtable();
331 1434 tao
    String [] principals=null;
332
    int permission =AccessControlList.intValue(myPermission);
333 2245 sgarg
334 1434 tao
    //for the commnad line invocation return null(no unaccessable subtree)
335
    if ((user==null) && (groups==null || groups.length==0))
336
    {
337
      return resultUnaccessableSubTree;
338
    }
339 2245 sgarg
340 1434 tao
    //create a userpackage including user, public and group member
341
    principals=createUsersPackage(user, groups);
342
    //for the document owner return null(no unaccessable subtree)
343
    try
344
    {
345 6744 leinfelder
      if (containDocumentOwner(principals))
346 1434 tao
      {
347
       return resultUnaccessableSubTree;
348
      }
349
    }
350
    catch (SQLException ee)
351
    {
352
      throw new McdbException(ee);
353
    }
354 2245 sgarg
355 1434 tao
    // go through every subtree which has access control
356
    for (int i = 0; i< subTreeList.size(); i++)
357
    {
358
      SubTree tree = (SubTree)subTreeList.elementAt(i);
359 1527 tao
      long startId = tree.getStartNodeId();
360 2245 sgarg
361
362 1434 tao
        try
363
        {
364 6744 leinfelder
          if (isAllowFirst(principals, startId))
365 1434 tao
          {
366 2245 sgarg
367 6744 leinfelder
            if (hasExplicitDenyRule(principals, permission, startId ))
368 1434 tao
            {
369 2245 sgarg
370 1434 tao
             //if it is allowfirst and has deny rule
371
              // put the subtree into unaccessable vector
372 1527 tao
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
373 1434 tao
              {
374 1527 tao
                resultUnaccessableSubTree.put(new Long(startId), tree);
375 1434 tao
              }
376
            }//if
377 6744 leinfelder
            else if ( hasAllowRule(principals, permission, startId))
378 1434 tao
            {
379
              //if it is allowfirst and hasn't deny rule and has allow rule
380
              //allow access do nothing
381 2245 sgarg
382 1434 tao
            }//else if
383
            else
384
            {
385
              //other situation deny access
386 1527 tao
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
387 1434 tao
              {
388 1527 tao
                resultUnaccessableSubTree.put(new Long(startId), tree);
389 1434 tao
              }
390 2245 sgarg
391 1434 tao
            }//else
392
          }//if isAllowFirst
393
          else //denyFirst
394
          {
395 6744 leinfelder
            if (hasAllowRule(principals, permission,startId))
396 1434 tao
            {
397
              //if it is denyFirst and has allow rule, allow access, do nothing
398 2245 sgarg
399 1434 tao
            }
400
            else
401
            {
402
              //if it is denyfirst but no allow rule, deny access
403
              // add into vector
404 1527 tao
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
405 1434 tao
              {
406 1527 tao
                resultUnaccessableSubTree.put(new Long(startId), tree);
407 1434 tao
              }
408
            }
409
          }//else denyfirst
410
        }//try
411
        catch( Exception e)
412
        {
413 5311 daigle
          logMetacat.error("PermissionController.hasUnaccessableSubTree - error in PermissionControl.has" +
414 2663 sgarg
                                   "UnaccessableSubTree "+e.getMessage());
415 1434 tao
          throw new McdbException(e);
416
        }
417 2245 sgarg
418 1434 tao
    }//for
419 2245 sgarg
    // merge the subtree if a subtree is another subtree'subtree
420 1521 tao
    resultUnaccessableSubTree = mergeEquivalentSubtree(resultUnaccessableSubTree);
421 1434 tao
    return resultUnaccessableSubTree;
422
  }//hasUnaccessableSubtree
423 2245 sgarg
424
425
  /*
426 1513 tao
   * A method to merge nested subtree into bigger one. For example subtree b
427
   * is a subtree of subtree a. And user doesn't have read permission for both
428
   * so we only use subtree a is enough.
429
   */
430 4861 daigle
  private Hashtable mergeEquivalentSubtree(Hashtable unAccessSubTree)
431 1513 tao
  {
432 4861 daigle
    Hashtable newSubTreeHash = new Hashtable();
433 1513 tao
    boolean   needDelete = false;
434
    // check the parameters
435
    if (unAccessSubTree == null || unAccessSubTree.isEmpty())
436
    {
437
      return newSubTreeHash;
438
    }
439
    else
440
    {
441
      // look every subtree start point and stop point, to see if it is embedded
442
      // in another one. If embedded, they are equavelent and we can use bigger
443
      // one to replace smaller one
444 4861 daigle
      Enumeration en = unAccessSubTree.elements();
445 1513 tao
      while (en.hasMoreElements())
446
      {
447
        SubTree tree    = (SubTree)en.nextElement();
448
        String  treeId  = tree.getSubTreeId();
449
        long    startId = tree.getStartNodeId();
450
        long    endId   = tree.getEndNodeId();
451 2245 sgarg
452 4861 daigle
        Enumeration enu = unAccessSubTree.elements();
453 1513 tao
        while (enu.hasMoreElements())
454
        {
455
          SubTree subTree = (SubTree)enu.nextElement();
456 4861 daigle
          String subTreeId= subTree.getSubTreeId();
457 1513 tao
          long   subTreeStartId = subTree.getStartNodeId();
458
          long   subTreeEndId   = subTree.getEndNodeId();
459
          //compare and if the first subtree is a subtree of the second
460
          // one, set neeDelete true
461
          if (startId > subTreeStartId && endId < subTreeEndId)
462
          {
463
            needDelete = true;
464 5311 daigle
            logMetacat.info("PermissionController.mergeEquivalentSubtree - the subtree: "+ treeId +
465 1513 tao
                                     " need to be get rid of from unaccessable"+
466
                                     " subtree list becuase it is a subtree of"+
467 2663 sgarg
                                     " another subtree in the list");
468 1513 tao
            break;
469
          }//if
470
        }//while
471
        // if not need to delete, put the subtree into hash
472
        if (!needDelete)
473
        {
474 1527 tao
          newSubTreeHash.put(new Long(startId), tree);
475 1513 tao
        }
476
        //reset needDelete
477
        needDelete = false;
478
      }//while
479
      return newSubTreeHash;
480
    }//else
481
  }
482 2245 sgarg
483 1434 tao
  /**
484 4080 daigle
	 * Check if a document id is a access document. Access document need user
485
	 * has "all" permission to access it.
486
	 *
487
	 * @param docId,
488
	 *            the document id need to be checked
489
	 */
490 6744 leinfelder
	private boolean isAccessDocument() throws SQLException {
491
		// get the docid from the guid
492
		String docId = null;
493
		try {
494
			docId = IdentifierManager.getInstance().getLocalId(guid);
495
		} catch (McdbDocNotFoundException e) {
496
			return false;
497
			//throw new SQLException(e);
498
		}
499
500 5025 daigle
		docId = DocumentUtil.getDocIdFromString(docId);
501 4080 daigle
		PreparedStatement pStmt = null;
502
		DBConnection conn = null;
503
		int serialNumber = -1;
504
		try {
505
			// check out DBConnection
506
			conn = DBConnectionPool.getDBConnection("PermissionControl.isAccessDoc");
507
			serialNumber = conn.getCheckOutSerialNumber();
508 6595 leinfelder
			pStmt = conn.prepareStatement("select doctype from xml_documents where docid like ? ");
509
			pStmt.setString(1, docId);
510 4080 daigle
			pStmt.execute();
511
			ResultSet rs = pStmt.getResultSet();
512
			boolean hasRow = rs.next();
513
			String doctype = null;
514
			if (hasRow) {
515
				doctype = rs.getString(1);
516 2245 sgarg
517 4080 daigle
			}
518
			pStmt.close();
519 2245 sgarg
520 4080 daigle
			// if it is an access document
521
			if (doctype != null
522 4698 daigle
					&& ((MetacatUtil.getOptionList(PropertyService
523 4213 daigle
							.getProperty("xml.accessdoctype")).contains(doctype)))) {
524 2245 sgarg
525 4080 daigle
				return true;
526
			}
527 2245 sgarg
528 4080 daigle
		} catch (SQLException e) {
529
			throw new SQLException("PermissionControl.isAccessDocument "
530
					+ "Error checking" + " on document " + docId + ". " + e.getMessage());
531
		} catch (PropertyNotFoundException pnfe) {
532
			throw new SQLException("PermissionControl.isAccessDocument "
533
					+ "Error checking" + " on document " + docId + ". " + pnfe.getMessage());
534
		} finally {
535
			try {
536
				pStmt.close();
537
			} finally {
538
				DBConnectionPool.returnDBConnection(conn, serialNumber);
539
			}
540
		}
541 2245 sgarg
542 4080 daigle
		return false;
543
	}// isAccessDocument
544 2245 sgarg
545
546
547 1425 tao
  /**
548 4080 daigle
	 * Check if a stirng array contains a given documents' owner
549
	 *
550
	 * @param principals,
551
	 *            a string array storing the username, groups name and public.
552
	 * @param docid,
553
	 *            the id of given documents
554
	 */
555 6744 leinfelder
  private boolean containDocumentOwner(String[] principals)
556 1425 tao
                    throws SQLException
557
  {
558 6744 leinfelder
559
	// get the docid
560
	String docId = null;
561
	try {
562
		docId = IdentifierManager.getInstance().getLocalId(guid);
563
		docId = DocumentUtil.getDocIdFromString(docId);
564
	} catch (McdbDocNotFoundException e) {
565
		// should be true if we own the parent doc, but likely won't be checked in that case
566
		return false;
567
	}
568
569 1425 tao
    int lengthOfArray=principals.length;
570 2245 sgarg
    boolean hasRow;
571 1425 tao
    PreparedStatement pStmt=null;
572
    DBConnection conn = null;
573
    int serialNumber = -1;
574 2245 sgarg
575 1425 tao
    try
576
    {
577
      //check out DBConnection
578 1434 tao
     conn=DBConnectionPool.getDBConnection("PermissionControl.containDocOnwer");
579 1425 tao
      serialNumber=conn.getCheckOutSerialNumber();
580
      pStmt = conn.prepareStatement(
581
                "SELECT 'x' FROM xml_documents " +
582 6196 leinfelder
                "WHERE docid = ? AND lower(user_owner) = ? " +
583
                "UNION ALL " +
584
                "SELECT 'x' FROM xml_revisions " +
585
                "WHERE docid = ? AND lower(user_owner) = ? ");
586 1425 tao
      //check every element in the string array too see if it conatains
587
      //the owner of document
588
      for (int i=0; i<lengthOfArray; i++)
589
      {
590 2245 sgarg
591 1425 tao
        // Bind the values to the query
592
        pStmt.setString(1, docId);
593
        pStmt.setString(2, principals[i]);
594 6196 leinfelder
        pStmt.setString(3, docId);
595
        pStmt.setString(4, principals[i]);
596 5311 daigle
        logMetacat.info("PermissionController.containDocumentOwner - the principle stack is : " +
597 2663 sgarg
                                  principals[i]);
598 1425 tao
599
        pStmt.execute();
600
        ResultSet rs = pStmt.getResultSet();
601
        hasRow = rs.next();
602 2245 sgarg
        if (hasRow)
603 1425 tao
        {
604
          pStmt.close();
605 5311 daigle
           logMetacat.info("PermissionController.containDocumentOwner - find the owner");
606 1425 tao
          return true;
607 2245 sgarg
        }//if
608
609 1425 tao
      }//for
610
    }//try
611 2245 sgarg
    catch (SQLException e)
612 1425 tao
    {
613
        pStmt.close();
614 2245 sgarg
615
        throw new
616 5099 daigle
        SQLException("PermissionControl.hasPermission - " +
617 1425 tao
                     "Error checking ownership for " + principals[0] +
618
                     " on document #" + docId + ". " + e.getMessage());
619
    }//catch
620
    finally
621
    {
622
      try
623
      {
624
        pStmt.close();
625
      }
626
      finally
627
      {
628
        DBConnectionPool.returnDBConnection(conn, serialNumber);
629
      }
630
    }
631 2245 sgarg
    return false;
632 1425 tao
  }//containDocumentOwner
633 2245 sgarg
634 1425 tao
  /**
635
    * Check if the permission order for user at that documents is allowFirst
636 2245 sgarg
    * @param principals, list of names of principals to check for
637 1425 tao
    * @param docid, document identifier to check for
638
    */
639 6744 leinfelder
  private boolean isAllowFirst(String [] principals, long startId)
640 1425 tao
                  throws SQLException, Exception
641
  {
642
    int lengthOfArray=principals.length;
643
    boolean hasRow;
644
    PreparedStatement pStmt = null;
645
    DBConnection conn = null;
646
    int serialNumber = -1;
647 1434 tao
    String sql = null;
648
    boolean topLever =false;
649 1527 tao
    if (startId == TOPLEVELSTARTNODEID)
650 1434 tao
    {
651
      //top level
652
      topLever = true;
653 6744 leinfelder
      sql =
654
    	  "SELECT perm_order FROM xml_access " +
655
		    "WHERE lower(principal_name) = ? " +
656
		    "AND guid = ? " +
657
		    "AND startnodeid is NULL";
658 1434 tao
    }
659
    else
660
    {
661
      //sub tree level
662
      sql = "SELECT perm_order FROM xml_access " +
663 6744 leinfelder
        "WHERE lower(principal_name) = ? " +
664
        "AND guid = ? " +
665
        "AND startnodeid = ?";
666 1434 tao
    }
667 2245 sgarg
668 1425 tao
    try
669
    {
670
      //check out DBConnection
671
      conn=DBConnectionPool.getDBConnection("AccessControlList.isAllowFirst");
672
      serialNumber=conn.getCheckOutSerialNumber();
673 2245 sgarg
674 1425 tao
      //select permission order from database
675 1434 tao
      pStmt = conn.prepareStatement(sql);
676 2245 sgarg
677 1425 tao
      //check every name in the array
678
      for (int i=0; i<lengthOfArray;i++)
679
      {
680
        //bind value
681
        pStmt.setString(1, principals[i]);//user name
682 6744 leinfelder
        pStmt.setString(2, guid);//guid
683
684 2245 sgarg
        // if subtree, we need set subtree id
685 1434 tao
        if (!topLever)
686
        {
687 1527 tao
          pStmt.setLong(3, startId);
688 1434 tao
        }
689 2245 sgarg
690 1425 tao
        pStmt.execute();
691
        ResultSet rs = pStmt.getResultSet();
692
        hasRow=rs.next();
693
        if (hasRow)
694
        {
695
          //get the permission order from data base
696
          String permissionOrder=rs.getString(1);
697
          //if the permission order is "allowFirst
698
          if (permissionOrder.equalsIgnoreCase(AccessControlInterface.ALLOWFIRST))
699
          {
700
            pStmt.close();
701
            return true;
702
          }
703
          else
704
          {
705
            pStmt.close();
706
            return false;
707
          }
708
        }//if
709
      }//for
710
    }//try
711
    catch (SQLException e)
712
    {
713
      throw e;
714
    }
715
    finally
716
    {
717
      try
718
      {
719
        pStmt.close();
720
      }
721
      finally
722
      {
723
        DBConnectionPool.returnDBConnection(conn, serialNumber);
724
      }
725
    }
726 2245 sgarg
727
    //if reach here, means there is no permssion record for given names and
728 1425 tao
    //docid. So throw a exception.
729 2245 sgarg
730 5099 daigle
    throw new Exception("PermissionController.isAllowFirst - There is no permission record for user "+ principals[0] +
731 6744 leinfelder
                        " at document " + guid);
732 2245 sgarg
733 1425 tao
  }//isAllowFirst
734 2245 sgarg
735 1425 tao
  /**
736 2245 sgarg
    * Check if the users array has allow rules for given users, docid and
737 1425 tao
    * permission.
738
    * If it has permission rule and ticket count is greater than 0, the ticket
739
    * number will decrease one for every allow rule
740 2245 sgarg
    * @param principals, list of names of principals to check for
741 1425 tao
    * @param docid, document identifier to check for
742
    * @param permission, the permssion need to check
743
    */
744 6744 leinfelder
  private boolean hasAllowRule(String [] principals, int permission, long startId)
745 1425 tao
                  throws SQLException, Exception
746
 {
747
   int lengthOfArray=principals.length;
748
   boolean allow=false;//initial value is no allow rule
749
   ResultSet rs;
750
   PreparedStatement pStmt = null;
751
   int permissionValue=permission;
752
   int permissionValueInTable;
753
   DBConnection conn = null;
754
   int serialNumber = -1;
755 1434 tao
   boolean topLever = false;
756
   String sql = null;
757 1527 tao
   if (startId == TOPLEVELSTARTNODEID)
758 1434 tao
   {
759
     // for toplevel
760
     topLever = true;
761 6744 leinfelder
     sql = "SELECT permission " +
762
		"FROM xml_access " +
763
 		"WHERE guid = ? " +
764
 		"AND lower(principal_name) = ? " +
765
 		"AND perm_type = ? " +
766
 		"AND startnodeid is NULL";
767 1434 tao
   }
768
   else
769
   {
770
     topLever =false;
771 6744 leinfelder
     sql = "SELECT permission " +
772
     		"FROM xml_access " +
773
     		"WHERE guid = ? " +
774
     		"AND lower(principal_name) = ? " +
775
     		"AND perm_type = ? " +
776
     		"AND startnodeid = ?";
777 1434 tao
   }
778 1425 tao
   try
779
   {
780
     //check out DBConnection
781
     conn=DBConnectionPool.getDBConnection("AccessControlList.hasAllowRule");
782
     serialNumber=conn.getCheckOutSerialNumber();
783 2245 sgarg
    //This sql statement will select entry with
784 1425 tao
    //begin_time<=currentTime<=end_time in xml_access table
785
    //If begin_time or end_time is null in table, isnull(begin_time, sysdate)
786
    //function will assign begin_time=sysdate
787 1434 tao
    pStmt = conn.prepareStatement(sql);
788 1425 tao
    //bind docid, perm_type
789 6744 leinfelder
    pStmt.setString(1, guid);
790 1425 tao
    pStmt.setString(3, AccessControlInterface.ALLOW);
791 2245 sgarg
792 1434 tao
    // if subtree lever, need to set subTreeId
793
    if (!topLever)
794
    {
795 1527 tao
      pStmt.setLong(4, startId);
796 1434 tao
    }
797 2245 sgarg
798 1425 tao
    //bind every elenment in user name array
799
    for (int i=0;i<lengthOfArray; i++)
800
    {
801 9705 leinfelder
        logMetacat.debug("Checking permission for principal: " + principals[i] );
802
        logMetacat.debug("SQL: " + pStmt.toString());
803
804 1425 tao
      pStmt.setString(2, principals[i]);
805
      pStmt.execute();
806
      rs=pStmt.getResultSet();
807
      while (rs.next())//check every entry for one user
808
      {
809
        permissionValueInTable=rs.getInt(1);
810 2245 sgarg
811
        //permission is ok
812 1425 tao
        //the user have a permission to access the file
813
        if (( permissionValueInTable & permissionValue )== permissionValue )
814
        {
815 2245 sgarg
816 1425 tao
           allow=true;//has allow rule entry
817
        }//if
818
      }//while
819
    }//for
820
   }//try
821
   catch (SQLException sqlE)
822
   {
823
     throw sqlE;
824
   }
825
   catch (Exception e)
826
   {
827
     throw e;
828
   }
829
   finally
830
   {
831
     try
832
     {
833
       pStmt.close();
834
     }
835
     finally
836
     {
837
       DBConnectionPool.returnDBConnection(conn, serialNumber);
838
     }
839
   }
840
    return allow;
841
 }//hasAllowRule
842 2245 sgarg
843
844
845 1425 tao
   /**
846 2245 sgarg
    * Check if the users array has explicit deny rules for given users, docid
847 1425 tao
    * and permission. That means the perm_type is deny and current time is
848
    * less than end_time and greater than begin time, or no time limit.
849 2245 sgarg
    * @param principals, list of names of principals to check for
850 1425 tao
    * @param docid, document identifier to check for
851
    * @param permission, the permssion need to check
852
    */
853 6744 leinfelder
  private boolean hasExplicitDenyRule(String [] principals,
854 1527 tao
                                      int permission, long startId)
855 1425 tao
                  throws SQLException
856
 {
857
   int lengthOfArray=principals.length;
858
   ResultSet rs;
859
   PreparedStatement pStmt = null;
860
   int permissionValue=permission;
861
   int permissionValueInTable;
862
   DBConnection conn = null;
863
   int serialNumber = -1;
864 1434 tao
   String sql = null;
865
   boolean topLevel = false;
866 2245 sgarg
867 1434 tao
   // decide top level or subtree level
868 1527 tao
   if (startId == TOPLEVELSTARTNODEID)
869 1434 tao
   {
870
     topLevel = true;
871 6744 leinfelder
     sql = "SELECT permission " +
872
     		"FROM xml_access " +
873
     		"WHERE guid = ? " +
874
	     	"AND lower(principal_name) = ? " +
875
	     	"AND perm_type = ? " +
876
	     	"AND startnodeid is NULL";
877 1434 tao
   }
878
   else
879
   {
880
     topLevel = false;
881 6744 leinfelder
     sql = "SELECT permission " +
882
		"FROM xml_access " +
883
		"WHERE guid = ? " +
884
	  	"AND lower(principal_name) = ? " +
885
	  	"AND perm_type = ? " +
886
	  	"AND startnodeid = ?";
887 1434 tao
   }
888 2245 sgarg
889 1425 tao
   try
890
   {
891
     //check out DBConnection
892 1434 tao
     conn=DBConnectionPool.getDBConnection("PermissionControl.hasExplicitDeny");
893 1425 tao
     serialNumber=conn.getCheckOutSerialNumber();
894 2245 sgarg
895 1434 tao
     pStmt = conn.prepareStatement(sql);
896 1425 tao
    //bind docid, perm_type
897 6744 leinfelder
    pStmt.setString(1, guid);
898 1425 tao
    pStmt.setString(3, AccessControlInterface.DENY);
899 2245 sgarg
900 1434 tao
    // subtree level need to set up subtreeid
901
    if (!topLevel)
902
    {
903 1527 tao
      pStmt.setLong(4, startId);
904 1434 tao
    }
905 2245 sgarg
906 1425 tao
    //bind every elenment in user name array
907
    for (int i=0;i<lengthOfArray; i++)
908
    {
909
      pStmt.setString(2, principals[i]);
910
      pStmt.execute();
911
      rs=pStmt.getResultSet();
912
      while (rs.next())//check every entry for one user
913
      {
914
        permissionValueInTable=rs.getInt(1);
915 2245 sgarg
916 1425 tao
        //permission is ok the user doesn't have permission to access the file
917
        if (( permissionValueInTable & permissionValue )== permissionValue )
918 2245 sgarg
919 1425 tao
        {
920
           pStmt.close();
921
           return true;
922
         }//if
923
      }//while
924
    }//for
925
   }//try
926
   catch (SQLException e)
927
   {
928
     throw e;
929
   }//catch
930
   finally
931
   {
932
     try
933
     {
934
       pStmt.close();
935
     }
936
     finally
937
     {
938
       DBConnectionPool.returnDBConnection(conn, serialNumber);
939
     }
940
   }//finally
941
   return false;//no deny rule
942 2245 sgarg
  }//hasExplicitDenyRule
943 1425 tao
944 2245 sgarg
945 1425 tao
  /**
946
    * Creat a users pakages to check permssion rule, user itself, public and
947
    * the gourps the user belong will be include in this package
948
    * @param user, the name of user
949
    * @param groups, the string array of the groups that user belong to
950
    */
951
  private String[] createUsersPackage(String user, String [] groups)
952
  {
953
    String [] usersPackage=null;
954
    int lengthOfPackage;
955 2245 sgarg
956 1425 tao
    if (groups!=null)
957
    {
958 2245 sgarg
      //if gouprs is not null and user is not public, we should create a array
959
      //to store the groups and user and public.
960 1425 tao
      //So the length of userPackage is the length of group plus two
961
      if (!user.equalsIgnoreCase(AccessControlInterface.PUBLIC))
962
      {
963
        lengthOfPackage=(groups.length)+2;
964
        usersPackage=new String [lengthOfPackage];
965
        //the first two elements is user self and public
966 2045 tao
        //in order to ignore case sensitive, we transfer user to lower case
967
        if (user != null)
968
        {
969
          usersPackage[0]= user.toLowerCase();
970 5311 daigle
          logMetacat.info("PermissionController.createUsersPackage - after transfer to lower case(not null): "+
971 2663 sgarg
                                     usersPackage[0]);
972 2045 tao
        }
973
        else
974
        {
975
          usersPackage[0] = user;
976
          usersPackage[0]= user.toLowerCase();
977 5311 daigle
          logMetacat.info("PermissionController.createUsersPackage - after transfer to lower case(null): "+
978 2663 sgarg
                                     usersPackage[0]);
979 2045 tao
        }
980 1425 tao
        usersPackage[1]=AccessControlInterface.PUBLIC;
981
        //put groups element from index 0 to lengthOfPackage-3 into userPackage
982
        //from index 2 to lengthOfPackage-1
983
        for (int i=2; i<lengthOfPackage; i++)
984
        {
985 2045 tao
          //tansfer group to lower case too
986
          if (groups[i-2] != null)
987
          {
988
            usersPackage[i]=groups[i-2].toLowerCase();
989
          }
990 1425 tao
        } //for
991
      }//if user!=public
992
      else//use=public
993
      {
994
        lengthOfPackage=(groups.length)+1;
995
        usersPackage=new String [lengthOfPackage];
996
        //the first lements is public
997
        usersPackage[0]=AccessControlInterface.PUBLIC;
998
        //put groups element from index 0 to lengthOfPackage-2 into userPackage
999
        //from index 1 to lengthOfPackage-1
1000
        for (int i=1; i<lengthOfPackage; i++)
1001
        {
1002 2045 tao
          if (groups[i-1] != null)
1003
          {
1004
            usersPackage[i]=groups[i-1].toLowerCase();
1005
          }
1006 1425 tao
        } //for
1007
      }//else user=public
1008 2245 sgarg
1009 1425 tao
    }//if groups!=null
1010
    else
1011
    {
1012
      //because no groups, the userPackage only need two elements
1013
      //one is for user, the other is for public
1014
      if (!user.equalsIgnoreCase(AccessControlInterface.PUBLIC))
1015
      {
1016
        lengthOfPackage=2;
1017
        usersPackage=new String [lengthOfPackage];
1018 2045 tao
        if (user != null)
1019
        {
1020
          usersPackage[0]=user.toLowerCase();
1021
        }
1022
        else
1023
        {
1024
          usersPackage[0]=user;
1025
        }
1026 1425 tao
        usersPackage[1]=AccessControlInterface.PUBLIC;
1027
      }//if user!=public
1028
      else //user==public
1029
      {
1030
        //only put public into array
1031
        lengthOfPackage=1;
1032
        usersPackage=new String [lengthOfPackage];
1033
        usersPackage[0]=AccessControlInterface.PUBLIC;
1034
      }
1035
    }//else groups==null
1036
    return usersPackage;
1037
  }//createUsersPackage
1038 2245 sgarg
1039
1040 1425 tao
  /**
1041 2245 sgarg
   * A static method to get Hashtable which cointains a inlinedata object list that
1042
   * user can't read it. The key is subtree id of inlinedata, the data is
1043
   * internal file name for the inline data which is stored as docid
1044
   * in xml_access table or data object doc id.
1045 6744 leinfelder
   * @param docid (With Rev), metadata docid which should be the accessfileid
1046 2245 sgarg
   *                         in the table
1047
   * @param user , the name of user
1048
   * @param groups, the group which the user belong to
1049 1425 tao
   */
1050 6744 leinfelder
   public static Hashtable<String, String> getUnReadableInlineDataIdList(String docid,
1051
                                                   String user, String[] groups)
1052 5298 jones
                                                throws McdbException
1053 2245 sgarg
   {
1054 6744 leinfelder
     Hashtable<String, String> inlineDataList = getUnAccessableInlineDataIdList(docid,
1055
                              user, groups, AccessControlInterface.READSTRING);
1056 2245 sgarg
1057
     return inlineDataList;
1058
   }
1059
1060
   /**
1061
  * A static method to get Hashtable which cointains a inline  data object list that
1062
  * user can't overwrite it. The key is subtree id of inline data distrubition,
1063
  * the value is internal file name for the inline data which is stored as docid
1064
  * in xml_access table or data object doc id.
1065
  * @param docidWithoutRev, metadata docid which should be the accessfileid
1066
  *                         in the table
1067
  * @param user , the name of user
1068
  * @param groups, the group which the user belong to
1069
  */
1070 4466 daigle
  public static Hashtable<String, String> getUnWritableInlineDataIdList(String docidWithoutRev,
1071 2291 sgarg
                                                  String user, String[] groups,
1072
                                                  boolean withRevision)
1073 2245 sgarg
                                               throws Exception
1074 1425 tao
  {
1075 4466 daigle
    Hashtable<String, String> inlineDataList = getUnAccessableInlineDataIdList(docidWithoutRev,
1076 6744 leinfelder
                            user, groups, AccessControlInterface.WRITESTRING);
1077 2245 sgarg
1078
    return inlineDataList;
1079
  }
1080
1081
1082 6744 leinfelder
   /**
1083 2245 sgarg
    * This method will get hashtable which contains a unaccessable distribution
1084
    * inlinedata object list
1085 2291 sgarg
    *
1086 2245 sgarg
    */
1087 4466 daigle
   private static Hashtable<String, String> getUnAccessableInlineDataIdList(String docid,
1088 6744 leinfelder
                               String user, String[] groups, String permission)
1089 5298 jones
                             throws McdbException
1090 2245 sgarg
   {
1091 5298 jones
       Hashtable<String, String> unAccessibleIdList = new Hashtable();
1092
       if (user == null) {
1093
           return unAccessibleIdList;
1094
       }
1095
1096
       Hashtable allIdList;
1097
       try {
1098
           allIdList = getAllInlineDataIdList(docid);
1099
       } catch (SQLException e) {
1100
           throw new McdbException(e.getMessage());
1101
       }
1102
       Enumeration<String> en = allIdList.keys();
1103 2245 sgarg
      while (en.hasMoreElements())
1104 1425 tao
      {
1105 2245 sgarg
        String subTreeId = (String) en.nextElement();
1106
        String fileId = (String) allIdList.get(subTreeId);
1107 6744 leinfelder
        //Here fileid is internal file id for line data. It stored in guid
1108
        // field in xml_access table.
1109
        PermissionController controller = new PermissionController(docid);
1110 2245 sgarg
        if (!controller.hasPermissionForInlineData(user, groups, permission, fileId))
1111
        {
1112 6744 leinfelder
1113
            logMetacat.info("PermissionController.getUnAccessableInlineDataIdList - Put subtree id " + subTreeId +
1114
                                     " and " + "inline data file name " +
1115
                                     fileId + " into " + "un" + permission +
1116
                                     " hash");
1117
            unAccessibleIdList.put(subTreeId, fileId);
1118 2291 sgarg
1119 6744 leinfelder
1120 2245 sgarg
        }
1121 1425 tao
      }
1122 4466 daigle
      return unAccessibleIdList;
1123 2245 sgarg
   }
1124
1125
1126
   /*
1127
    * This method will get a hash table from xml_access table for all records
1128
    * about the inline data. The key is subtree id and data is a inline internal
1129
    * file name
1130
    */
1131 4861 daigle
   private static Hashtable getAllInlineDataIdList(String docid) throws SQLException
1132 2245 sgarg
   {
1133 4861 daigle
     Hashtable inlineDataList = new Hashtable();
1134 6744 leinfelder
     String sql =
1135
    	 "SELECT subtreeid, guid " +
1136
    	 "FROM xml_access " +
1137
    	 "WHERE accessfileid = ? " +
1138
    	 "AND subtreeid IS NOT NULL";
1139 2245 sgarg
     PreparedStatement pStmt=null;
1140
     ResultSet rs=null;
1141
     DBConnection conn=null;
1142
     int serialNumber=-1;
1143
     try
1144
     {
1145
       //check out DBConnection
1146
       conn=DBConnectionPool.getDBConnection("PermissionControl.getDataSetId");
1147
       serialNumber=conn.getCheckOutSerialNumber();
1148
       pStmt=conn.prepareStatement(sql);
1149
       //bind the value to query
1150
       pStmt.setString(1, docid);
1151
       //execute the query
1152
       pStmt.execute();
1153
       rs=pStmt.getResultSet();
1154
       //process the result
1155
       while(rs.next())
1156
       {
1157
         String subTreeId = rs.getString(1);
1158
         String inlineDataId = rs.getString(2);
1159
         if (subTreeId != null && !subTreeId.trim().equals("") &&
1160
            inlineDataId != null && !inlineDataId.trim().equals(""))
1161
         {
1162
           inlineDataList.put(subTreeId, inlineDataId);
1163
         }
1164
      }//while
1165
     }//try
1166
     finally
1167
     {
1168
       try
1169
       {
1170
         pStmt.close();
1171
       }
1172
       finally
1173
       {
1174
         DBConnectionPool.returnDBConnection(conn, serialNumber);
1175
       }
1176
     }//finally
1177
     return inlineDataList;
1178
   }//getAllInlineDataIdList
1179 1425 tao
}