Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A class that handles checking permssision for a document
4
               and subtree in a document
5
 *
6
 *  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: leinfelder $'
11
 *     '$Date: 2012-12-12 14:38:02 -0800 (Wed, 12 Dec 2012) $'
12
 * '$Revision: 7475 $'
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

    
30
import java.sql.PreparedStatement;
31
import java.sql.ResultSet;
32
import java.sql.SQLException;
33
import java.util.Enumeration;
34
import java.util.Hashtable;
35
import java.util.Vector;
36

    
37
import org.apache.log4j.Logger;
38

    
39
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlList;
40
import edu.ucsb.nceas.metacat.database.DBConnection;
41
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
42
import edu.ucsb.nceas.metacat.properties.PropertyService;
43
import edu.ucsb.nceas.metacat.service.SessionService;
44
import edu.ucsb.nceas.metacat.shared.MetacatUtilException;
45
import edu.ucsb.nceas.metacat.util.AuthUtil;
46
import edu.ucsb.nceas.metacat.util.DocumentUtil;
47
import edu.ucsb.nceas.metacat.util.MetacatUtil;
48
import edu.ucsb.nceas.metacat.util.SessionData;
49
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
50
import edu.ucsb.nceas.utilities.access.AccessControlInterface;
51

    
52
public class PermissionController
53
{
54
//   private String docId = null;
55
//   private int rev = -1;
56
   
57
   private String guid = null;
58
   
59
   private boolean hasSubTreeAccessControl = false; // flag if has a subtree
60
                                                    // access for this docid
61
   private Vector subTreeList = new Vector();
62

    
63
   private static final long TOPLEVELSTARTNODEID = 0; //if start node is 0, means it is top
64
                                         //level document
65

    
66
   private static Logger logMetacat = Logger.getLogger(PermissionController.class);
67

    
68
   /**
69
	 * Constructor for PermissionController
70
	 * 
71
	 * @param myDocid the docid need to access
72
	 */
73
	public PermissionController(String myDocid) throws McdbException {
74

    
75
		// find the guid if we can
76
		String docId = null;
77
		int rev = -1;
78

    
79
		// get the parts
80
		docId = DocumentUtil.getSmartDocId(myDocid);
81
		rev = DocumentUtil.getRevisionFromAccessionNumber(myDocid);
82

    
83
		// this is what we really want
84
		guid = IdentifierManager.getInstance().getGUID(docId, rev);
85

    
86
	}
87

    
88
   /**
89
    * Return if a document has subtree access control
90
    */
91
   public boolean hasSubTreeAccessControl()
92
   {
93
     return hasSubTreeAccessControl;
94
   }
95

    
96
   public boolean hasPermission(String sessionId, String myPermission) throws SQLException {
97
       SessionData sessionData = null;
98
       sessionData = SessionService.getInstance().getRegisteredSession(sessionId);
99
       if (sessionData == null) {
100
           return false;
101
       }
102
	   
103
	   return hasPermission(sessionData.getUserName(), sessionData.getGroupNames(), myPermission); 
104
   }
105
   
106

    
107
  /**
108
    * Check from db connection if at least one of the list of @principals
109
    * Administrators are allowed all permission
110
    * @param user  the user name
111
    * @param groups  the groups which the use is in
112
    * @param myPermission  permission type to check for
113
    */
114
  public boolean hasPermission(String user, String[]groups, String myPermission)
115
                              throws SQLException //, Exception
116
  {
117
    boolean hasPermission=false;
118
    String [] userPackage=null;
119
    int permission = AccessControlList.intValue(myPermission);
120

    
121
    //for the command line invocation and replication
122
    if ((user==null) && (groups==null || groups.length==0))
123
    {
124
      return true;
125
    }
126
    
127
    // for administrators
128
    //see http://bugzilla.ecoinformatics.org/show_bug.cgi?id=4728
129
    try {
130
		if (AuthUtil.isAdministrator(user, groups)) {
131
			return true;
132
		}
133
	} catch (MetacatUtilException e) {
134
		// not much we can do here, except treat them as normal
135
		logMetacat.warn("Error checking for administrator: " + e.getMessage(), e);
136
	}
137

    
138
    //create a userpackage including user, public and group member
139
    userPackage=createUsersPackage(user, groups);
140

    
141
    //if the requested document is access documents and requested permission
142
    //is "write", the user should have "all" right
143

    
144
    if (isAccessDocument() && (permission == AccessControlInterface.WRITE))
145
    {
146

    
147
      hasPermission = hasPermission(userPackage, 7);// 7 is all permission
148
    }//if
149
    else //in other situation, just check the request permission
150
    {
151
      // Check for @permission on @docid for @user and/or @groups
152
      hasPermission = hasPermission(userPackage, permission);
153
    }//else
154

    
155
    return hasPermission;
156
  }
157

    
158

    
159
  /**
160
    * Check from db connection if the users in String array @principals has
161
    * @permission on @docid*
162
    * @param principals, names in userPakcage need to check for @permission
163
    * @param docid, document identifier to check on
164
    * @param permission, permission (write or all...) to check for
165
    */
166
  private boolean hasPermission(String [] principals, int permission)
167
                         throws SQLException
168
  {
169
    long startId = TOPLEVELSTARTNODEID;// this is for top level, so startid is 0
170
    try
171
    {
172
      //first, if there is a docid owner in user package, return true
173
      //because doc owner has all permssion
174
      if (containDocumentOwner(principals))
175
      {
176

    
177
          return true;
178
      }
179

    
180
      //If there is no owner in user package, checking the table
181
      //check perm_order
182
      if (isAllowFirst(principals, startId))
183
      {
184

    
185
        if (hasExplicitDenyRule(principals, permission, startId))
186
        {
187
          //if it is allowfirst and has deny rule(either explicit )
188
          //deny access
189

    
190
          return false;
191
        }//if
192
        else if ( hasAllowRule(principals, permission, startId))
193
        {
194
          //if it is allowfirst and hasn't deny rule and has allow rule
195
          //allow access
196

    
197
          return true;
198
        }//else if
199
        else
200
        {
201
          //other situation deny access
202

    
203
          return false;
204
        }//else
205
     }//if isAllowFirst
206
     else //denyFirst
207
     {
208
       if (hasAllowRule(principals, permission, startId))
209
       {
210
         //if it is denyFirst and has allow rule, allow access
211
         return true;
212
       }
213
       else
214
       {
215
         //if it is denyfirst but no allow rule, deny access
216
         return false;
217
       }
218
     }//else denyfirst
219
    }//try
220
    catch (Exception e)
221
    {
222
      logMetacat.warn("PermissionController.hasPermission - There is a exception in hasPermission method: "
223
                         +e.getMessage());
224
    }
225

    
226
    return false;
227
  }//hasPermission
228

    
229
  /**
230
   *  Method to check if a person has permission to a inline data file
231
   * @param user String
232
   * @param groups String[]
233
   * @param myPermission String
234
   * @param inlineDataId String
235
   * @throws McdbException
236
   * @return boolean
237
   */
238
  private boolean hasPermissionForInlineData(String user, String[] groups,
239
                                      String myPermission, String inlineDataId)
240
      throws McdbException
241
  {
242
     // this method can call the public method - hasPermission(...)
243
     // the only difference is about the ownership, you couldn't find the owner
244
     // from inlinedataId directly. You should get it from eml document itself
245
     String []userPackage = createUsersPackage(user, groups);
246
     try {
247
        if (containDocumentOwner(userPackage))
248
         {
249
           return true;
250
         }
251
         else
252
         {
253
        	 // is a funky inline data id with the extra part, so we set it manually
254
             PermissionController controller = new PermissionController(guid);
255
             controller.guid = inlineDataId;
256
             return controller.hasPermission(user, groups, myPermission);
257
         }
258
    } catch (SQLException e) {
259
        throw new McdbException(e.getMessage());
260
    }
261
  }
262

    
263
  /**
264
   * The method to determine of a node can be access by a user just by subtree
265
   * access control
266
   */
267
  public boolean hasPermissionForSubTreeNode(String user, String[] groups,
268
                                             String myPermission, long nodeId)
269
                                             throws McdbException
270
  {
271
    boolean flag = true;
272
    // Get unaccessble subtree for this user
273
    Hashtable unaccessableSubTree = hasUnaccessableSubTree(user, groups,
274
                                                           myPermission);
275
    Enumeration en = unaccessableSubTree.elements();
276
    while (en.hasMoreElements())
277
    {
278
      SubTree tree = (SubTree)en.nextElement();
279
      long start = tree.getStartNodeId();
280
      long stop  = tree.getEndNodeId();
281
      // nodeid in unaccessablesubtree, return false
282
      if ( nodeId >= start && nodeId <= stop)
283
      {
284
        flag = false;
285
        break;
286
      }
287
    }
288
    return flag;
289
  }
290
  /**
291
   * This method will return a hasTable of subtree which user doesn't has the
292
   * permssion to access
293
   * @param user  the user name
294
   * @param groups  the groups which the use is in
295
   * @param myPermission  permission type to check for
296
   */
297
  public Hashtable hasUnaccessableSubTree(String user, String[] groups,
298
                                       String myPermission) throws McdbException
299
  {
300
    Hashtable resultUnaccessableSubTree = new Hashtable();
301
    String [] principals=null;
302
    int permission =AccessControlList.intValue(myPermission);
303

    
304
    //for the commnad line invocation return null(no unaccessable subtree)
305
    if ((user==null) && (groups==null || groups.length==0))
306
    {
307
      return resultUnaccessableSubTree;
308
    }
309

    
310
    //create a userpackage including user, public and group member
311
    principals=createUsersPackage(user, groups);
312
    //for the document owner return null(no unaccessable subtree)
313
    try
314
    {
315
      if (containDocumentOwner(principals))
316
      {
317
       return resultUnaccessableSubTree;
318
      }
319
    }
320
    catch (SQLException ee)
321
    {
322
      throw new McdbException(ee);
323
    }
324

    
325
    // go through every subtree which has access control
326
    for (int i = 0; i< subTreeList.size(); i++)
327
    {
328
      SubTree tree = (SubTree)subTreeList.elementAt(i);
329
      long startId = tree.getStartNodeId();
330

    
331

    
332
        try
333
        {
334
          if (isAllowFirst(principals, startId))
335
          {
336

    
337
            if (hasExplicitDenyRule(principals, permission, startId ))
338
            {
339

    
340
             //if it is allowfirst and has deny rule
341
              // put the subtree into unaccessable vector
342
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
343
              {
344
                resultUnaccessableSubTree.put(new Long(startId), tree);
345
              }
346
            }//if
347
            else if ( hasAllowRule(principals, permission, startId))
348
            {
349
              //if it is allowfirst and hasn't deny rule and has allow rule
350
              //allow access do nothing
351

    
352
            }//else if
353
            else
354
            {
355
              //other situation deny access
356
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
357
              {
358
                resultUnaccessableSubTree.put(new Long(startId), tree);
359
              }
360

    
361
            }//else
362
          }//if isAllowFirst
363
          else //denyFirst
364
          {
365
            if (hasAllowRule(principals, permission,startId))
366
            {
367
              //if it is denyFirst and has allow rule, allow access, do nothing
368

    
369
            }
370
            else
371
            {
372
              //if it is denyfirst but no allow rule, deny access
373
              // add into vector
374
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
375
              {
376
                resultUnaccessableSubTree.put(new Long(startId), tree);
377
              }
378
            }
379
          }//else denyfirst
380
        }//try
381
        catch( Exception e)
382
        {
383
          logMetacat.error("PermissionController.hasUnaccessableSubTree - error in PermissionControl.has" +
384
                                   "UnaccessableSubTree "+e.getMessage());
385
          throw new McdbException(e);
386
        }
387

    
388
    }//for
389
    // merge the subtree if a subtree is another subtree'subtree
390
    resultUnaccessableSubTree = mergeEquivalentSubtree(resultUnaccessableSubTree);
391
    return resultUnaccessableSubTree;
392
  }//hasUnaccessableSubtree
393

    
394

    
395
  /*
396
   * A method to merge nested subtree into bigger one. For example subtree b
397
   * is a subtree of subtree a. And user doesn't have read permission for both
398
   * so we only use subtree a is enough.
399
   */
400
  private Hashtable mergeEquivalentSubtree(Hashtable unAccessSubTree)
401
  {
402
    Hashtable newSubTreeHash = new Hashtable();
403
    boolean   needDelete = false;
404
    // check the parameters
405
    if (unAccessSubTree == null || unAccessSubTree.isEmpty())
406
    {
407
      return newSubTreeHash;
408
    }
409
    else
410
    {
411
      // look every subtree start point and stop point, to see if it is embedded
412
      // in another one. If embedded, they are equavelent and we can use bigger
413
      // one to replace smaller one
414
      Enumeration en = unAccessSubTree.elements();
415
      while (en.hasMoreElements())
416
      {
417
        SubTree tree    = (SubTree)en.nextElement();
418
        String  treeId  = tree.getSubTreeId();
419
        long    startId = tree.getStartNodeId();
420
        long    endId   = tree.getEndNodeId();
421

    
422
        Enumeration enu = unAccessSubTree.elements();
423
        while (enu.hasMoreElements())
424
        {
425
          SubTree subTree = (SubTree)enu.nextElement();
426
          String subTreeId= subTree.getSubTreeId();
427
          long   subTreeStartId = subTree.getStartNodeId();
428
          long   subTreeEndId   = subTree.getEndNodeId();
429
          //compare and if the first subtree is a subtree of the second
430
          // one, set neeDelete true
431
          if (startId > subTreeStartId && endId < subTreeEndId)
432
          {
433
            needDelete = true;
434
            logMetacat.info("PermissionController.mergeEquivalentSubtree - the subtree: "+ treeId +
435
                                     " need to be get rid of from unaccessable"+
436
                                     " subtree list becuase it is a subtree of"+
437
                                     " another subtree in the list");
438
            break;
439
          }//if
440
        }//while
441
        // if not need to delete, put the subtree into hash
442
        if (!needDelete)
443
        {
444
          newSubTreeHash.put(new Long(startId), tree);
445
        }
446
        //reset needDelete
447
        needDelete = false;
448
      }//while
449
      return newSubTreeHash;
450
    }//else
451
  }
452

    
453
  /**
454
	 * Check if a document id is a access document. Access document need user
455
	 * has "all" permission to access it.
456
	 * 
457
	 * @param docId,
458
	 *            the document id need to be checked
459
	 */
460
	private boolean isAccessDocument() throws SQLException {
461
		// get the docid from the guid
462
		String docId = null;
463
		try {
464
			docId = IdentifierManager.getInstance().getLocalId(guid);
465
		} catch (McdbDocNotFoundException e) {
466
			return false;
467
			//throw new SQLException(e);
468
		}
469

    
470
		docId = DocumentUtil.getDocIdFromString(docId);
471
		PreparedStatement pStmt = null;
472
		DBConnection conn = null;
473
		int serialNumber = -1;
474
		try {
475
			// check out DBConnection
476
			conn = DBConnectionPool.getDBConnection("PermissionControl.isAccessDoc");
477
			serialNumber = conn.getCheckOutSerialNumber();
478
			pStmt = conn.prepareStatement("select doctype from xml_documents where docid like ? ");
479
			pStmt.setString(1, docId);
480
			pStmt.execute();
481
			ResultSet rs = pStmt.getResultSet();
482
			boolean hasRow = rs.next();
483
			String doctype = null;
484
			if (hasRow) {
485
				doctype = rs.getString(1);
486

    
487
			}
488
			pStmt.close();
489

    
490
			// if it is an access document
491
			if (doctype != null
492
					&& ((MetacatUtil.getOptionList(PropertyService
493
							.getProperty("xml.accessdoctype")).contains(doctype)))) {
494

    
495
				return true;
496
			}
497

    
498
		} catch (SQLException e) {
499
			throw new SQLException("PermissionControl.isAccessDocument "
500
					+ "Error checking" + " on document " + docId + ". " + e.getMessage());
501
		} catch (PropertyNotFoundException pnfe) {
502
			throw new SQLException("PermissionControl.isAccessDocument "
503
					+ "Error checking" + " on document " + docId + ". " + pnfe.getMessage());
504
		} finally {
505
			try {
506
				pStmt.close();
507
			} finally {
508
				DBConnectionPool.returnDBConnection(conn, serialNumber);
509
			}
510
		}
511

    
512
		return false;
513
	}// isAccessDocument
514

    
515

    
516

    
517
  /**
518
	 * Check if a stirng array contains a given documents' owner
519
	 * 
520
	 * @param principals,
521
	 *            a string array storing the username, groups name and public.
522
	 * @param docid,
523
	 *            the id of given documents
524
	 */
525
  private boolean containDocumentOwner(String[] principals)
526
                    throws SQLException
527
  {
528
	 
529
	// get the docid  
530
	String docId = null;
531
	try {
532
		docId = IdentifierManager.getInstance().getLocalId(guid);
533
		docId = DocumentUtil.getDocIdFromString(docId);
534
	} catch (McdbDocNotFoundException e) {
535
		// should be true if we own the parent doc, but likely won't be checked in that case
536
		return false;
537
	}
538
	
539
    int lengthOfArray=principals.length;
540
    boolean hasRow;
541
    PreparedStatement pStmt=null;
542
    DBConnection conn = null;
543
    int serialNumber = -1;
544

    
545
    try
546
    {
547
      //check out DBConnection
548
     conn=DBConnectionPool.getDBConnection("PermissionControl.containDocOnwer");
549
      serialNumber=conn.getCheckOutSerialNumber();
550
      pStmt = conn.prepareStatement(
551
                "SELECT 'x' FROM xml_documents " +
552
                "WHERE docid = ? AND lower(user_owner) = ? " +
553
                "UNION ALL " +
554
                "SELECT 'x' FROM xml_revisions " +
555
                "WHERE docid = ? AND lower(user_owner) = ? ");
556
      //check every element in the string array too see if it conatains
557
      //the owner of document
558
      for (int i=0; i<lengthOfArray; i++)
559
      {
560

    
561
        // Bind the values to the query
562
        pStmt.setString(1, docId);
563
        pStmt.setString(2, principals[i]);
564
        pStmt.setString(3, docId);
565
        pStmt.setString(4, principals[i]);
566
        logMetacat.info("PermissionController.containDocumentOwner - the principle stack is : " +
567
                                  principals[i]);
568

    
569
        pStmt.execute();
570
        ResultSet rs = pStmt.getResultSet();
571
        hasRow = rs.next();
572
        if (hasRow)
573
        {
574
          pStmt.close();
575
           logMetacat.info("PermissionController.containDocumentOwner - find the owner");
576
          return true;
577
        }//if
578

    
579
      }//for
580
    }//try
581
    catch (SQLException e)
582
    {
583
        pStmt.close();
584

    
585
        throw new
586
        SQLException("PermissionControl.hasPermission - " +
587
                     "Error checking ownership for " + principals[0] +
588
                     " on document #" + docId + ". " + e.getMessage());
589
    }//catch
590
    finally
591
    {
592
      try
593
      {
594
        pStmt.close();
595
      }
596
      finally
597
      {
598
        DBConnectionPool.returnDBConnection(conn, serialNumber);
599
      }
600
    }
601
    return false;
602
  }//containDocumentOwner
603

    
604
  /**
605
    * Check if the permission order for user at that documents is allowFirst
606
    * @param principals, list of names of principals to check for
607
    * @param docid, document identifier to check for
608
    */
609
  private boolean isAllowFirst(String [] principals, long startId)
610
                  throws SQLException, Exception
611
  {
612
    int lengthOfArray=principals.length;
613
    boolean hasRow;
614
    PreparedStatement pStmt = null;
615
    DBConnection conn = null;
616
    int serialNumber = -1;
617
    String sql = null;
618
    boolean topLever =false;
619
    if (startId == TOPLEVELSTARTNODEID)
620
    {
621
      //top level
622
      topLever = true;
623
      sql = 
624
    	  "SELECT perm_order FROM xml_access " +
625
		    "WHERE lower(principal_name) = ? " +
626
		    "AND guid = ? " +
627
		    "AND startnodeid is NULL";
628
    }
629
    else
630
    {
631
      //sub tree level
632
      sql = "SELECT perm_order FROM xml_access " +
633
        "WHERE lower(principal_name) = ? " +
634
        "AND guid = ? " +
635
        "AND startnodeid = ?";
636
    }
637

    
638
    try
639
    {
640
      //check out DBConnection
641
      conn=DBConnectionPool.getDBConnection("AccessControlList.isAllowFirst");
642
      serialNumber=conn.getCheckOutSerialNumber();
643

    
644
      //select permission order from database
645
      pStmt = conn.prepareStatement(sql);
646

    
647
      //check every name in the array
648
      for (int i=0; i<lengthOfArray;i++)
649
      {
650
        //bind value
651
        pStmt.setString(1, principals[i]);//user name
652
        pStmt.setString(2, guid);//guid
653
        
654
        // if subtree, we need set subtree id
655
        if (!topLever)
656
        {
657
          pStmt.setLong(3, startId);
658
        }
659

    
660
        pStmt.execute();
661
        ResultSet rs = pStmt.getResultSet();
662
        hasRow=rs.next();
663
        if (hasRow)
664
        {
665
          //get the permission order from data base
666
          String permissionOrder=rs.getString(1);
667
          //if the permission order is "allowFirst
668
          if (permissionOrder.equalsIgnoreCase(AccessControlInterface.ALLOWFIRST))
669
          {
670
            pStmt.close();
671
            return true;
672
          }
673
          else
674
          {
675
            pStmt.close();
676
            return false;
677
          }
678
        }//if
679
      }//for
680
    }//try
681
    catch (SQLException e)
682
    {
683
      throw e;
684
    }
685
    finally
686
    {
687
      try
688
      {
689
        pStmt.close();
690
      }
691
      finally
692
      {
693
        DBConnectionPool.returnDBConnection(conn, serialNumber);
694
      }
695
    }
696

    
697
    //if reach here, means there is no permssion record for given names and
698
    //docid. So throw a exception.
699

    
700
    throw new Exception("PermissionController.isAllowFirst - There is no permission record for user "+ principals[0] + 
701
                        " at document " + guid);
702

    
703
  }//isAllowFirst
704

    
705
  /**
706
    * Check if the users array has allow rules for given users, docid and
707
    * permission.
708
    * If it has permission rule and ticket count is greater than 0, the ticket
709
    * number will decrease one for every allow rule
710
    * @param principals, list of names of principals to check for
711
    * @param docid, document identifier to check for
712
    * @param permission, the permssion need to check
713
    */
714
  private boolean hasAllowRule(String [] principals, int permission, long startId)
715
                  throws SQLException, Exception
716
 {
717
   int lengthOfArray=principals.length;
718
   boolean allow=false;//initial value is no allow rule
719
   ResultSet rs;
720
   PreparedStatement pStmt = null;
721
   int permissionValue=permission;
722
   int permissionValueInTable;
723
   DBConnection conn = null;
724
   int serialNumber = -1;
725
   boolean topLever = false;
726
   String sql = null;
727
   if (startId == TOPLEVELSTARTNODEID)
728
   {
729
     // for toplevel
730
     topLever = true;
731
     sql = "SELECT permission " +
732
		"FROM xml_access " +
733
 		"WHERE guid = ? " +
734
 		"AND lower(principal_name) = ? " +
735
 		"AND perm_type = ? " +
736
 		"AND startnodeid is NULL";
737
   }
738
   else
739
   {
740
     topLever =false;
741
     sql = "SELECT permission " +
742
     		"FROM xml_access " +
743
     		"WHERE guid = ? " +
744
     		"AND lower(principal_name) = ? " +
745
     		"AND perm_type = ? " +
746
     		"AND startnodeid = ?";
747
   }
748
   try
749
   {
750
     //check out DBConnection
751
     conn=DBConnectionPool.getDBConnection("AccessControlList.hasAllowRule");
752
     serialNumber=conn.getCheckOutSerialNumber();
753
    //This sql statement will select entry with
754
    //begin_time<=currentTime<=end_time in xml_access table
755
    //If begin_time or end_time is null in table, isnull(begin_time, sysdate)
756
    //function will assign begin_time=sysdate
757
    pStmt = conn.prepareStatement(sql);
758
    //bind docid, perm_type
759
    pStmt.setString(1, guid);
760
    pStmt.setString(3, AccessControlInterface.ALLOW);
761

    
762
    // if subtree lever, need to set subTreeId
763
    if (!topLever)
764
    {
765
      pStmt.setLong(4, startId);
766
    }
767

    
768
    //bind every elenment in user name array
769
    for (int i=0;i<lengthOfArray; i++)
770
    {
771
      pStmt.setString(2, principals[i]);
772
      pStmt.execute();
773
      rs=pStmt.getResultSet();
774
      while (rs.next())//check every entry for one user
775
      {
776
        permissionValueInTable=rs.getInt(1);
777

    
778
        //permission is ok
779
        //the user have a permission to access the file
780
        if (( permissionValueInTable & permissionValue )== permissionValue )
781
        {
782

    
783
           allow=true;//has allow rule entry
784
        }//if
785
      }//while
786
    }//for
787
   }//try
788
   catch (SQLException sqlE)
789
   {
790
     throw sqlE;
791
   }
792
   catch (Exception e)
793
   {
794
     throw e;
795
   }
796
   finally
797
   {
798
     try
799
     {
800
       pStmt.close();
801
     }
802
     finally
803
     {
804
       DBConnectionPool.returnDBConnection(conn, serialNumber);
805
     }
806
   }
807
    return allow;
808
 }//hasAllowRule
809

    
810

    
811

    
812
   /**
813
    * Check if the users array has explicit deny rules for given users, docid
814
    * and permission. That means the perm_type is deny and current time is
815
    * less than end_time and greater than begin time, or no time limit.
816
    * @param principals, list of names of principals to check for
817
    * @param docid, document identifier to check for
818
    * @param permission, the permssion need to check
819
    */
820
  private boolean hasExplicitDenyRule(String [] principals,
821
                                      int permission, long startId)
822
                  throws SQLException
823
 {
824
   int lengthOfArray=principals.length;
825
   ResultSet rs;
826
   PreparedStatement pStmt = null;
827
   int permissionValue=permission;
828
   int permissionValueInTable;
829
   DBConnection conn = null;
830
   int serialNumber = -1;
831
   String sql = null;
832
   boolean topLevel = false;
833

    
834
   // decide top level or subtree level
835
   if (startId == TOPLEVELSTARTNODEID)
836
   {
837
     topLevel = true;
838
     sql = "SELECT permission " +
839
     		"FROM xml_access " +
840
     		"WHERE guid = ? " +
841
	     	"AND lower(principal_name) = ? " +
842
	     	"AND perm_type = ? " +
843
	     	"AND startnodeid is NULL";
844
   }
845
   else
846
   {
847
     topLevel = false;
848
     sql = "SELECT permission " +
849
		"FROM xml_access " +
850
		"WHERE guid = ? " +
851
	  	"AND lower(principal_name) = ? " +
852
	  	"AND perm_type = ? " +
853
	  	"AND startnodeid = ?";
854
   }
855

    
856
   try
857
   {
858
     //check out DBConnection
859
     conn=DBConnectionPool.getDBConnection("PermissionControl.hasExplicitDeny");
860
     serialNumber=conn.getCheckOutSerialNumber();
861

    
862
     pStmt = conn.prepareStatement(sql);
863
    //bind docid, perm_type
864
    pStmt.setString(1, guid);
865
    pStmt.setString(3, AccessControlInterface.DENY);
866

    
867
    // subtree level need to set up subtreeid
868
    if (!topLevel)
869
    {
870
      pStmt.setLong(4, startId);
871
    }
872

    
873
    //bind every elenment in user name array
874
    for (int i=0;i<lengthOfArray; i++)
875
    {
876
      pStmt.setString(2, principals[i]);
877
      pStmt.execute();
878
      rs=pStmt.getResultSet();
879
      while (rs.next())//check every entry for one user
880
      {
881
        permissionValueInTable=rs.getInt(1);
882

    
883
        //permission is ok the user doesn't have permission to access the file
884
        if (( permissionValueInTable & permissionValue )== permissionValue )
885

    
886
        {
887
           pStmt.close();
888
           return true;
889
         }//if
890
      }//while
891
    }//for
892
   }//try
893
   catch (SQLException e)
894
   {
895
     throw e;
896
   }//catch
897
   finally
898
   {
899
     try
900
     {
901
       pStmt.close();
902
     }
903
     finally
904
     {
905
       DBConnectionPool.returnDBConnection(conn, serialNumber);
906
     }
907
   }//finally
908
   return false;//no deny rule
909
  }//hasExplicitDenyRule
910

    
911

    
912
  /**
913
    * Creat a users pakages to check permssion rule, user itself, public and
914
    * the gourps the user belong will be include in this package
915
    * @param user, the name of user
916
    * @param groups, the string array of the groups that user belong to
917
    */
918
  private String[] createUsersPackage(String user, String [] groups)
919
  {
920
    String [] usersPackage=null;
921
    int lengthOfPackage;
922

    
923
    if (groups!=null)
924
    {
925
      //if gouprs is not null and user is not public, we should create a array
926
      //to store the groups and user and public.
927
      //So the length of userPackage is the length of group plus two
928
      if (!user.equalsIgnoreCase(AccessControlInterface.PUBLIC))
929
      {
930
        lengthOfPackage=(groups.length)+2;
931
        usersPackage=new String [lengthOfPackage];
932
        //the first two elements is user self and public
933
        //in order to ignore case sensitive, we transfer user to lower case
934
        if (user != null)
935
        {
936
          usersPackage[0]= user.toLowerCase();
937
          logMetacat.info("PermissionController.createUsersPackage - after transfer to lower case(not null): "+
938
                                     usersPackage[0]);
939
        }
940
        else
941
        {
942
          usersPackage[0] = user;
943
          usersPackage[0]= user.toLowerCase();
944
          logMetacat.info("PermissionController.createUsersPackage - after transfer to lower case(null): "+
945
                                     usersPackage[0]);
946
        }
947
        usersPackage[1]=AccessControlInterface.PUBLIC;
948
        //put groups element from index 0 to lengthOfPackage-3 into userPackage
949
        //from index 2 to lengthOfPackage-1
950
        for (int i=2; i<lengthOfPackage; i++)
951
        {
952
          //tansfer group to lower case too
953
          if (groups[i-2] != null)
954
          {
955
            usersPackage[i]=groups[i-2].toLowerCase();
956
          }
957
        } //for
958
      }//if user!=public
959
      else//use=public
960
      {
961
        lengthOfPackage=(groups.length)+1;
962
        usersPackage=new String [lengthOfPackage];
963
        //the first lements is public
964
        usersPackage[0]=AccessControlInterface.PUBLIC;
965
        //put groups element from index 0 to lengthOfPackage-2 into userPackage
966
        //from index 1 to lengthOfPackage-1
967
        for (int i=1; i<lengthOfPackage; i++)
968
        {
969
          if (groups[i-1] != null)
970
          {
971
            usersPackage[i]=groups[i-1].toLowerCase();
972
          }
973
        } //for
974
      }//else user=public
975

    
976
    }//if groups!=null
977
    else
978
    {
979
      //because no groups, the userPackage only need two elements
980
      //one is for user, the other is for public
981
      if (!user.equalsIgnoreCase(AccessControlInterface.PUBLIC))
982
      {
983
        lengthOfPackage=2;
984
        usersPackage=new String [lengthOfPackage];
985
        if (user != null)
986
        {
987
          usersPackage[0]=user.toLowerCase();
988
        }
989
        else
990
        {
991
          usersPackage[0]=user;
992
        }
993
        usersPackage[1]=AccessControlInterface.PUBLIC;
994
      }//if user!=public
995
      else //user==public
996
      {
997
        //only put public into array
998
        lengthOfPackage=1;
999
        usersPackage=new String [lengthOfPackage];
1000
        usersPackage[0]=AccessControlInterface.PUBLIC;
1001
      }
1002
    }//else groups==null
1003
    return usersPackage;
1004
  }//createUsersPackage
1005

    
1006

    
1007
  /**
1008
   * A static method to get Hashtable which cointains a inlinedata object list that
1009
   * user can't read it. The key is subtree id of inlinedata, the data is
1010
   * internal file name for the inline data which is stored as docid
1011
   * in xml_access table or data object doc id.
1012
   * @param docid (With Rev), metadata docid which should be the accessfileid
1013
   *                         in the table
1014
   * @param user , the name of user
1015
   * @param groups, the group which the user belong to
1016
   */
1017
   public static Hashtable<String, String> getUnReadableInlineDataIdList(String docid,
1018
                                                   String user, String[] groups)
1019
                                                throws McdbException
1020
   {
1021
     Hashtable<String, String> inlineDataList = getUnAccessableInlineDataIdList(docid,
1022
                              user, groups, AccessControlInterface.READSTRING);
1023

    
1024
     return inlineDataList;
1025
   }
1026

    
1027
   /**
1028
  * A static method to get Hashtable which cointains a inline  data object list that
1029
  * user can't overwrite it. The key is subtree id of inline data distrubition,
1030
  * the value is internal file name for the inline data which is stored as docid
1031
  * in xml_access table or data object doc id.
1032
  * @param docidWithoutRev, metadata docid which should be the accessfileid
1033
  *                         in the table
1034
  * @param user , the name of user
1035
  * @param groups, the group which the user belong to
1036
  */
1037
  public static Hashtable<String, String> getUnWritableInlineDataIdList(String docidWithoutRev,
1038
                                                  String user, String[] groups,
1039
                                                  boolean withRevision)
1040
                                               throws Exception
1041
  {
1042
    Hashtable<String, String> inlineDataList = getUnAccessableInlineDataIdList(docidWithoutRev,
1043
                            user, groups, AccessControlInterface.WRITESTRING);
1044

    
1045
    return inlineDataList;
1046
  }
1047

    
1048

    
1049
   /**
1050
    * This method will get hashtable which contains a unaccessable distribution
1051
    * inlinedata object list
1052
    *
1053
    */
1054
   private static Hashtable<String, String> getUnAccessableInlineDataIdList(String docid,
1055
                               String user, String[] groups, String permission)
1056
                             throws McdbException
1057
   {
1058
       Hashtable<String, String> unAccessibleIdList = new Hashtable();
1059
       if (user == null) {
1060
           return unAccessibleIdList;
1061
       }
1062

    
1063
       Hashtable allIdList;
1064
       try {
1065
           allIdList = getAllInlineDataIdList(docid);
1066
       } catch (SQLException e) {
1067
           throw new McdbException(e.getMessage());
1068
       }
1069
       Enumeration<String> en = allIdList.keys();
1070
      while (en.hasMoreElements())
1071
      {
1072
        String subTreeId = (String) en.nextElement();
1073
        String fileId = (String) allIdList.get(subTreeId);
1074
        //Here fileid is internal file id for line data. It stored in guid
1075
        // field in xml_access table. 
1076
        PermissionController controller = new PermissionController(docid);
1077
        if (!controller.hasPermissionForInlineData(user, groups, permission, fileId))
1078
        {
1079
            
1080
            logMetacat.info("PermissionController.getUnAccessableInlineDataIdList - Put subtree id " + subTreeId +
1081
                                     " and " + "inline data file name " +
1082
                                     fileId + " into " + "un" + permission +
1083
                                     " hash");
1084
            unAccessibleIdList.put(subTreeId, fileId);
1085

    
1086
                        
1087
        }
1088
      }
1089
      return unAccessibleIdList;
1090
   }
1091

    
1092

    
1093
   /*
1094
    * This method will get a hash table from xml_access table for all records
1095
    * about the inline data. The key is subtree id and data is a inline internal
1096
    * file name
1097
    */
1098
   private static Hashtable getAllInlineDataIdList(String docid) throws SQLException
1099
   {
1100
     Hashtable inlineDataList = new Hashtable();
1101
     String sql = 
1102
    	 "SELECT subtreeid, guid " +
1103
    	 "FROM xml_access " +
1104
    	 "WHERE accessfileid = ? " +
1105
    	 "AND subtreeid IS NOT NULL";
1106
     PreparedStatement pStmt=null;
1107
     ResultSet rs=null;
1108
     DBConnection conn=null;
1109
     int serialNumber=-1;
1110
     try
1111
     {
1112
       //check out DBConnection
1113
       conn=DBConnectionPool.getDBConnection("PermissionControl.getDataSetId");
1114
       serialNumber=conn.getCheckOutSerialNumber();
1115
       pStmt=conn.prepareStatement(sql);
1116
       //bind the value to query
1117
       pStmt.setString(1, docid);
1118
       //execute the query
1119
       pStmt.execute();
1120
       rs=pStmt.getResultSet();
1121
       //process the result
1122
       while(rs.next())
1123
       {
1124
         String subTreeId = rs.getString(1);
1125
         String inlineDataId = rs.getString(2);
1126
         if (subTreeId != null && !subTreeId.trim().equals("") &&
1127
            inlineDataId != null && !inlineDataId.trim().equals(""))
1128
         {
1129
           inlineDataList.put(subTreeId, inlineDataId);
1130
         }
1131
      }//while
1132
     }//try
1133
     finally
1134
     {
1135
       try
1136
       {
1137
         pStmt.close();
1138
       }
1139
       finally
1140
       {
1141
         DBConnectionPool.returnDBConnection(conn, serialNumber);
1142
       }
1143
     }//finally
1144
     return inlineDataList;
1145
   }//getAllInlineDataIdList
1146
}
(51-51/63)