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-08-24 14:34:17 -0700 (Mon, 24 Aug 2009) $'
12
 * '$Revision: 5030 $'
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.util.DocumentUtil;
43
import edu.ucsb.nceas.metacat.util.MetacatUtil;
44
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
45

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

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

    
56
   private static Logger logMetacat = Logger.getLogger(PermissionController.class);
57

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

    
69
   /**
70
    * Constructor for PermssionController
71
    * @param myDocid String
72
    * @param needDeleteRev boolean
73
    */
74
   public PermissionController(String myDocid, boolean needDeleteRevFromDocid)
75
   {
76
     if (!needDeleteRevFromDocid)
77
     {
78
       docId = myDocid;
79
     }
80
     else
81
     {
82
         docId = DocumentUtil.getDocIdFromAccessionNumber(myDocid);
83
     }
84
   }
85

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

    
94

    
95
  /**
96
    * Check from db connection if at least one of the list of @principals
97
    * @param user  the user name
98
    * @param groups  the groups which the use is in
99
    * @param myPermission  permission type to check for
100
    */
101
  public boolean hasPermission(String user, String[]groups, String myPermission)
102
                              throws SQLException //, Exception
103
  {
104
    boolean hasPermission=false;
105
    String [] userPackage=null;
106
    int permission =AccessControlList.intValue(myPermission);
107

    
108
    //for the commnad line invocation
109
    if ((user==null) && (groups==null || groups.length==0))
110
    {
111
      return true;
112
    }
113

    
114
    //create a userpackage including user, public and group member
115
    userPackage=createUsersPackage(user, groups);
116

    
117
    //if the requested document is access documents and requested permission
118
    //is "write", the user should have "all" right
119

    
120
    if (isAccessDocument(docId) && (permission == AccessControlInterface.WRITE))
121
    {
122

    
123
      hasPermission = hasPermission(userPackage,docId, 7);// 7 is all permission
124
    }//if
125
    else //in other situation, just check the request permission
126
    {
127

    
128

    
129
      // Check for @permission on @docid for @user and/or @groups
130
      hasPermission = hasPermission(userPackage,docId, permission);
131

    
132
    }//else
133

    
134
    return hasPermission;
135
  }
136

    
137

    
138
  /**
139
    * Check from db connection if the users in String array @principals has
140
    * @permission on @docid*
141
    * @param principals, names in userPakcage need to check for @permission
142
    * @param docid, document identifier to check on
143
    * @param permission, permission (write or all...) to check for
144
    */
145
  private boolean hasPermission(String [] principals, String docId,
146
                                           int permission)
147
                         throws SQLException
148
  {
149
    long startId = TOPLEVELSTARTNODEID;// this is for top level, so startid is 0
150
    try
151
    {
152
      //first, if there is a docid owner in user package, return true
153
      //because doc owner has all permssion
154
      if (containDocumentOwner(principals, docId))
155
      {
156

    
157
          return true;
158
      }
159

    
160
      //If there is no owner in user package, checking the table
161
      //check perm_order
162
      if (isAllowFirst(principals, docId, startId))
163
      {
164

    
165
        if (hasExplicitDenyRule(principals, docId, permission, startId))
166
        {
167
          //if it is allowfirst and has deny rule(either explicit )
168
          //deny access
169

    
170
          return false;
171
        }//if
172
        else if ( hasAllowRule(principals, docId, permission, startId))
173
        {
174
          //if it is allowfirst and hasn't deny rule and has allow rule
175
          //allow access
176

    
177
          return true;
178
        }//else if
179
        else
180
        {
181
          //other situation deny access
182

    
183
          return false;
184
        }//else
185
     }//if isAllowFirst
186
     else //denyFirst
187
     {
188
       if (hasAllowRule(principals, docId, permission, startId))
189
       {
190
         //if it is denyFirst and has allow rule, allow access
191
         return true;
192
       }
193
       else
194
       {
195
         //if it is denyfirst but no allow rule, deny access
196
         return false;
197
       }
198
     }//else denyfirst
199
    }//try
200
    catch (Exception e)
201
    {
202
      logMetacat.warn("There is a exception in hasPermission method: "
203
                         +e.getMessage());
204
    }
205

    
206
    return false;
207
  }//hasPermission
