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: daigle $'
11
 *     '$Date: 2009-10-02 13:11:46 -0700 (Fri, 02 Oct 2009) $'
12
 * '$Revision: 5072 $'
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.*;
31
import java.util.Enumeration;
32
import java.util.Hashtable;
33
import java.util.Stack;
34
import java.util.Vector;
35
import java.util.Iterator;
36

    
37
import org.apache.log4j.Logger;
38

    
39
import edu.ucsb.nceas.metacat.database.DBConnection;
40
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
41
import edu.ucsb.nceas.metacat.properties.PropertyService;
42
import edu.ucsb.nceas.metacat.service.SessionService;
43
import edu.ucsb.nceas.metacat.util.DocumentUtil;
44
import edu.ucsb.nceas.metacat.util.MetacatUtil;
45
import edu.ucsb.nceas.metacat.util.SessionData;
46
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
47

    
48
public class PermissionController
49
{
50
   private String docId = null;
51
   private boolean hasSubTreeAccessControl = false; // flag if has a subtree
52
                                                    // access for this docid
53
   private Vector subTreeList = new Vector();
54

    
55
   private long TOPLEVELSTARTNODEID = 0; //if start node is 0, means it is top
56
                                         //level document
57

    
58
   private static Logger logMetacat = Logger.getLogger(PermissionController.class);
59

    
60
   /**
61
    * Constructor for PermissionController
62
    * @param myDocid      the docid need to access
63
    */
64
   public PermissionController(String myDocid) throws McdbException
65
   {
66
     // Get rid of rev number
67
     docId = DocumentUtil.getSmartDocId(myDocid);
68
     //hasSubTreeAccessControl = checkSubTreeAccessControl();
69
   }
70

    
71
   /**
72
    * Constructor for PermssionController
73
    * @param myDocid String
74
    * @param needDeleteRev boolean
75
    */
76
   public PermissionController(String myDocid, boolean needDeleteRevFromDocid)
77
   {
78
     if (!needDeleteRevFromDocid)
79
     {
80
       docId = myDocid;
81
     }
82
     else
83
     {
84
         docId = DocumentUtil.getDocIdFromAccessionNumber(myDocid);
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 = SessionService.getRegisteredSession(sessionId);
98
	   if (sessionData == null) {
99
		   return false;
100
	   }
101
	   
102
	   return hasPermission(sessionData.getUserName(), sessionData.getGroupNames(), myPermission); 
103
   }
104
   
105

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

    
119
    //for the commnad line invocation
120
    if ((user==null) && (groups==null || groups.length==0))
121
    {
122
      return true;
123
    }
124

    
125
    //create a userpackage including user, public and group member
126
    userPackage=createUsersPackage(user, groups);
127

    
128
    //if the requested document is access documents and requested permission
129
    //is "write", the user should have "all" right
130

    
131
    if (isAccessDocument(docId) && (permission == AccessControlInterface.WRITE))
132
    {
133

    
134
      hasPermission = hasPermission(userPackage,docId, 7);// 7 is all permission
135
    }//if
136
    else //in other situation, just check the request permission
137
    {
138

    
139

    
140
      // Check for @permission on @docid for @user and/or @groups
141
      hasPermission = hasPermission(userPackage,docId, permission);
142

    
143
    }//else
144

    
145
    return hasPermission;
146
  }
147

    
148

    
149
  /**
150
    * Check from db connection if the users in String array @principals has
151
    * @permission on @docid*
152
    * @param principals, names in userPakcage need to check for @permission
153
    * @param docid, document identifier to check on
154
    * @param permission, permission (write or all...) to check for
155
    */
156
  private boolean hasPermission(String [] principals, String docId,
157
                                           int permission)
158
                         throws SQLException
159
  {
160
    long startId = TOPLEVELSTARTNODEID;// this is for top level, so startid is 0
161
    try
162
    {
163
      //first, if there is a docid owner in user package, return true
164
      //because doc owner has all permssion
165
      if (containDocumentOwner(principals, docId))
166
      {
167

    
168
          return true;
169
      }
170

    
171
      //If there is no owner in user package, checking the table
172
      //check perm_order
173
      if (isAllowFirst(principals, docId, startId))
174
      {
175

    
176
        if (hasExplicitDenyRule(principals, docId, permission, startId))
177
        {
178
          //if it is allowfirst and has deny rule(either explicit )
179
          //deny access
180

    
181
          return false;
182
        }//if
183
        else if ( hasAllowRule(principals, docId, permission, startId))
184
        {
185
          //if it is allowfirst and hasn't deny rule and has allow rule
186
          //allow access
187

    
188
          return true;
189
        }//else if
190
        else
191
        {
192
          //other situation deny access
193

    
194
          return false;
195
        }//else
196
     }//if isAllowFirst
197
     else //denyFirst
198
     {
199
       if (hasAllowRule(principals, docId, permission, startId))
200
       {
201
         //if it is denyFirst and has allow rule, allow access
202
         return true;
203
       }
204
       else
205
       {
206
         //if it is denyfirst but no allow rule, deny access
207
         return false;
208
       }
209
     }//else denyfirst
210
    }//try
211
    catch (Exception e)
212
    {
213
      logMetacat.warn("There is a exception in hasPermission method: "
214
                         +e.getMessage());
215
    }
216

    
217
    return false;
218
  }//hasPermission
219

    
220
  /**
221
   *  Method to check if a person has permission to a inline data file
222
   * @param user String
223
   * @param groups String[]
224
   * @param myPermission String
225
   * @param inlineDataId String
226
   * @throws McdbException
227
   * @return boolean
228
   */
229
  private boolean hasPermissionForInlineData(String user, String[] groups,
230
                                      String myPermission, String inlineDataId)
231
                                      throws Exception
232
  {
233
     // this method can call the public method - hasPermission(...)
234
     // the only difference is about the ownership, you couldn't find the owner
235
     // from inlinedataId directly. You should get it from eml document itself
236
     String []userPackage = createUsersPackage(user, groups);
237
     if (containDocumentOwner(userPackage, docId))
238
     {
239
       return true;
240
     }
241
     else
242
     {
243
         PermissionController controller =
244
                               new PermissionController(inlineDataId, false);
245
         return controller.hasPermission(user, groups, myPermission);
246
     }
247
  }
248

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

    
290
    //for the commnad line invocation return null(no unaccessable subtree)
291
    if ((user==null) && (groups==null || groups.length==0))
292
    {
293
      return resultUnaccessableSubTree;
294
    }
295

    
296
    //create a userpackage including user, public and group member
297
    principals=createUsersPackage(user, groups);
298
    //for the document owner return null(no unaccessable subtree)
299
    try
300
    {
301
      if (containDocumentOwner(principals, docId))
302
      {
303
       return resultUnaccessableSubTree;
304
      }
305
    }
306
    catch (SQLException ee)
307
    {
308
      throw new McdbException(ee);
309
    }
310

    
311
    // go through every subtree which has access control
312
    for (int i = 0; i< subTreeList.size(); i++)
313
    {
314
      SubTree tree = (SubTree)subTreeList.elementAt(i);
315
      long startId = tree.getStartNodeId();
316

    
317

    
318
        try
319
        {
320
          if (isAllowFirst(principals, docId, startId))
321
          {
322

    
323
            if (hasExplicitDenyRule(principals, docId, permission, startId ))
324
            {
325

    
326
             //if it is allowfirst and has deny rule
327
              // put the subtree into unaccessable vector
328
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
329
              {
330
                resultUnaccessableSubTree.put(new Long(startId), tree);
331
              }
332
            }//if
333
            else if ( hasAllowRule(principals, docId, permission, startId))
334
            {
335
              //if it is allowfirst and hasn't deny rule and has allow rule
336
              //allow access do nothing
337

    
338
            }//else if
339
            else
340
            {
341
              //other situation deny access
342
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
343
              {
344
                resultUnaccessableSubTree.put(new Long(startId), tree);
345
              }
346

    
347
            }//else
348
          }//if isAllowFirst
349
          else //denyFirst
350
          {
351
            if (hasAllowRule(principals, docId, permission,startId))
352
            {
353
              //if it is denyFirst and has allow rule, allow access, do nothing
354

    
355
            }
356
            else
357
            {
358
              //if it is denyfirst but no allow rule, deny access
359
              // add into vector
360
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
361
              {
362
                resultUnaccessableSubTree.put(new Long(startId), tree);
363
              }
364
            }
365
          }//else denyfirst
366
        }//try
367
        catch( Exception e)
368
        {
369
          logMetacat.error("error in PermissionControl.has" +
370
                                   "UnaccessableSubTree "+e.getMessage());
371
          throw new McdbException(e);
372
        }
373

    
374
    }//for
375
    // merge the subtree if a subtree is another subtree'subtree
376
    resultUnaccessableSubTree = mergeEquivalentSubtree(resultUnaccessableSubTree);
377
    return resultUnaccessableSubTree;
378
  }//hasUnaccessableSubtree
