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: 2010-04-14 11:31:03 -0700 (Wed, 14 Apr 2010) $'
12
 * '$Revision: 5311 $'
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.DocumentUtil;
47
import edu.ucsb.nceas.metacat.util.MetacatUtil;
48
import edu.ucsb.nceas.metacat.util.SessionData;
49
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
50

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

    
58
   private static final long TOPLEVELSTARTNODEID = 0; //if start node is 0, means it is top
59
                                         //level document
60

    
61
   private static Logger logMetacat = Logger.getLogger(PermissionController.class);
62

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

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

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

    
99
   public boolean hasPermission(String sessionId, String myPermission) throws SQLException {
100
	   SessionData sessionData = SessionService.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
    * @param user  the user name
112
    * @param groups  the groups which the use is in
113
    * @param myPermission  permission type to check for
114
    */
115
  public boolean hasPermission(String user, String[]groups, String myPermission)
116
                              throws SQLException //, Exception
117
  {
118
    boolean hasPermission=false;
119
    String [] userPackage=null;
120
    int permission =AccessControlList.intValue(myPermission);
121

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

    
128
    //create a userpackage including user, public and group member
129
    userPackage=createUsersPackage(user, groups);
130

    
131
    //if the requested document is access documents and requested permission
132
    //is "write", the user should have "all" right
133

    
134
    if (isAccessDocument(docId) && (permission == AccessControlInterface.WRITE))
135
    {
136

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

    
142

    
143
      // Check for @permission on @docid for @user and/or @groups
144
      hasPermission = hasPermission(userPackage,docId, permission);
145

    
146
    }//else
147

    
148
    return hasPermission;
149
  }
150

    
151

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

    
171
          return true;
172
      }
173

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

    
179
        if (hasExplicitDenyRule(principals, docId, permission, startId))
180
        {
181
          //if it is allowfirst and has deny rule(either explicit )
182
          //deny access
183

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

    
191
          return true;
192
        }//else if
193
        else
194
        {
195
          //other situation deny access
196

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

    
220
    return false;
221
  }//hasPermission
222

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

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

    
297
    //for the commnad line invocation return null(no unaccessable subtree)
298
    if ((user==null) && (groups==null || groups.length==0))
299
    {
300
      return resultUnaccessableSubTree;
301
    }
302

    
303
    //create a userpackage including user, public and group member
304
    principals=createUsersPackage(user, groups);
305
    //for the document owner return null(no unaccessable subtree)
306
    try
307
    {
308
      if (containDocumentOwner(principals, docId))
309
      {
310
       return resultUnaccessableSubTree;
311
      }
312
    }
313
    catch (SQLException ee)
314
    {
315
      throw new McdbException(ee);
316
    }
317

    
318
    // go through every subtree which has access control
319
    for (int i = 0; i< subTreeList.size(); i++)