208

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

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

    
279
    //for the commnad line invocation return null(no unaccessable subtree)
280
    if ((user==null) && (groups==null || groups.length==0))
281
    {
282
      return resultUnaccessableSubTree;
283
    }
284

    
285
    //create a userpackage including user, public and group member
286
    principals=createUsersPackage(user, groups);
287
    //for the document owner return null(no unaccessable subtree)
288
    try
289
    {
290
      if (containDocumentOwner(principals, docId))
291
      {
292
       return resultUnaccessableSubTree;
293
      }
294
    }
295
    catch (SQLException ee)
296
    {
297
      throw new McdbException(ee);
298
    }
299

    
300
    // go through every subtree which has access control
301
    for (int i = 0; i< subTreeList.size(); i++)
302
    {
303
      SubTree tree = (SubTree)subTreeList.elementAt(i);
304
      long startId = tree.getStartNodeId();
305

    
306

    
307
        try
308
        {
309
          if (isAllowFirst(principals, docId, startId))
310
          {
311

    
312
            if (hasExplicitDenyRule(principals, docId, permission, startId ))
313
            {
314

    
315
             //if it is allowfirst and has deny rule
316
              // put the subtree into unaccessable vector
317
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
318
              {
319
                resultUnaccessableSubTree.put(new Long(startId), tree);
320
              }
321
            }//if
322
            else if ( hasAllowRule(principals, docId, permission, startId))
323
            {
324
              //if it is allowfirst and hasn't deny rule and has allow rule
325
              //allow access do nothing
326

    
327
            }//else if
328
            else
329
            {
330
              //other situation deny access
331
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
332
              {
333
                resultUnaccessableSubTree.put(new Long(startId), tree);
334
              }
335

    
336
            }//else
337
          }//if isAllowFirst
338
          else //denyFirst
339
          {
340
            if (hasAllowRule(principals, docId, permission,startId))
341
            {
342
              //if it is denyFirst and has allow rule, allow access, do nothing
343

    
344
            }
345
            else
346
            {
347
              //if it is denyfirst but no allow rule, deny access
348
              // add into vector
349
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
350
              {
351
                resultUnaccessableSubTree.put(new Long(startId), tree);
352
              }
353
            }
354
          }//else denyfirst
355
        }//try
356
        catch( Exception e)
357
        {
358
          logMetacat.error("error in PermissionControl.has" +
359
                                   "UnaccessableSubTree "+e.getMessage());
360
          throw new McdbException(e);
361
        }
362

    
363
    }//for
364
    // merge the subtree if a subtree is another subtree'subtree
365
    resultUnaccessableSubTree = mergeEquivalentSubtree(resultUnaccessableSubTree);
366
    return resultUnaccessableSubTree;
367
  }//hasUnaccessableSubtree
368

    
369

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

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

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

    
454
			}
455
			pStmt.close();
456

    
457
			// if it is an access document
458
			if (doctype != null
459
					&& ((MetacatUtil.getOptionList(PropertyService
460
							.getProperty("xml.accessdoctype")).contains(doctype)))) {
461

    
462
				return true;
463
			}
464

    
465
		} catch (SQLException e) {
466
			throw new SQLException("PermissionControl.isAccessDocument "
467
					+ "Error checking" + " on document " + docId + ". " + e.getMessage());
468
		} catch (PropertyNotFoundException pnfe) {
469
			throw new SQLException("PermissionControl.isAccessDocument "
470
					+ "Error checking" + " on document " + docId + ". " + pnfe.getMessage());
471
		} finally {
472
			try {
473
				pStmt.close();
474
			} finally {
475
				DBConnectionPool.returnDBConnection(conn, serialNumber);
476
			}
477
		}
478

    
479
		return false;
480
	}// isAccessDocument
481

    
482

    
483

    
484
  /**
485
	 * Check if a stirng array contains a given documents' owner
486
	 * 
487
	 * @param principals,
488
	 *            a string array storing the username, groups name and public.
489
	 * @param docid,
490
	 *            the id of given documents
491
	 */