379

    
380

    
381
  /*
382
   * A method to merge nested subtree into bigger one. For example subtree b
383
   * is a subtree of subtree a. And user doesn't have read permission for both
384
   * so we only use subtree a is enough.
385
   */
386
  private Hashtable mergeEquivalentSubtree(Hashtable unAccessSubTree)
387
  {
388
    Hashtable newSubTreeHash = new Hashtable();
389
    boolean   needDelete = false;
390
    // check the parameters
391
    if (unAccessSubTree == null || unAccessSubTree.isEmpty())
392
    {
393
      return newSubTreeHash;
394
    }
395
    else
396
    {
397
      // look every subtree start point and stop point, to see if it is embedded
398
      // in another one. If embedded, they are equavelent and we can use bigger
399
      // one to replace smaller one
400
      Enumeration en = unAccessSubTree.elements();
401
      while (en.hasMoreElements())
402
      {
403
        SubTree tree    = (SubTree)en.nextElement();
404
        String  treeId  = tree.getSubTreeId();
405
        long    startId = tree.getStartNodeId();
406
        long    endId   = tree.getEndNodeId();
407

    
408
        Enumeration enu = unAccessSubTree.elements();
409
        while (enu.hasMoreElements())
410
        {
411
          SubTree subTree = (SubTree)enu.nextElement();
412
          String subTreeId= subTree.getSubTreeId();
413
          long   subTreeStartId = subTree.getStartNodeId();
414
          long   subTreeEndId   = subTree.getEndNodeId();
415
          //compare and if the first subtree is a subtree of the second
416
          // one, set neeDelete true
417
          if (startId > subTreeStartId && endId < subTreeEndId)
418
          {
419
            needDelete = true;
420
            logMetacat.info("the subtree: "+ treeId +
421
                                     " need to be get rid of from unaccessable"+
422
                                     " subtree list becuase it is a subtree of"+
423
                                     " another subtree in the list");
424
            break;
425
          }//if
426
        }//while
427
        // if not need to delete, put the subtree into hash
428
        if (!needDelete)
429
        {
430
          newSubTreeHash.put(new Long(startId), tree);
431
        }
432
        //reset needDelete
433
        needDelete = false;
434
      }//while
435
      return newSubTreeHash;
436
    }//else
437
  }
