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: berkley $'
11
 *     '$Date: 2010-12-15 11:05:44 -0800 (Wed, 15 Dec 2010) $'
12
 * '$Revision: 5742 $'
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
import edu.ucsb.nceas.metacat.shared.ServiceException;
51

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

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

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

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

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

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

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

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

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

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

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

    
136
    if (isAccessDocument(docId) && (permission == AccessControlInterface.WRITE))
137
    {
138

    
139
      hasPermission = hasPermission(userPackage,docId, 7);// 7 is all permission
140
    }//if
141
    else //in other situation, just check the request permission
142
    {
143
      // Check for @permission on @docid for @user and/or @groups
144
      hasPermission = hasPermission(userPackage,docId, permission);
145
    }//else
146

    
147
    return hasPermission;
148
  }
149

    
150

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

    
170
          return true;
171
      }
172

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

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

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

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

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

    
219
    return false;
220
  }//hasPermission
221

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

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

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

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

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

    
323

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

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

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

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

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

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

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

    
386

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

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

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

    
471
			}
472
			pStmt.close();
473

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

    
479
				return true;
480
			}
481

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

    
496
		return false;
497
	}// isAccessDocument
498

    
499

    
500

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
667
  }//isAllowFirst
668

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

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

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

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

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

    
768

    
769

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

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

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

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

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

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

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

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

    
861

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

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

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

    
956

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

    
976
     return inlineDataList;
977
   }
978

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

    
998
    return inlineDataList;
999
  }
1000

    
1001

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

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

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

    
1060

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