492
  private boolean containDocumentOwner( String[] principals, String docId)
493
                    throws SQLException
494
  {
495
    int lengthOfArray=principals.length;
496
    boolean hasRow;
497
    PreparedStatement pStmt=null;
498
    DBConnection conn = null;
499
    int serialNumber = -1;
500

    
501
    try
502
    {
503
      //check out DBConnection
504
     conn=DBConnectionPool.getDBConnection("PermissionControl.containDocOnwer");
505
      serialNumber=conn.getCheckOutSerialNumber();
506
      pStmt = conn.prepareStatement(
507
                "SELECT 'x' FROM xml_documents " +
508
                "WHERE docid = ? AND lower(user_owner) = ?");
509
      //check every element in the string array too see if it conatains
510
      //the owner of document
511
      for (int i=0; i<lengthOfArray; i++)
512
      {
513

    
514
        // Bind the values to the query
515
        pStmt.setString(1, docId);
516
        pStmt.setString(2, principals[i]);
517
        logMetacat.info("the principle stack is : " +
518
                                  principals[i]);
519

    
520
        pStmt.execute();
521
        ResultSet rs = pStmt.getResultSet();
522
        hasRow = rs.next();
523
        if (hasRow)
524
        {
525
          pStmt.close();
526
           logMetacat.info("find the owner");
527
          return true;
528
        }//if
529

    
530
      }//for
531
    }//try
532
    catch (SQLException e)
533
    {
534
        pStmt.close();
535

    
536
        throw new
537
        SQLException("PermissionControl.hasPermission(). " +
538
                     "Error checking ownership for " + principals[0] +
539
                     " on document #" + docId + ". " + e.getMessage());
540
    }//catch
541
    finally
542
    {
543
      try
544
      {
545
        pStmt.close();
546
      }
547
      finally
548
      {
549
        DBConnectionPool.returnDBConnection(conn, serialNumber);
550
      }
551
    }
552
    return false;
553
  }//containDocumentOwner
554

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

    
585
    try
586
    {
587
      //check out DBConnection
588
      conn=DBConnectionPool.getDBConnection("AccessControlList.isAllowFirst");
589
      serialNumber=conn.getCheckOutSerialNumber();
590

    
591
      //select permission order from database
592
      pStmt = conn.prepareStatement(sql);
593

    
594
      //check every name in the array
595
      for (int i=0; i<lengthOfArray;i++)
596
      {
597
        //bind value
598
        pStmt.setString(1, principals[i]);//user name
599
        pStmt.setString(2, docId);//docid
600

    
601
        // if subtree, we need set subtree id
602
        if (!topLever)
603
        {
604
          pStmt.setLong(3, startId);
605
        }
606

    
607
        pStmt.execute();
608
        ResultSet rs = pStmt.getResultSet();
609
        hasRow=rs.next();
610
        if (hasRow)
611
        {
612
          //get the permission order from data base
613
          String permissionOrder=rs.getString(1);
614
          //if the permission order is "allowFirst
615
          if (permissionOrder.equalsIgnoreCase(AccessControlInterface.ALLOWFIRST))
616
          {
617
            pStmt.close();
618
            return true;
619
          }
620
          else
621
          {
622
            pStmt.close();
623
            return false;
624
          }
625
        }//if
626
      }//for
627
    }//try
628
    catch (SQLException e)
629
    {
630
      throw e;
631
    }
632
    finally
633
    {
634
      try
635
      {
636
        pStmt.close();
637
      }
638
      finally
639
      {
640
        DBConnectionPool.returnDBConnection(conn, serialNumber);
641
      }
642
    }
643

    
644
    //if reach here, means there is no permssion record for given names and
645
    //docid. So throw a exception.
646

    
647
    throw new Exception("There is no permission record for user"+principals[0]+
648
                        "at document "+docId);
649

    
650
  }//isAllowFirst
651

    
652
/**
653
    * Check if the permission order for user at that documents is allowFirst
654
    * @param principals, list of names of principals to check for
655
    * @param docid, document identifier to check for
656
    */