438

    
439
  /**
440
	 * Check if a document id is a access document. Access document need user
441
	 * has "all" permission to access it.
442
	 * 
443
	 * @param docId,
444
	 *            the document id need to be checked
445
	 */
446
	private boolean isAccessDocument(String docId) throws SQLException {
447
		// detele the rev number if docid contains it
448
		docId = DocumentUtil.getDocIdFromString(docId);
449
		PreparedStatement pStmt = null;
450
		DBConnection conn = null;
451
		int serialNumber = -1;
452
		try {
453
			// check out DBConnection
454
			conn = DBConnectionPool.getDBConnection("PermissionControl.isAccessDoc");
455
			serialNumber = conn.getCheckOutSerialNumber();
456
			pStmt = conn.prepareStatement("select doctype from xml_documents where "
457
					+ "docid like '" + docId + "'");
458
			pStmt.execute();
459
			ResultSet rs = pStmt.getResultSet();
460
			boolean hasRow = rs.next();
461
			String doctype = null;
462
			if (hasRow) {
463
				doctype = rs.getString(1);
464

    
465
			}
466
			pStmt.close();
467

    
468
			// if it is an access document
469
			if (doctype != null
470
					&& ((MetacatUtil.getOptionList(PropertyService
471
							.getProperty("xml.accessdoctype")).contains(doctype)))) {
472

    
473
				return true;
474
			}
475

    
476
		} catch (SQLException e) {
477
			throw new SQLException("PermissionControl.isAccessDocument "
478
					+ "Error checking" + " on document " + docId + ". " + e.getMessage());
479
		} catch (PropertyNotFoundException pnfe) {
480
			throw new SQLException("PermissionControl.isAccessDocument "
481
					+ "Error checking" + " on document " + docId + ". " + pnfe.getMessage());
482
		} finally {
483
			try {
484
				pStmt.close();
485
			} finally {
486
				DBConnectionPool.returnDBConnection(conn, serialNumber);
487
			}
488
		}
489

    
490
		return false;
491
	}// isAccessDocument