320
    {
321
      SubTree tree = (SubTree)subTreeList.elementAt(i);
322
      long startId = tree.getStartNodeId();
323

    
324

    
325
        try
326
        {
327
          if (isAllowFirst(principals, docId, startId))
328
          {
329

    
330
            if (hasExplicitDenyRule(principals, docId, permission, startId ))
331
            {
332

    
333
             //if it is allowfirst and has deny rule
334
              // put the subtree into unaccessable vector
335
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
336
              {
337
                resultUnaccessableSubTree.put(new Long(startId), tree);
338
              }
339
            }//if
340
            else if ( hasAllowRule(principals, docId, permission, startId))
341
            {
342
              //if it is allowfirst and hasn't deny rule and has allow rule
343
              //allow access do nothing
344

    
345
            }//else if
346
            else
347
            {
348
              //other situation deny access
349
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
350
              {
351
                resultUnaccessableSubTree.put(new Long(startId), tree);
352
              }
353

    
354
            }//else
355
          }//if isAllowFirst
356
          else //denyFirst
357
          {
358
            if (hasAllowRule(principals, docId, permission,startId))
359
            {
360
              //if it is denyFirst and has allow rule, allow access, do nothing
361

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

    
381
    }//for
382
    // merge the subtree if a subtree is another subtree'subtree
383
    resultUnaccessableSubTree = mergeEquivalentSubtree(resultUnaccessableSubTree);
384
    return resultUnaccessableSubTree;
385
  }//hasUnaccessableSubtree
386

    
387

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

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

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

    
472
			}
473
			pStmt.close();
474

    
475
			// if it is an access document
476
			if (doctype != null
477
					&& ((MetacatUtil.getOptionList(PropertyService
478
							.getProperty("xml.accessdoctype")).contains(doctype)))) {
479

    
480
				return true;
481
			}
482

    
483
		} catch (SQLException e) {
484
			throw new SQLException("PermissionControl.isAccessDocument "
485
					+ "Error checking" + " on document " + docId + ". " + e.getMessage());
486
		} catch (PropertyNotFoundException pnfe) {
487
			throw new SQLException("PermissionControl.isAccessDocument "
488
					+ "Error checking" + " on document " + docId + ". " + pnfe.getMessage());
489
		} finally {
490
			try {
491
				pStmt.close();
492
			} finally {
493
				DBConnectionPool.returnDBConnection(conn, serialNumber);
494
			}
495
		}
496

    
497
		return false;
498
	}// isAccessDocument
499

    
500

    
501

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

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

    
532
        // Bind the values to the query
533
        pStmt.setString(1, docId);
534
        pStmt.setString(2, principals[i]);
535
        logMetacat.info("PermissionController.containDocumentOwner - the principle stack is : " +
536
                                  principals[i]);
537

    
538
        pStmt.execute();
539
        ResultSet rs = pStmt.getResultSet();
540
        hasRow = rs.next();
541
        if (hasRow)
542
        {
543
          pStmt.close();
544
           logMetacat.info("PermissionController.containDocumentOwner - find the owner");
545
          return true;
546
        }//if
547

    
548
      }//for
549
    }//try
550
    catch (SQLException e)
551
    {
552
        pStmt.close();
553

    
554
        throw new
555
        SQLException("PermissionControl.hasPermission - " +
556
                     "Error checking ownership for " + principals[0] +
557
                     " on document #" + docId + ". " + e.getMessage());
558
    }//catch
559
    finally
560
    {
561
      try
562
      {
563
        pStmt.close();
564
      }
565
      finally
566
      {
567
        DBConnectionPool.returnDBConnection(conn, serialNumber);
568
      }
569
    }
570
    return false;
571
  }//containDocumentOwner
572

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

    
603
    try