657
  public Vector<AccessControlForSingleFile> getAccessControl()
658
                  throws SQLException, Exception
659
  {
660
	Vector<AccessControlForSingleFile> accessControl = new Vector<AccessControlForSingleFile>();
661
    boolean hasRow;
662
    PreparedStatement pStmt = null;
663
    DBConnection conn = null;
664
    int serialNumber = -1;
665
    String sql = null;
666
    boolean topLever =false;
667
    sql = "SELECT principal_name, permission, perm_type, perm_order FROM xml_access ";
668

    
669
    //TODO, need this?
670
    long startId = 0;
671
    if (startId == TOPLEVELSTARTNODEID)
672
    {
673
      //top level
674
      topLever = true;
675
      sql += "WHERE docid = ? AND startnodeid is NULL";
676
    }
677
    else
678
    {
679
      //sub tree level
680
      sql += "WHERE docid = ? AND startnodeid = ?";
681
    }
682

    
683
    try
684
    {
685
      //check out DBConnection
686
      conn=DBConnectionPool.getDBConnection("AccessControlList.getPermissions");
687
      serialNumber=conn.getCheckOutSerialNumber();
688

    
689
      //select permission order from database
690
      pStmt = conn.prepareStatement(sql);
691

    
692
        //bind value
693
        pStmt.setString(1, docId);//docid
694

    
695
        // if subtree, we need set subtree id
696
        if (!topLever)
697
        {
698
          pStmt.setLong(2, startId);
699
        }
700

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

    
739
  }//getPermissions
740

    
741
  /**
742
    * Check if the users array has allow rules for given users, docid and
743
    * permission.
744
    * If it has permission rule and ticket count is greater than 0, the ticket
745
    * number will decrease one for every allow rule
746
    * @param principals, list of names of principals to check for
747
    * @param docid, document identifier to check for
748
    * @param permission, the permssion need to check
749
    */
750
  private boolean hasAllowRule(String [] principals, String docId,
751
                                  int permission, long startId)
752
                  throws SQLException, Exception
753
 {
754
   int lengthOfArray=principals.length;
755
   boolean allow=false;//initial value is no allow rule
756
   ResultSet rs;
757
   PreparedStatement pStmt = null;
758
   int permissionValue=permission;
759
   int permissionValueInTable;
760
   int ticketCount;
761
   DBConnection conn = null;
762
   int serialNumber = -1;
763
   boolean topLever = false;
764
   String sql = null;
765
   if (startId == TOPLEVELSTARTNODEID)
766
   {
767
     // for toplevel
768
     topLever = true;
769
     sql = "SELECT permission FROM xml_access WHERE docid = ? " +
770
   "AND lower(principal_name) = ? AND perm_type = ? AND startnodeid is NULL";
771
   }
772
   else
773
   {
774
     topLever =false;
775
     sql = "SELECT permission FROM xml_access WHERE docid = ? " +
776
      "AND lower(principal_name) = ? AND perm_type = ? AND startnodeid = ?";
777
   }
778
   try
779
   {
780
     //check out DBConnection
781
     conn=DBConnectionPool.getDBConnection("AccessControlList.hasAllowRule");
782
     serialNumber=conn.getCheckOutSerialNumber();
783
    //This sql statement will select entry with
784
    //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
    pStmt = conn.prepareStatement(sql);
788
    //bind docid, perm_type
789
    pStmt.setString(1, docId);
790
    pStmt.setString(3, AccessControlInterface.ALLOW);
791

    
792
    // if subtree lever, need to set subTreeId
793
    if (!topLever)
794
    {
795
      pStmt.setLong(4, startId);
796
    }
797

    
798
    //bind every elenment in user name array
799
    for (int i=0;i<lengthOfArray; i++)
800
    {
801
      pStmt.setString(2, principals[i]);
802
      pStmt.execute();
803
      rs=pStmt.getResultSet();
804
      while (rs.next())//check every entry for one user
805
      {
806
        permissionValueInTable=rs.getInt(1);
807

    
808
        //permission is ok
809
        //the user have a permission to access the file
810
        if (( permissionValueInTable & permissionValue )== permissionValue )
811
        {
812

    
813
           allow=true;//has allow rule entry
814
        }//if
815
      }//while
816
    }//for
817
   }//try
818
   catch (SQLException sqlE)
819
   {
820
     throw sqlE;
821
   }
822
   catch (Exception e)
823
   {
824
     throw e;
825
   }
826
   finally
827
   {
828
     try
829
     {
830
       pStmt.close();
831
     }
832
     finally
833
     {
834
       DBConnectionPool.returnDBConnection(conn, serialNumber);
835
     }
836
   }
837
    return allow;
838
 }//hasAllowRule
839

    
840

    
841

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

    
864
   // decide top level or subtree level
865
   if (startId == TOPLEVELSTARTNODEID)
866
   {
867
     topLevel = true;
868
     sql = "SELECT permission FROM xml_access WHERE docid = ? " +
869
    "AND lower(principal_name) = ? AND perm_type = ? AND startnodeid is NULL";
870
   }
871
   else
872
   {
873
     topLevel = false;
874
     sql = "SELECT permission FROM xml_access WHERE docid = ? " +
875
     "AND lower(principal_name) = ? AND perm_type = ? AND startnodeid = ?";
876
   }
877

    
878
   try
879
   {
880
     //check out DBConnection
881
     conn=DBConnectionPool.getDBConnection("PermissionControl.hasExplicitDeny");
882
     serialNumber=conn.getCheckOutSerialNumber();
883

    
884
     pStmt = conn.prepareStatement(sql);
885
    //bind docid, perm_type
886
    pStmt.setString(1, docId);
887
    pStmt.setString(3, AccessControlInterface.DENY);
888

    
889
    // subtree level need to set up subtreeid
890
    if (!topLevel)
891
    {
892
      pStmt.setLong(4, startId);
893
    }
894

    
895
    //bind every elenment in user name array
896
    for (int i=0;i<lengthOfArray; i++)
897
    {
898
      pStmt.setString(2, principals[i]);
899
      pStmt.execute();
900
      rs=pStmt.getResultSet();
901
      while (rs.next())//check every entry for one user
902
      {
903
        permissionValueInTable=rs.getInt(1);
904

    
905
        //permission is ok the user doesn't have permission to access the file
906
        if (( permissionValueInTable & permissionValue )== permissionValue )
907

    
908
        {
909
           pStmt.close();
910
           return true;
911
         }//if
912
      }//while
913
    }//for
914
   }//try
915
   catch (SQLException e)
916
   {
917
     throw e;
918
   }//catch
919
   finally
920
   {
921
     try
922
     {
923
       pStmt.close();
924
     }
925
     finally
926
     {
927
       DBConnectionPool.returnDBConnection(conn, serialNumber);
928
     }
929
   }//finally
930
   return false;//no deny rule
931
  }//hasExplicitDenyRule
