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: 2011-12-07 12:18:24 -0800 (Wed, 07 Dec 2011) $'
12
 * '$Revision: 6744 $'
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.accesscontrol.AccessControlForSingleFile;
40
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlInterface;
41
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlList;
42
import edu.ucsb.nceas.metacat.database.DBConnection;
43
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
44
import edu.ucsb.nceas.metacat.properties.PropertyService;
45
import edu.ucsb.nceas.metacat.service.SessionService;
46
import edu.ucsb.nceas.metacat.util.AuthUtil;
47
import edu.ucsb.nceas.metacat.util.DocumentUtil;
48
import edu.ucsb.nceas.metacat.util.MetacatUtil;
49
import edu.ucsb.nceas.metacat.util.SessionData;
50
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
51
import edu.ucsb.nceas.metacat.shared.MetacatUtilException;
52
import edu.ucsb.nceas.metacat.shared.ServiceException;
53

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

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

    
68
   private static Logger logMetacat = Logger.getLogger(PermissionController.class);
69

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

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

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

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

    
88
	}
89

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

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

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

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

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

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

    
146
    if (isAccessDocument() && (permission == AccessControlInterface.WRITE))
147
    {
148

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

    
157
    return hasPermission;
158
  }
159

    
160

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

    
179
          return true;
180
      }
181

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

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

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

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

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

    
228
    return false;
229
  }//hasPermission
230

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

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

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

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

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

    
333

    
334
        try
335
        {
336
          if (isAllowFirst(principals, startId))
337
          {
338

    
339
            if (hasExplicitDenyRule(principals, permission, startId ))
340
            {
341

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

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

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

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

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

    
396

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

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

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

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

    
489
			}
490
			pStmt.close();
491

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

    
497
				return true;
498
			}
499

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

    
514
		return false;
515
	}// isAccessDocument
516

    
517

    
518

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

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

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

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

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

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

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

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

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

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

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

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

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

    
705
  }//isAllowFirst
706

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

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

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

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

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

    
812

    
813

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

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

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

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

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

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

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

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

    
913

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

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

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

    
1008

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

    
1026
     return inlineDataList;
1027
   }
1028

    
1029
   /**
1030
  * A static method to get Hashtable which cointains a inline  data object list that
1031
  * user can't overwrite it. The key is subtree id of inline data distrubition,
1032
  * the value is 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> getUnWritableInlineDataIdList(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.WRITESTRING);
1046

    
1047
    return inlineDataList;
1048
  }
1049

    
1050

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

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

    
1088
                        
1089
        }
1090
      }
1091
      return unAccessibleIdList;
1092
   }
1093

    
1094

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