492

    
493

    
494

    
495
  /**
496
	 * Check if a stirng array contains a given documents' owner
497
	 * 
498
	 * @param principals,
499
	 *            a string array storing the username, groups name and public.
500
	 * @param docid,
501
	 *            the id of given documents
502
	 */
503
  private boolean containDocumentOwner( String[] principals, String docId)
504
                    throws SQLException
505
  {
506
    int lengthOfArray=principals.length;
507
    boolean hasRow;
508
    PreparedStatement pStmt=null;
509
    DBConnection conn = null;
510
    int serialNumber = -1;
511

    
512
    try
513
    {
514
      //check out DBConnection
515
     conn=DBConnectionPool.getDBConnection("PermissionControl.containDocOnwer");
516
      serialNumber=conn.getCheckOutSerialNumber();
517
      pStmt = conn.prepareStatement(
518
                "SELECT 'x' FROM xml_documents " +
519
                "WHERE docid = ? AND lower(user_owner) = ?");
520
      //check every element in the string array too see if it conatains
521
      //the owner of document
522
      for (int i=0; i<lengthOfArray; i++)
523
      {
524

    
525
        // Bind the values to the query
526
        pStmt.setString(1, docId);
527
        pStmt.setString(2, principals[i]);
528
        logMetacat.info("the principle stack is : " +
529
                                  principals[i]);
530

    
531
        pStmt.execute();
532
        ResultSet rs = pStmt.getResultSet();
533
        hasRow = rs.next();
534
        if (hasRow)
535
        {
536
          pStmt.close();
537
           logMetacat.info("find the owner");
538
          return true;
539
        }//if
540

    
541
      }//for
542
    }//try
543
    catch (SQLException e)
544
    {
545
        pStmt.close();
546

    
547
        throw new
548
        SQLException("PermissionControl.hasPermission(). " +
549
                     "Error checking ownership for " + principals[0] +
550
                     " on document #" + docId + ". " + e.getMessage());
551
    }//catch
552
    finally
553
    {
554
      try
555
      {
556
        pStmt.close();
557
      }
558
      finally
559
      {
560
        DBConnectionPool.returnDBConnection(conn, serialNumber);
561
      }
562
    }
563
    return false;
564
  }//containDocumentOwner
565

    
566
  /**
567
    * Check if the permission order for user at that documents is allowFirst
568
    * @param principals, list of names of principals to check for
569
    * @param docid, document identifier to check for
570
    */
571
  private boolean isAllowFirst(String [] principals, String docId,
572
                               long startId)
573
                  throws SQLException, Exception