932

    
933

    
934
  /**
935
    * Creat a users pakages to check permssion rule, user itself, public and
936
    * the gourps the user belong will be include in this package
937
    * @param user, the name of user
938
    * @param groups, the string array of the groups that user belong to
939
    */
940
  private String[] createUsersPackage(String user, String [] groups)
941
  {
942
    String [] usersPackage=null;
943
    int lengthOfPackage;
944

    
945
    if (groups!=null)
946
    {
947
      //if gouprs is not null and user is not public, we should create a array
948
      //to store the groups and user and public.
949
      //So the length of userPackage is the length of group plus two
950
      if (!user.equalsIgnoreCase(AccessControlInterface.PUBLIC))
951
      {
952
        lengthOfPackage=(groups.length)+2;
953
        usersPackage=new String [lengthOfPackage];
954
        //the first two elements is user self and public
955
        //in order to ignore case sensitive, we transfer user to lower case
956
        if (user != null)
957
        {
958
          usersPackage[0]= user.toLowerCase();
959
          logMetacat.info("after transfer to lower case(not null): "+
960
                                     usersPackage[0]);
961
        }
962
        else
963
        {
964
          usersPackage[0] = user;
965
          usersPackage[0]= user.toLowerCase();
966
          logMetacat.info("after transfer to lower case(null): "+
967
                                     usersPackage[0]);
968
        }
969
        usersPackage[1]=AccessControlInterface.PUBLIC;
970
        //put groups element from index 0 to lengthOfPackage-3 into userPackage
971
        //from index 2 to lengthOfPackage-1
972
        for (int i=2; i<lengthOfPackage; i++)
973
        {
974
          //tansfer group to lower case too
975
          if (groups[i-2] != null)
976
          {
977
            usersPackage[i]=groups[i-2].toLowerCase();
978
          }
979
        } //for
980
      }//if user!=public
981
      else//use=public
982
      {
983
        lengthOfPackage=(groups.length)+1;
984
        usersPackage=new String [lengthOfPackage];
985
        //the first lements is public
986
        usersPackage[0]=AccessControlInterface.PUBLIC;
987
        //put groups element from index 0 to lengthOfPackage-2 into userPackage
988
        //from index 1 to lengthOfPackage-1
989
        for (int i=1; i<lengthOfPackage; i++)
990
        {
991
          if (groups[i-1] != null)
992
          {
993
            usersPackage[i]=groups[i-1].toLowerCase();
994
          }
995
        } //for
996
      }//else user=public
997

    
998
    }//if groups!=null
999
    else
1000
    {
1001
      //because no groups, the userPackage only need two elements
1002
      //one is for user, the other is for public
1003
      if (!user.equalsIgnoreCase(AccessControlInterface.PUBLIC))
1004
      {
1005
        lengthOfPackage=2;
1006
        usersPackage=new String [lengthOfPackage];
1007
        if (user != null)
1008
        {
1009
          usersPackage[0]=user.toLowerCase();
1010
        }
1011
        else
1012
        {
1013
          usersPackage[0]=user;
1014
        }
1015
        usersPackage[1]=AccessControlInterface.PUBLIC;
1016
      }//if user!=public
1017
      else //user==public
1018
      {
1019
        //only put public into array
1020
        lengthOfPackage=1;
1021
        usersPackage=new String [lengthOfPackage];
1022
        usersPackage[0]=AccessControlInterface.PUBLIC;
1023
      }
1024
    }//else groups==null
1025
    return usersPackage;
1026
  }//createUsersPackage