604
    {
605
      //check out DBConnection
606
      conn=DBConnectionPool.getDBConnection("AccessControlList.isAllowFirst");
607
      serialNumber=conn.getCheckOutSerialNumber();
608

    
609
      //select permission order from database
610
      pStmt = conn.prepareStatement(sql);
611

    
612
      //check every name in the array
613
      for (int i=0; i<lengthOfArray;i++)
614
      {
615
        //bind value
616
        pStmt.setString(1, principals[i]);//user name
617
        pStmt.setString(2, docId);//docid
618

    
619
        // if subtree, we need set subtree id
620
        if (!topLever)
621
        {
622
          pStmt.setLong(3, startId);
623
        }
624

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

    
662
    //if reach here, means there is no permssion record for given names and
663
    //docid. So throw a exception.
664

    
665
    throw new Exception("PermissionController.isAllowFirst - There is no permission record for user "+ principals[0] + 
666
                        " at document " + docId);
667

    
668
  }//isAllowFirst
669

    
670
  /**
671
    * Check if the users array has allow rules for given users, docid and
672
    * permission.
673
    * If it has permission rule and ticket count is greater than 0, the ticket
674
    * number will decrease one for every allow rule
675
    * @param principals, list of names of principals to check for
676
    * @param docid, document identifier to check for
677
    * @param permission, the permssion need to check
678
    */
679
  private boolean hasAllowRule(String [] principals, String docId,
680
                                  int permission, long startId)
681
                  throws SQLException, Exception
682
 {
683
   int lengthOfArray=principals.length;
684
   boolean allow=false;//initial value is no allow rule
685
   ResultSet rs;
686
   PreparedStatement pStmt = null;
687
   int permissionValue=permission;
688
   int permissionValueInTable;
689
   int ticketCount;
690
   DBConnection conn = null;
691
   int serialNumber = -1;
692
   boolean topLever = false;
693
   String sql = null;
694
   if (startId == TOPLEVELSTARTNODEID)
695
   {
696
     // for toplevel
697
     topLever = true;
698
     sql = "SELECT permission FROM xml_access WHERE docid = ? " +
699
   "AND lower(principal_name) = ? AND perm_type = ? AND startnodeid is NULL";
700
   }
701
   else
702
   {
703
     topLever =false;
704
     sql = "SELECT permission FROM xml_access WHERE docid = ? " +
705
      "AND lower(principal_name) = ? AND perm_type = ? AND startnodeid = ?";
706
   }
707
   try
708
   {
709
     //check out DBConnection
710
     conn=DBConnectionPool.getDBConnection("AccessControlList.hasAllowRule");
711
     serialNumber=conn.getCheckOutSerialNumber();
712
    //This sql statement will select entry with
713
    //begin_time<=currentTime<=end_time in xml_access table
714
    //If begin_time or end_time is null in table, isnull(begin_time, sysdate)
715
    //function will assign begin_time=sysdate
716
    pStmt = conn.prepareStatement(sql);
717
    //bind docid, perm_type
718
    pStmt.setString(1, docId);
719
    pStmt.setString(3, AccessControlInterface.ALLOW);
720

    
721
    // if subtree lever, need to set subTreeId
722
    if (!topLever)
723
    {
724
      pStmt.setLong(4, startId);
725
    }
726

    
727
    //bind every elenment in user name array
728
    for (int i=0;i<lengthOfArray; i++)
729
    {
730
      pStmt.setString(2, principals[i]);
731
      pStmt.execute();
732
      rs=pStmt.getResultSet();
733
      while (rs.next())//check every entry for one user
734
      {
735
        permissionValueInTable=rs.getInt(1);
736

    
737
        //permission is ok
738
        //the user have a permission to access the file
739
        if (( permissionValueInTable & permissionValue )== permissionValue )
740
        {
741

    
742
           allow=true;//has allow rule entry
743
        }//if
744
      }//while
745
    }//for
746
   }//try
747
   catch (SQLException sqlE)
748
   {
749
     throw sqlE;
750
   }
751
   catch (Exception e)
752
   {
753
     throw e;
754
   }
755
   finally
756
   {
757
     try
758
     {
759
       pStmt.close();
760
     }
761
     finally
762
     {
763
       DBConnectionPool.returnDBConnection(conn, serialNumber);
764
     }
765
   }
766
    return allow;
767
 }//hasAllowRule
768

    
769

    
770

    
771
   /**
772
    * Check if the users array has explicit deny rules for given users, docid
773
    * and permission. That means the perm_type is deny and current time is
774
    * less than end_time and greater than begin time, or no time limit.
775
    * @param principals, list of names of principals to check for
776
    * @param docid, document identifier to check for
777
    * @param permission, the permssion need to check
778
    */
779
  private boolean hasExplicitDenyRule(String [] principals, String docId,
780
                                      int permission, long startId)
781
                  throws SQLException
782
 {
783
   int lengthOfArray=principals.length;
784
   ResultSet rs;
785
   PreparedStatement pStmt = null;
786
   int permissionValue=permission;
787
   int permissionValueInTable;
788
   DBConnection conn = null;
789
   int serialNumber = -1;
790
   String sql = null;
791
   boolean topLevel = false;
792

    
793
   // decide top level or subtree level
794
   if (startId == TOPLEVELSTARTNODEID)
795
   {
796
     topLevel = true;
797
     sql = "SELECT permission FROM xml_access WHERE docid = ? " +
798
    "AND lower(principal_name) = ? AND perm_type = ? AND startnodeid is NULL";
799
   }
800
   else
801
   {
802
     topLevel = false;
803
     sql = "SELECT permission FROM xml_access WHERE docid = ? " +
804
     "AND lower(principal_name) = ? AND perm_type = ? AND startnodeid = ?";
805
   }
806

    
807
   try
808
   {
809
     //check out DBConnection
810
     conn=DBConnectionPool.getDBConnection("PermissionControl.hasExplicitDeny");
811
     serialNumber=conn.getCheckOutSerialNumber();
812

    
813
     pStmt = conn.prepareStatement(sql);
814
    //bind docid, perm_type
815
    pStmt.setString(1, docId);
816
    pStmt.setString(3, AccessControlInterface.DENY);
817

    
818
    // subtree level need to set up subtreeid
819
    if (!topLevel)
820
    {
821
      pStmt.setLong(4, startId);
822
    }
823

    
824
    //bind every elenment in user name array
825
    for (int i=0;i<lengthOfArray; i++)
826
    {
827
      pStmt.setString(2, principals[i]);
828
      pStmt.execute();
829
      rs=pStmt.getResultSet();
830
      while (rs.next())//check every entry for one user
831
      {
832
        permissionValueInTable=rs.getInt(1);
833

    
834
        //permission is ok the user doesn't have permission to access the file
835
        if (( permissionValueInTable & permissionValue )== permissionValue )
836

    
837
        {
838
           pStmt.close();
839
           return true;
840
         }//if
841
      }//while
842
    }//for
843
   }//try
844
   catch (SQLException e)
845
   {
846
     throw e;
847
   }//catch
848
   finally
849
   {
850
     try
851
     {
852
       pStmt.close();
853
     }
854
     finally
855
     {
856
       DBConnectionPool.returnDBConnection(conn, serialNumber);
857
     }
858
   }//finally
859
   return false;//no deny rule
860
  }//hasExplicitDenyRule
861

    
862

    
863
  /**
864
    * Creat a users pakages to check permssion rule, user itself, public and
865
    * the gourps the user belong will be include in this package
866
    * @param user, the name of user
867
    * @param groups, the string array of the groups that user belong to
868
    */
869
  private String[] createUsersPackage(String user, String [] groups)
870
  {
871
    String [] usersPackage=null;
872
    int lengthOfPackage;
873

    
874
    if (groups!=null)
875
    {
876
      //if gouprs is not null and user is not public, we should create a array
877
      //to store the groups and user and public.
878
      //So the length of userPackage is the length of group plus two
879
      if (!user.equalsIgnoreCase(AccessControlInterface.PUBLIC))
880
      {
881
        lengthOfPackage=(groups.length)+2;
882
        usersPackage=new String [lengthOfPackage];
883
        //the first two elements is user self and public
884
        //in order to ignore case sensitive, we transfer user to lower case
885
        if (user != null)
886
        {
887
          usersPackage[0]= user.toLowerCase();
888
          logMetacat.info("PermissionController.createUsersPackage - after transfer to lower case(not null): "+
889
                                     usersPackage[0]);
890
        }
891
        else
892
        {
893
          usersPackage[0] = user;
894
          usersPackage[0]= user.toLowerCase();
895
          logMetacat.info("PermissionController.createUsersPackage - after transfer to lower case(null): "+
896
                                     usersPackage[0]);
897
        }
898
        usersPackage[1]=AccessControlInterface.PUBLIC;
899
        //put groups element from index 0 to lengthOfPackage-3 into userPackage
900
        //from index 2 to lengthOfPackage-1
901
        for (int i=2; i<lengthOfPackage; i++)
902
        {
903
          //tansfer group to lower case too
904
          if (groups[i-2] != null)
905
          {
906
            usersPackage[i]=groups[i-2].toLowerCase();
907
          }
908
        } //for
909
      }//if user!=public
910
      else//use=public
911
      {
912
        lengthOfPackage=(groups.length)+1;
913
        usersPackage=new String [lengthOfPackage];
914
        //the first lements is public
915
        usersPackage[0]=AccessControlInterface.PUBLIC;
916
        //put groups element from index 0 to lengthOfPackage-2 into userPackage
917
        //from index 1 to lengthOfPackage-1
918
        for (int i=1; i<lengthOfPackage; i++)
919
        {
920
          if (groups[i-1] != null)
921
          {
922
            usersPackage[i]=groups[i-1].toLowerCase();
923
          }
924
        } //for
925
      }//else user=public
926

    
927
    }//if groups!=null
928
    else
929
    {
930
      //because no groups, the userPackage only need two elements
931
      //one is for user, the other is for public
932
      if (!user.equalsIgnoreCase(AccessControlInterface.PUBLIC))
933
      {
934
        lengthOfPackage=2;
935
        usersPackage=new String [lengthOfPackage];
936
        if (user != null)
937
        {
938
          usersPackage[0]=user.toLowerCase();
939
        }
940
        else
941
        {
942
          usersPackage[0]=user;
943
        }
944
        usersPackage[1]=AccessControlInterface.PUBLIC;
945
      }//if user!=public
946
      else //user==public
947
      {
948
        //only put public into array
949
        lengthOfPackage=1;
950
        usersPackage=new String [lengthOfPackage];
951
        usersPackage[0]=AccessControlInterface.PUBLIC;
952
      }
953
    }//else groups==null
954
    return usersPackage;
955
  }//createUsersPackage
956

    
957

    
958
  /**
959
   * A static method to get Hashtable which cointains a inlinedata object list that
960
   * user can't read it. The key is subtree id of inlinedata, the data is
961
   * internal file name for the inline data which is stored as docid
962
   * in xml_access table or data object doc id.
963
   * @param docidWithoutRev, metadata docid which should be the accessfileid
964
   *                         in the table
965
   * @param user , the name of user
966
   * @param groups, the group which the user belong to
967
   */
968
   public static Hashtable<String, String> getUnReadableInlineDataIdList(String docidWithoutRev,
969
                                                   String user, String[] groups,
970
                                                   boolean withRevision)
971
                                                throws McdbException
972
   {
973
     Hashtable<String, String> inlineDataList = getUnAccessableInlineDataIdList(docidWithoutRev,
974
                              user, groups, AccessControlInterface.READSTRING,
975
                              withRevision);
976

    
977
     return inlineDataList;
978
   }
979

    
980
   /**
981
  * A static method to get Hashtable which cointains a inline  data object list that
982
  * user can't overwrite it. The key is subtree id of inline data distrubition,
983
  * the value is internal file name for the inline data which is stored as docid
984
  * in xml_access table or data object doc id.
985
  * @param docidWithoutRev, metadata docid which should be the accessfileid
986
  *                         in the table
987
  * @param user , the name of user
988
  * @param groups, the group which the user belong to
989
  */
990
  public static Hashtable<String, String> getUnWritableInlineDataIdList(String docidWithoutRev,
991
                                                  String user, String[] groups,
992
                                                  boolean withRevision)
993
                                               throws Exception
994
  {
995
    Hashtable<String, String> inlineDataList = getUnAccessableInlineDataIdList(docidWithoutRev,
996
                            user, groups, AccessControlInterface.WRITESTRING,
997
                            withRevision);
998

    
999
    return inlineDataList;
1000
  }
1001

    
1002

    
1003
   /*
1004
    * This method will get hashtable which contains a unaccessable distribution
1005
    * inlinedata object list
1006
    *
1007
    * withRevision is used to get inline id list with or without revision number
1008
    * e.g. when withRevision is true, temp.1.1.1, temp.1.1.2 would be returned
1009
    * otherwise temp.1.1 and temp.1.2 would be returned.
1010
    */
1011
   private static Hashtable<String, String> getUnAccessableInlineDataIdList(String docid,
1012
                               String user, String[] groups, String permission,
1013
                               boolean withRevision)
1014
                             throws McdbException
1015
   {
1016
       Hashtable<String, String> unAccessibleIdList = new Hashtable();
1017
       if (user == null) {
1018
           return unAccessibleIdList;
1019
       }
1020

    
1021
       Hashtable allIdList;
1022
       try {
1023
           allIdList = getAllInlineDataIdList(docid);
1024
       } catch (SQLException e) {
1025
           throw new McdbException(e.getMessage());
1026
       }
1027
       Enumeration<String> en = allIdList.keys();
1028
      while (en.hasMoreElements())
1029
      {
1030
        String subTreeId = (String) en.nextElement();
1031
        String fileId = (String) allIdList.get(subTreeId);
1032
        //Here fileid is internal file id for line data. It stored in docid
1033
        // field in xml_access table. so we don't need to delete rev
1034
        PermissionController controller = new PermissionController(docid, false);
1035
        if (!controller.hasPermissionForInlineData(user, groups, permission, fileId))
1036
        {
1037
            if(withRevision)
1038
            {
1039
                logMetacat.info("PermissionController.getUnAccessableInlineDataIdList - Put subtree id " + subTreeId +
1040
                                         " and " + "inline data file name " +
1041
                                         fileId + " into " + "un" + permission +
1042
                                         " hash");
1043
                unAccessibleIdList.put(subTreeId, fileId);
1044

    
1045
            }
1046
            else
1047
            {
1048
                logMetacat.info("PermissionController.getUnAccessableInlineDataIdList - Put subtree id " + subTreeId +
1049
                                         " and " + "inline data file name " +
1050
                                         DocumentUtil.getInlineDataIdWithoutRev(fileId) +
1051
                                         " into " + "un" + permission +
1052
                                         " hash");
1053
                unAccessibleIdList.put(subTreeId, 
1054
                		DocumentUtil.getInlineDataIdWithoutRev(fileId));
1055
            }
1056
        }
1057
      }
1058
      return unAccessibleIdList;
1059
   }
1060

    
1061

    
1062
   /*
1063
    * This method will get a hash table from xml_access table for all records
1064
    * about the inline data. The key is subtree id and data is a inline internal
1065
    * file name
1066
    */
1067
   private static Hashtable getAllInlineDataIdList(String docid) throws SQLException
1068
   {
1069
     Hashtable inlineDataList = new Hashtable();
1070
     String sql = "SELECT subtreeid, docid FROM xml_access WHERE " +
1071
                   "accessfileid = ? AND subtreeid  IS NOT NULL";
1072
     PreparedStatement pStmt=null;
1073
     ResultSet rs=null;
1074
     DBConnection conn=null;
1075
     int serialNumber=-1;
1076
     try
1077
     {
1078
       //check out DBConnection
1079
       conn=DBConnectionPool.getDBConnection("PermissionControl.getDataSetId");
1080
       serialNumber=conn.getCheckOutSerialNumber();
1081
       pStmt=conn.prepareStatement(sql);
1082
       //bind the value to query
1083
       pStmt.setString(1, docid);
1084
       //execute the query
1085
       pStmt.execute();
1086
       rs=pStmt.getResultSet();
1087
       //process the result
1088
       while(rs.next())
1089
       {
1090
         String subTreeId = rs.getString(1);
1091
         String inlineDataId = rs.getString(2);
1092
         if (subTreeId != null && !subTreeId.trim().equals("") &&
1093
            inlineDataId != null && !inlineDataId.trim().equals(""))
1094
         {
1095
           inlineDataList.put(subTreeId, inlineDataId);
1096
         }
1097
      }//while
1098
     }//try
1099
     finally
1100
     {
1101
       try
1102
       {
1103
         pStmt.close();
1104
       }
1105
       finally
1106
       {
1107
         DBConnectionPool.returnDBConnection(conn, serialNumber);
1108
       }
1109
     }//finally
1110
     return inlineDataList;
1111
   }//getAllInlineDataIdList
1112
}
(50-50/62)