574
  {
575
    int lengthOfArray=principals.length;
576
    boolean hasRow;
577
    PreparedStatement pStmt = null;
578
    DBConnection conn = null;
579
    int serialNumber = -1;
580
    String sql = null;
581
    boolean topLever =false;
582
    if (startId == TOPLEVELSTARTNODEID)
583
    {
584
      //top level
585
      topLever = true;
586
      sql = "SELECT perm_order FROM xml_access " +
587
    "WHERE lower(principal_name) = ? AND docid = ? AND startnodeid is NULL";
588
    }
589
    else
590
    {
591
      //sub tree level
592
      sql = "SELECT perm_order FROM xml_access " +
593
        "WHERE lower(principal_name)= ? AND docid = ? AND startnodeid = ?";
594
    }
595

    
596
    try
597
    {
598
      //check out DBConnection
599
      conn=DBConnectionPool.getDBConnection("AccessControlList.isAllowFirst");
600
      serialNumber=conn.getCheckOutSerialNumber();
601

    
602
      //select permission order from database
603
      pStmt = conn.prepareStatement(sql);
604

    
605
      //check every name in the array
606
      for (int i=0; i<lengthOfArray;i++)
607
      {
608
        //bind value
609
        pStmt.setString(1, principals[i]);//user name
610
        pStmt.setString(2, docId);//docid
611

    
612
        // if subtree, we need set subtree id
613
        if (!topLever)
614
        {
615
          pStmt.setLong(3, startId);
616
        }
617

    
618
        pStmt.execute();
619
        ResultSet rs = pStmt.getResultSet();
620
        hasRow=rs.next();
621
        if (hasRow)
622
        {
623
          //get the permission order from data base
624
          String permissionOrder=rs.getString(1);
625
          //if the permission order is "allowFirst
626
          if (permissionOrder.equalsIgnoreCase(AccessControlInterface.ALLOWFIRST))
627
          {
628
            pStmt.close();
629
            return true;
630
          }
631
          else
632
          {
633
            pStmt.close();
634
            return false;
635
          }
636
        }//if
637
      }//for
638
    }//try
639
    catch (SQLException e)
640
    {
641
      throw e;
642
    }
643
    finally
644
    {
645
      try
646
      {
647
        pStmt.close();
648
      }
649
      finally
650
      {
651
        DBConnectionPool.returnDBConnection(conn, serialNumber);
652
      }
653
    }
654

    
655
    //if reach here, means there is no permssion record for given names and
656
    //docid. So throw a exception.
657

    
658
    throw new Exception("There is no permission record for user"+principals[0]+
659
                        "at document "+docId);
660

    
661
  }//isAllowFirst
662

    
663
/**
664
    * Check if the permission order for user at that documents is allowFirst
665
    * @param principals, list of names of principals to check for
666
    * @param docid, document identifier to check for
667
    */
668
  public Vector<AccessControlForSingleFile> getAccessControl()
669
                  throws SQLException, Exception
670
  {
671
	Vector<AccessControlForSingleFile> accessControl = new Vector<AccessControlForSingleFile>();
672
    boolean hasRow;
673
    PreparedStatement pStmt = null;
674
    DBConnection conn = null;
675
    int serialNumber = -1;
676
    String sql = null;
677
    boolean topLever =false;
678
    sql = "SELECT principal_name, permission, perm_type, perm_order FROM xml_access ";
679

    
680
    //TODO, need this?
681
    long startId = 0;
682
    if (startId == TOPLEVELSTARTNODEID)
683
    {
684
      //top level
685
      topLever = true;
686
      sql += "WHERE docid = ? AND startnodeid is NULL";
687
    }
688
    else
689
    {
690
      //sub tree level
691
      sql += "WHERE docid = ? AND startnodeid = ?";
692
    }
693

    
694
    try
695
    {
696
      //check out DBConnection
697
      conn=DBConnectionPool.getDBConnection("AccessControlList.getPermissions");
698
      serialNumber=conn.getCheckOutSerialNumber();
699

    
700
      //select permission order from database
701
      pStmt = conn.prepareStatement(sql);
702

    
703
        //bind value
704
        pStmt.setString(1, docId);//docid
705

    
706
        // if subtree, we need set subtree id
707
        if (!topLever)
708
        {
709
          pStmt.setLong(2, startId);
710
        }
711

    
712
        pStmt.execute();
713
        ResultSet rs = pStmt.getResultSet();
714
        while (rs.next())
715
        {
716
          //get the permission order from data base
717
          String principalName=rs.getString(1);
718
          String permission=rs.getString(2);
719
          String permType=rs.getString(3);
720
          String permOrder=rs.getString(4);
721
          
722
          //translate to string version
723
          String myPermission = AccessControlList.txtValue(Integer.parseInt(permission));
724
          
725
          //make it into an object
726
          AccessControlForSingleFile acfsf = 
727
        	  new AccessControlForSingleFile(docId, principalName, myPermission, permType, permOrder);
728
          accessControl.add(acfsf);
729
        }
730
        pStmt.close();
731
    }//try
732
    catch (SQLException e)
733
    {
734
      throw e;
735
    }
736
    finally
737
    {
738
      try
739
      {
740
        pStmt.close();
741
      }
742
      finally
743
      {
744
        DBConnectionPool.returnDBConnection(conn, serialNumber);
745
      }
746
    }
747
    
748
    return accessControl;
749

    
750
  }//getPermissions