1027

    
1028

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

    
1048
     return inlineDataList;
1049
   }
1050

    
1051
   /**
1052
  * A static method to get Hashtable which cointains a inline  data object list that
1053
  * user can't overwrite it. The key is subtree id of inline data distrubition,
1054
  * the value is internal file name for the inline data which is stored as docid
1055
  * in xml_access table or data object doc id.
1056
  * @param docidWithoutRev, metadata docid which should be the accessfileid
1057
  *                         in the table
1058
  * @param user , the name of user
1059
  * @param groups, the group which the user belong to
1060
  */
1061
  public static Hashtable<String, String> getUnWritableInlineDataIdList(String docidWithoutRev,
1062
                                                  String user, String[] groups,
1063
                                                  boolean withRevision)
1064
                                               throws Exception
1065
  {
1066
    Hashtable<String, String> inlineDataList = getUnAccessableInlineDataIdList(docidWithoutRev,
1067
                            user, groups, AccessControlInterface.WRITESTRING,
1068
                            withRevision);
1069

    
1070
    return inlineDataList;
1071
  }
1072

    
1073

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

    
1111
            }
1112
            else
1113
            {
1114
                logMetacat.info("Put subtree id " + subTreeId +
1115
                                         " and " + "inline data file name " +
1116
                                         DocumentUtil.getInlineDataIdWithoutRev(fileId) +
1117
                                         " into " + "un" + permission +
1118
                                         " hash");
1119
                unAccessibleIdList.put(subTreeId, 
1120
                		DocumentUtil.getInlineDataIdWithoutRev(fileId));
1121
            }
1122
        }
1123
      }
1124
      return unAccessibleIdList;
1125
   }
1126

    
1127

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