751

    
752
  /**
753
    * Check if the users array has allow rules for given users, docid and
754
    * permission.
755
    * If it has permission rule and ticket count is greater than 0, the ticket
756
    * number will decrease one for every allow rule
757
    * @param principals, list of names of principals to check for
758
    * @param docid, document identifier to check for
759
    * @param permission, the permssion need to check
760
    */
761
  private boolean hasAllowRule(String [] principals, String docId,
762
                                  int permission, long startId)
763
                  throws SQLException, Exception
764
 {
765
   int lengthOfArray=principals.length;
766
   boolean allow=false;//initial value is no allow rule
767
   ResultSet rs;
768
   PreparedStatement pStmt = null;
769
   int permissionValue=permission;
770
   int permissionValueInTable;
771
   int ticketCount;
772
   DBConnection conn = null;
773
   int serialNumber = -1;
774
   boolean topLever = false;
775
   String sql = null;
776
   if (startId == TOPLEVELSTARTNODEID)
777
   {
778
     // for toplevel
779
     topLever = true;
780
     sql = "SELECT permission FROM xml_access WHERE docid = ? " +
781
   "AND lower(principal_name) = ? AND perm_type = ? AND startnodeid is NULL";
782
   }
783
   else
784
   {
785
     topLever =false;
786
     sql = "SELECT permission FROM xml_access WHERE docid = ? " +
787
      "AND lower(principal_name) = ? AND perm_type = ? AND startnodeid = ?";
788
   }
789
   try
790
   {
791
     //check out DBConnection
792
     conn=DBConnectionPool.getDBConnection("AccessControlList.hasAllowRule");
793
     serialNumber=conn.getCheckOutSerialNumber();
794
    //This sql statement will select entry with
795
    //begin_time<=currentTime<=end_time in xml_access table
796
    //If begin_time or end_time is null in table, isnull(begin_time, sysdate)
797
    //function will assign begin_time=sysdate
798
    pStmt = conn.prepareStatement(sql);
799
    //bind docid, perm_type
800
    pStmt.setString(1, docId);
801
    pStmt.setString(3, AccessControlInterface.ALLOW);
802

    
803
    // if subtree lever, need to set subTreeId
804
    if (!topLever)
805
    {
806
      pStmt.setLong(4, startId);
807
    }
808

    
809
    //bind every elenment in user name array
810
    for (int i=0;i<lengthOfArray; i++)
811
    {
812
      pStmt.setString(2, principals[i]);
813
      pStmt.execute();
814
      rs=pStmt.getResultSet();
815
      while (rs.next())//check every entry for one user
816
      {
817
        permissionValueInTable=rs.getInt(1);
818

    
819
        //permission is ok
820
        //the user have a permission to access the file
821
        if (( permissionValueInTable & permissionValue )== permissionValue )
822
        {
823

    
824
           allow=true;//has allow rule entry
825
        }//if
826
      }//while
827
    }//for
828
   }//try
829
   catch (SQLException sqlE)
830
   {
831
     throw sqlE;
832
   }
833
   catch (Exception e)
834
   {
835
     throw e;
836
   }
837
   finally
838
   {
839
     try
840
     {
841
       pStmt.close();
842
     }
843
     finally
844
     {
845
       DBConnectionPool.returnDBConnection(conn, serialNumber);
846
     }
847
   }
848
    return allow;
849
 }//hasAllowRule
850

    
851

    
852

    
853
   /**
854
    * Check if the users array has explicit deny rules for given users, docid
855
    * and permission. That means the perm_type is deny and current time is
856
    * less than end_time and greater than begin time, or no time limit.
857
    * @param principals, list of names of principals to check for
858
    * @param docid, document identifier to check for
859
    * @param permission, the permssion need to check
860
    */
861
  private boolean hasExplicitDenyRule(String [] principals, String docId,
862
                                      int permission, long startId)
863
                  throws SQLException
864
 {
865
   int lengthOfArray=principals.length;
866
   ResultSet rs;
867
   PreparedStatement pStmt = null;
868
   int permissionValue=permission;
869
   int permissionValueInTable;
870
   DBConnection conn = null;
871
   int serialNumber = -1;
872
   String sql = null;
873
   boolean topLevel = false;
874

    
875
   // decide top level or subtree level
876
   if (startId == TOPLEVELSTARTNODEID)
877
   {
878
     topLevel = true;
879
     sql = "SELECT permission FROM xml_access WHERE docid = ? " +
880
    "AND lower(principal_name) = ? AND perm_type = ? AND startnodeid is NULL";
881
   }
882
   else
883
   {
884
     topLevel = false;
885
     sql = "SELECT permission FROM xml_access WHERE docid = ? " +
886
     "AND lower(principal_name) = ? AND perm_type = ? AND startnodeid = ?";
887
   }
888

    
889
   try
890
   {
891
     //check out DBConnection
892
     conn=DBConnectionPool.getDBConnection("PermissionControl.hasExplicitDeny");
893
     serialNumber=conn.getCheckOutSerialNumber();
894

    
895
     pStmt = conn.prepareStatement(sql);
896
    //bind docid, perm_type
897
    pStmt.setString(1, docId);
898
    pStmt.setString(3, AccessControlInterface.DENY);
899

    
900
    // subtree level need to set up subtreeid
901
    if (!topLevel)
902
    {
903
      pStmt.setLong(4, startId);
904
    }
905

    
906
    //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

    
916
        //permission is ok the user doesn't have permission to access the file
917
        if (( permissionValueInTable & permissionValue )== permissionValue )
918

    
919
        {
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
  }//hasExplicitDenyRule
943

    
944

    
945
  /**
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

    
956
    if (groups!=null)
957
    {
958
      //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
      //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
        //in order to ignore case sensitive, we transfer user to lower case
967
        if (user != null)
968
        {
969
          usersPackage[0]= user.toLowerCase();
970
          logMetacat.info("after transfer to lower case(not null): "+
971
                                     usersPackage[0]);
972
        }
973
        else
974
        {
975
          usersPackage[0] = user;
976
          usersPackage[0]= user.toLowerCase();
977
          logMetacat.info("after transfer to lower case(null): "+
978
                                     usersPackage[0]);
979
        }
980
        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
          //tansfer group to lower case too
986
          if (groups[i-2] != null)
987
          {
988
            usersPackage[i]=groups[i-2].toLowerCase();
989
          }
990
        } //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
          if (groups[i-1] != null)
1003
          {
1004
            usersPackage[i]=groups[i-1].toLowerCase();
1005
          }
1006
        } //for
1007
      }//else user=public
1008

    
1009
    }//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
        if (user != null)
1019
        {
1020
          usersPackage[0]=user.toLowerCase();
1021
        }
1022
        else
1023
        {
1024
          usersPackage[0]=user;
1025
        }
1026
        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

    
1039

    
1040
  /**
1041
   * 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
   * @param docidWithoutRev, metadata docid which should be the accessfileid
1046
   *                         in the table
1047
   * @param user , the name of user
1048
   * @param groups, the group which the user belong to
1049
   */
1050
   public static Hashtable<String, String> getUnReadableInlineDataIdList(String docidWithoutRev,
1051
                                                   String user, String[] groups,
1052
                                                   boolean withRevision)
1053
                                                throws Exception
1054
   {
1055
     Hashtable<String, String> inlineDataList = getUnAccessableInlineDataIdList(docidWithoutRev,
1056
                              user, groups, AccessControlInterface.READSTRING,
1057
                              withRevision);
1058

    
1059
     return inlineDataList;
1060
   }
1061

    
1062
   /**
1063
  * A static method to get Hashtable which cointains a inline  data object list that
1064
  * user can't overwrite it. The key is subtree id of inline data distrubition,
1065
  * the value is internal file name for the inline data which is stored as docid
1066
  * in xml_access table or data object doc id.
1067
  * @param docidWithoutRev, metadata docid which should be the accessfileid
1068
  *                         in the table
1069
  * @param user , the name of user
1070
  * @param groups, the group which the user belong to
1071
  */
1072
  public static Hashtable<String, String> getUnWritableInlineDataIdList(String docidWithoutRev,
1073
                                                  String user, String[] groups,
1074
                                                  boolean withRevision)
1075
                                               throws Exception
1076
  {
1077
    Hashtable<String, String> inlineDataList = getUnAccessableInlineDataIdList(docidWithoutRev,
1078
                            user, groups, AccessControlInterface.WRITESTRING,
1079
                            withRevision);
1080

    
1081
    return inlineDataList;
1082
  }
1083

    
1084

    
1085
   /*
1086
    * This method will get hashtable which contains a unaccessable distribution
1087
    * inlinedata object list
1088
    *
1089
    * withRevision is used to get inline id list with or without revision number
1090
    * e.g. when withRevision is true, temp.1.1.1, temp.1.1.2 would be returned
1091
    * otherwise temp.1.1 and temp.1.2 would be returned.
1092
    */
1093
   private static Hashtable<String, String> getUnAccessableInlineDataIdList(String docid,
1094
                               String user, String[] groups, String permission,
1095
                               boolean withRevision)
1096
                             throws SQLException,McdbException, Exception
1097
   {
1098
      Hashtable<String, String> unAccessibleIdList = new Hashtable();
1099
      if (user == null) {
1100
    	  return unAccessibleIdList;
1101
      }
1102
      
1103
      Hashtable allIdList = getAllInlineDataIdList(docid);
1104
      Enumeration<String> en = allIdList.keys();
1105
      while (en.hasMoreElements())
1106
      {
1107
        String subTreeId = (String) en.nextElement();
1108
        String fileId = (String) allIdList.get(subTreeId);
1109
        //Here fileid is internal file id for line data. It stored in docid
1110
        // field in xml_access table. so we don't need to delete rev
1111
        PermissionController controller = new PermissionController(docid, false);
1112
        if (!controller.hasPermissionForInlineData(user, groups, permission, fileId))
1113
        {
1114
            if(withRevision)
1115
            {
1116
                logMetacat.info("Put subtree id " + subTreeId +
1117
                                         " and " + "inline data file name " +
1118
                                         fileId + " into " + "un" + permission +
1119
                                         " hash");
1120
                unAccessibleIdList.put(subTreeId, fileId);
1121

    
1122
            }
1123
            else
1124
            {
1125
                logMetacat.info("Put subtree id " + subTreeId +
1126
                                         " and " + "inline data file name " +
1127
                                         DocumentUtil.getInlineDataIdWithoutRev(fileId) +
1128
                                         " into " + "un" + permission +
1129
                                         " hash");
1130
                unAccessibleIdList.put(subTreeId, 
1131
                		DocumentUtil.getInlineDataIdWithoutRev(fileId));
1132
            }
1133
        }
1134
      }
1135
      return unAccessibleIdList;
1136
   }
1137

    
1138

    
1139
   /*
1140
    * This method will get a hash table from xml_access table for all records
1141
    * about the inline data. The key is subtree id and data is a inline internal
1142
    * file name
1143
    */
1144
   private static Hashtable getAllInlineDataIdList(String docid) throws SQLException
1145
   {
1146
     Hashtable inlineDataList = new Hashtable();
1147
     String sql = "SELECT subtreeid, docid FROM xml_access WHERE " +
1148
                   "accessfileid = ? AND subtreeid  IS NOT NULL";
1149
     PreparedStatement pStmt=null;
1150
     ResultSet rs=null;
1151
     DBConnection conn=null;
1152
     int serialNumber=-1;
1153
     try
1154
     {
1155
       //check out DBConnection
1156
       conn=DBConnectionPool.getDBConnection("PermissionControl.getDataSetId");
1157
       serialNumber=conn.getCheckOutSerialNumber();
1158
       pStmt=conn.prepareStatement(sql);
1159
       //bind the value to query
1160
       pStmt.setString(1, docid);
1161
       //execute the query
1162
       pStmt.execute();
1163
       rs=pStmt.getResultSet();
1164
       //process the result
1165
       while(rs.next())
1166
       {
1167
         String subTreeId = rs.getString(1);
1168
         String inlineDataId = rs.getString(2);
1169
         if (subTreeId != null && !subTreeId.trim().equals("") &&
1170
            inlineDataId != null && !inlineDataId.trim().equals(""))
1171
         {
1172
           inlineDataList.put(subTreeId, inlineDataId);
1173
         }
1174
      }//while
1175
     }//try
1176
     finally
1177
     {
1178
       try
1179
       {
1180
         pStmt.close();
1181
       }
1182
       finally
1183
       {
1184
         DBConnectionPool.returnDBConnection(conn, serialNumber);
1185
       }
1186
     }//finally
1187
     return inlineDataList;
1188
   }//getAllInlineDataIdList
1189
}
(52-52/63)