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: 2015-12-16 14:58:36 -0800 (Wed, 16 Dec 2015) $'
12
 * '$Revision: 9455 $'
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.PreparedStatement;
31
import java.sql.ResultSet;
32
import java.sql.SQLException;
33
import java.util.Enumeration;
34
import java.util.Hashtable;
35
import java.util.Vector;
36

    
37
import org.apache.log4j.Logger;
38
import org.dataone.service.types.v1.Identifier;
39
import org.dataone.service.types.v1.Permission;
40
import org.dataone.service.types.v1.Session;
41
import org.dataone.service.types.v1.Subject;
42
import org.dataone.service.types.v2.SystemMetadata;
43

    
44
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlList;
45
import edu.ucsb.nceas.metacat.database.DBConnection;
46
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
47
import edu.ucsb.nceas.metacat.dataone.D1NodeService;
48
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
49
import edu.ucsb.nceas.metacat.properties.PropertyService;
50
import edu.ucsb.nceas.metacat.service.SessionService;
51
import edu.ucsb.nceas.metacat.shared.MetacatUtilException;
52
import edu.ucsb.nceas.metacat.util.AuthUtil;
53
import edu.ucsb.nceas.metacat.util.DocumentUtil;
54
import edu.ucsb.nceas.metacat.util.MetacatUtil;
55
import edu.ucsb.nceas.metacat.util.SessionData;
56
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
57
import edu.ucsb.nceas.utilities.access.AccessControlInterface;
58

    
59
public class PermissionController
60
{
61
//   private String docId = null;
62
//   private int rev = -1;
63
   
64
   private String guid = null;
65
   
66
   private boolean hasSubTreeAccessControl = false; // flag if has a subtree
67
                                                    // access for this docid
68
   private Vector subTreeList = new Vector();
69

    
70
   private static final long TOPLEVELSTARTNODEID = 0; //if start node is 0, means it is top
71
                                         //level document
72

    
73
   private static Logger logMetacat = Logger.getLogger(PermissionController.class);
74

    
75
   /**
76
	 * Constructor for PermissionController
77
	 * 
78
	 * @param myDocid the docid need to access
79
	 */
80
	public PermissionController(String myDocid) throws McdbException {
81

    
82
		// find the guid if we can
83
		String docId = null;
84
		int rev = -1;
85

    
86
		// get the parts
87
		docId = DocumentUtil.getSmartDocId(myDocid);
88
		rev = DocumentUtil.getRevisionFromAccessionNumber(myDocid);
89

    
90
		// this is what we really want
91
		guid = IdentifierManager.getInstance().getGUID(docId, rev);
92

    
93
	}
94

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

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

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

    
128
    //for the command line invocation and replication
129
    if ((user==null) && (groups==null || groups.length==0))
130
    {
131
      return true;
132
    }
133
    
134
    // for administrators
135
    //see http://bugzilla.ecoinformatics.org/show_bug.cgi?id=4728
136
    try {
137
		if (AuthUtil.isAdministrator(user, groups)) {
138
			return true;
139
		}
140
	} catch (MetacatUtilException e) {
141
		// not much we can do here, except treat them as normal
142
		logMetacat.warn("Error checking for administrator: " + e.getMessage(), e);
143
	}
144
    
145
    // for DataONE rightsHolder permission
146
    boolean isOwner = false;
147
    try {
148
		Session userSession = new Session();
149
		Subject subject = new Subject();
150
		subject.setValue(user);
151
		userSession.setSubject(subject);
152
		Identifier pid = new Identifier();
153
		pid.setValue(guid);
154
		//isOwner = D1NodeService.userHasPermission(userSession, pid, Permission.CHANGE_PERMISSION);
155
		SystemMetadata sysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
156
		isOwner = (sysMeta.getRightsHolder().equals(subject));
157
    } catch (Exception e) {
158
		logMetacat.warn("Error checking for DataONE permissions: " + e.getMessage(), e);
159
		isOwner = false;
160
    }
161
    if (isOwner) {
162
    	return true;
163
    }
164

    
165
    //create a userpackage including user, public and group member
166
    userPackage=createUsersPackage(user, groups);
167

    
168
    //if the requested document is access documents and requested permission
169
    //is "write", the user should have "all" right
170

    
171
    if (isAccessDocument() && (permission == AccessControlInterface.WRITE))
172
    {
173

    
174
      hasPermission = hasPermission(userPackage, 7);// 7 is all permission
175
    }//if
176
    else //in other situation, just check the request permission
177
    {
178
      // Check for @permission on @docid for @user and/or @groups
179
      hasPermission = hasPermission(userPackage, permission);
180
    }//else
181

    
182
    return hasPermission;
183
  }
184

    
185

    
186
  /**
187
    * Check from db connection if the users in String array @principals has
188
    * @permission on @docid*
189
    * @param principals, names in userPakcage need to check for @permission
190
    * @param docid, document identifier to check on
191
    * @param permission, permission (write or all...) to check for
192
    */
193
  private boolean hasPermission(String [] principals, int permission)
194
                         throws SQLException
195
  {
196
    long startId = TOPLEVELSTARTNODEID;// this is for top level, so startid is 0
197
    try
198
    {
199
      //first, if there is a docid owner in user package, return true
200
      //because doc owner has all permssion
201
      if (containDocumentOwner(principals))
202
      {
203

    
204
          return true;
205
      }
206

    
207
      //If there is no owner in user package, checking the table
208
      //check perm_order
209
      if (isAllowFirst(principals, startId))
210
      {
211

    
212
        if (hasExplicitDenyRule(principals, permission, startId))
213
        {
214
          //if it is allowfirst and has deny rule(either explicit )
215
          //deny access
216

    
217
          return false;
218
        }//if
219
        else if ( hasAllowRule(principals, permission, startId))
220
        {
221
          //if it is allowfirst and hasn't deny rule and has allow rule
222
          //allow access
223

    
224
          return true;
225
        }//else if
226
        else
227
        {
228
          //other situation deny access
229

    
230
          return false;
231
        }//else
232
     }//if isAllowFirst
233
     else //denyFirst
234
     {
235
       if (hasAllowRule(principals, permission, startId))
236
       {
237
         //if it is denyFirst and has allow rule, allow access
238
         return true;
239
       }
240
       else
241
       {
242
         //if it is denyfirst but no allow rule, deny access
243
         return false;
244
       }
245
     }//else denyfirst
246
    }//try
247
    catch (Exception e)
248
    {
249
      logMetacat.warn("PermissionController.hasPermission - There is a exception in hasPermission method: "
250
                         +e.getMessage());
251
    }
252

    
253
    return false;
254
  }//hasPermission
255

    
256
  /**
257
   *  Method to check if a person has permission to a inline data file
258
   * @param user String
259
   * @param groups String[]
260
   * @param myPermission String
261
   * @param inlineDataId String
262
   * @throws McdbException
263
   * @return boolean
264
   */
265
  private boolean hasPermissionForInlineData(String user, String[] groups,
266
                                      String myPermission, String inlineDataId)
267
      throws McdbException
268
  {
269
     // this method can call the public method - hasPermission(...)
270
     // the only difference is about the ownership, you couldn't find the owner
271
     // from inlinedataId directly. You should get it from eml document itself
272
     String []userPackage = createUsersPackage(user, groups);
273
     try {
274
        if (containDocumentOwner(userPackage))
275
         {
276
           return true;
277
         }
278
         else
279
         {
280
        	 // is a funky inline data id with the extra part, so we set it manually
281
             PermissionController controller = new PermissionController(guid);
282
             controller.guid = inlineDataId;
283
             return controller.hasPermission(user, groups, myPermission);
284
         }
285
    } catch (SQLException e) {
286
        throw new McdbException(e.getMessage());
287
    }
288
  }
289

    
290
  /**
291
   * The method to determine of a node can be access by a user just by subtree
292
   * access control
293
   */
294
  public boolean hasPermissionForSubTreeNode(String user, String[] groups,
295
                                             String myPermission, long nodeId)
296
                                             throws McdbException
297
  {
298
    boolean flag = true;
299
    // Get unaccessble subtree for this user
300
    Hashtable unaccessableSubTree = hasUnaccessableSubTree(user, groups,
301
                                                           myPermission);
302
    Enumeration en = unaccessableSubTree.elements();
303
    while (en.hasMoreElements())
304
    {
305
      SubTree tree = (SubTree)en.nextElement();
306
      long start = tree.getStartNodeId();
307
      long stop  = tree.getEndNodeId();
308
      // nodeid in unaccessablesubtree, return false
309
      if ( nodeId >= start && nodeId <= stop)
310
      {
311
        flag = false;
312
        break;
313
      }
314
    }
315
    return flag;
316
  }
317
  /**
318
   * This method will return a hasTable of subtree which user doesn't has the
319
   * permssion to access
320
   * @param user  the user name
321
   * @param groups  the groups which the use is in
322
   * @param myPermission  permission type to check for
323
   */
324
  public Hashtable hasUnaccessableSubTree(String user, String[] groups,
325
                                       String myPermission) throws McdbException
326
  {
327
    Hashtable resultUnaccessableSubTree = new Hashtable();
328
    String [] principals=null;
329
    int permission =AccessControlList.intValue(myPermission);
330

    
331
    //for the commnad line invocation return null(no unaccessable subtree)
332
    if ((user==null) && (groups==null || groups.length==0))
333
    {
334
      return resultUnaccessableSubTree;
335
    }
336

    
337
    //create a userpackage including user, public and group member
338
    principals=createUsersPackage(user, groups);
339
    //for the document owner return null(no unaccessable subtree)
340
    try
341
    {
342
      if (containDocumentOwner(principals))
343
      {
344
       return resultUnaccessableSubTree;
345
      }
346
    }
347
    catch (SQLException ee)
348
    {
349
      throw new McdbException(ee);
350
    }
351

    
352
    // go through every subtree which has access control
353
    for (int i = 0; i< subTreeList.size(); i++)
354
    {
355
      SubTree tree = (SubTree)subTreeList.elementAt(i);
356
      long startId = tree.getStartNodeId();
357

    
358

    
359
        try
360
        {
361
          if (isAllowFirst(principals, startId))
362
          {
363

    
364
            if (hasExplicitDenyRule(principals, permission, startId ))
365
            {
366

    
367
             //if it is allowfirst and has deny rule
368
              // put the subtree into unaccessable vector
369
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
370
              {
371
                resultUnaccessableSubTree.put(new Long(startId), tree);
372
              }
373
            }//if
374
            else if ( hasAllowRule(principals, permission, startId))
375
            {
376
              //if it is allowfirst and hasn't deny rule and has allow rule
377
              //allow access do nothing
378

    
379
            }//else if
380
            else
381
            {
382
              //other situation deny access
383
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
384
              {
385
                resultUnaccessableSubTree.put(new Long(startId), tree);
386
              }
387

    
388
            }//else
389
          }//if isAllowFirst
390
          else //denyFirst
391
          {
392
            if (hasAllowRule(principals, permission,startId))
393
            {
394
              //if it is denyFirst and has allow rule, allow access, do nothing
395

    
396
            }
397
            else
398
            {
399
              //if it is denyfirst but no allow rule, deny access
400
              // add into vector
401
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
402
              {
403
                resultUnaccessableSubTree.put(new Long(startId), tree);
404
              }
405
            }
406
          }//else denyfirst
407
        }//try
408
        catch( Exception e)
409
        {
410
          logMetacat.error("PermissionController.hasUnaccessableSubTree - error in PermissionControl.has" +
411
                                   "UnaccessableSubTree "+e.getMessage());
412
          throw new McdbException(e);
413
        }
414

    
415
    }//for
416
    // merge the subtree if a subtree is another subtree'subtree
417
    resultUnaccessableSubTree = mergeEquivalentSubtree(resultUnaccessableSubTree);
418
    return resultUnaccessableSubTree;
419
  }//hasUnaccessableSubtree
420

    
421

    
422
  /*
423
   * A method to merge nested subtree into bigger one. For example subtree b
424
   * is a subtree of subtree a. And user doesn't have read permission for both
425
   * so we only use subtree a is enough.
426
   */
427
  private Hashtable mergeEquivalentSubtree(Hashtable unAccessSubTree)
428
  {
429
    Hashtable newSubTreeHash = new Hashtable();
430
    boolean   needDelete = false;
431
    // check the parameters
432
    if (unAccessSubTree == null || unAccessSubTree.isEmpty())
433
    {
434
      return newSubTreeHash;
435
    }
436
    else
437
    {
438
      // look every subtree start point and stop point, to see if it is embedded
439
      // in another one. If embedded, they are equavelent and we can use bigger
440
      // one to replace smaller one
441
      Enumeration en = unAccessSubTree.elements();
442
      while (en.hasMoreElements())
443
      {
444
        SubTree tree    = (SubTree)en.nextElement();
445
        String  treeId  = tree.getSubTreeId();
446
        long    startId = tree.getStartNodeId();
447
        long    endId   = tree.getEndNodeId();
448

    
449
        Enumeration enu = unAccessSubTree.elements();
450
        while (enu.hasMoreElements())
451
        {
452
          SubTree subTree = (SubTree)enu.nextElement();
453
          String subTreeId= subTree.getSubTreeId();
454
          long   subTreeStartId = subTree.getStartNodeId();
455
          long   subTreeEndId   = subTree.getEndNodeId();
456
          //compare and if the first subtree is a subtree of the second
457
          // one, set neeDelete true
458
          if (startId > subTreeStartId && endId < subTreeEndId)
459
          {
460
            needDelete = true;
461
            logMetacat.info("PermissionController.mergeEquivalentSubtree - the subtree: "+ treeId +
462
                                     " need to be get rid of from unaccessable"+
463
                                     " subtree list becuase it is a subtree of"+
464
                                     " another subtree in the list");
465
            break;
466
          }//if
467
        }//while
468
        // if not need to delete, put the subtree into hash
469
        if (!needDelete)
470
        {
471
          newSubTreeHash.put(new Long(startId), tree);
472
        }
473
        //reset needDelete
474
        needDelete = false;
475
      }//while
476
      return newSubTreeHash;
477
    }//else
478
  }
479

    
480
  /**
481
	 * Check if a document id is a access document. Access document need user
482
	 * has "all" permission to access it.
483
	 * 
484
	 * @param docId,
485
	 *            the document id need to be checked
486
	 */
487
	private boolean isAccessDocument() throws SQLException {
488
		// get the docid from the guid
489
		String docId = null;
490
		try {
491
			docId = IdentifierManager.getInstance().getLocalId(guid);
492
		} catch (McdbDocNotFoundException e) {
493
			return false;
494
			//throw new SQLException(e);
495
		}
496

    
497
		docId = DocumentUtil.getDocIdFromString(docId);
498
		PreparedStatement pStmt = null;
499
		DBConnection conn = null;
500
		int serialNumber = -1;
501
		try {
502
			// check out DBConnection
503
			conn = DBConnectionPool.getDBConnection("PermissionControl.isAccessDoc");
504
			serialNumber = conn.getCheckOutSerialNumber();
505
			pStmt = conn.prepareStatement("select doctype from xml_documents where docid like ? ");
506
			pStmt.setString(1, docId);
507
			pStmt.execute();
508
			ResultSet rs = pStmt.getResultSet();
509
			boolean hasRow = rs.next();
510
			String doctype = null;
511
			if (hasRow) {
512
				doctype = rs.getString(1);
513

    
514
			}
515
			pStmt.close();
516

    
517
			// if it is an access document
518
			if (doctype != null
519
					&& ((MetacatUtil.getOptionList(PropertyService
520
							.getProperty("xml.accessdoctype")).contains(doctype)))) {
521

    
522
				return true;
523
			}
524

    
525
		} catch (SQLException e) {
526
			throw new SQLException("PermissionControl.isAccessDocument "
527
					+ "Error checking" + " on document " + docId + ". " + e.getMessage());
528
		} catch (PropertyNotFoundException pnfe) {
529
			throw new SQLException("PermissionControl.isAccessDocument "
530
					+ "Error checking" + " on document " + docId + ". " + pnfe.getMessage());
531
		} finally {
532
			try {
533
				pStmt.close();
534
			} finally {
535
				DBConnectionPool.returnDBConnection(conn, serialNumber);
536
			}
537
		}
538

    
539
		return false;
540
	}// isAccessDocument
541

    
542

    
543

    
544
  /**
545
	 * Check if a stirng array contains a given documents' owner
546
	 * 
547
	 * @param principals,
548
	 *            a string array storing the username, groups name and public.
549
	 * @param docid,
550
	 *            the id of given documents
551
	 */
552
  private boolean containDocumentOwner(String[] principals)
553
                    throws SQLException
554
  {
555
	 
556
	// get the docid  
557
	String docId = null;
558
	try {
559
		docId = IdentifierManager.getInstance().getLocalId(guid);
560
		docId = DocumentUtil.getDocIdFromString(docId);
561
	} catch (McdbDocNotFoundException e) {
562
		// should be true if we own the parent doc, but likely won't be checked in that case
563
		return false;
564
	}
565
	
566
    int lengthOfArray=principals.length;
567
    boolean hasRow;
568
    PreparedStatement pStmt=null;
569
    DBConnection conn = null;
570
    int serialNumber = -1;
571

    
572
    try
573
    {
574
      //check out DBConnection
575
     conn=DBConnectionPool.getDBConnection("PermissionControl.containDocOnwer");
576
      serialNumber=conn.getCheckOutSerialNumber();
577
      pStmt = conn.prepareStatement(
578
                "SELECT 'x' FROM xml_documents " +
579
                "WHERE docid = ? AND lower(user_owner) = ? " +
580
                "UNION ALL " +
581
                "SELECT 'x' FROM xml_revisions " +
582
                "WHERE docid = ? AND lower(user_owner) = ? ");
583
      //check every element in the string array too see if it conatains
584
      //the owner of document
585
      for (int i=0; i<lengthOfArray; i++)
586
      {
587

    
588
        // Bind the values to the query
589
        pStmt.setString(1, docId);
590
        pStmt.setString(2, principals[i]);
591
        pStmt.setString(3, docId);
592
        pStmt.setString(4, principals[i]);
593
        logMetacat.info("PermissionController.containDocumentOwner - the principle stack is : " +
594
                                  principals[i]);
595

    
596
        pStmt.execute();
597
        ResultSet rs = pStmt.getResultSet();
598
        hasRow = rs.next();
599
        if (hasRow)
600
        {
601
          pStmt.close();
602
           logMetacat.info("PermissionController.containDocumentOwner - find the owner");
603
          return true;
604
        }//if
605

    
606
      }//for
607
    }//try
608
    catch (SQLException e)
609
    {
610
        pStmt.close();
611

    
612
        throw new
613
        SQLException("PermissionControl.hasPermission - " +
614
                     "Error checking ownership for " + principals[0] +
615
                     " on document #" + docId + ". " + e.getMessage());
616
    }//catch
617
    finally
618
    {
619
      try
620
      {
621
        pStmt.close();
622
      }
623
      finally
624
      {
625
        DBConnectionPool.returnDBConnection(conn, serialNumber);
626
      }
627
    }
628
    return false;
629
  }//containDocumentOwner
630

    
631
  /**
632
    * Check if the permission order for user at that documents is allowFirst
633
    * @param principals, list of names of principals to check for
634
    * @param docid, document identifier to check for
635
    */
636
  private boolean isAllowFirst(String [] principals, long startId)
637
                  throws SQLException, Exception
638
  {
639
    int lengthOfArray=principals.length;
640
    boolean hasRow;
641
    PreparedStatement pStmt = null;
642
    DBConnection conn = null;
643
    int serialNumber = -1;
644
    String sql = null;
645
    boolean topLever =false;
646
    if (startId == TOPLEVELSTARTNODEID)
647
    {
648
      //top level
649
      topLever = true;
650
      sql = 
651
    	  "SELECT perm_order FROM xml_access " +
652
		    "WHERE lower(principal_name) = ? " +
653
		    "AND guid = ? " +
654
		    "AND startnodeid is NULL";
655
    }
656
    else
657
    {
658
      //sub tree level
659
      sql = "SELECT perm_order FROM xml_access " +
660
        "WHERE lower(principal_name) = ? " +
661
        "AND guid = ? " +
662
        "AND startnodeid = ?";
663
    }
664

    
665
    try
666
    {
667
      //check out DBConnection
668
      conn=DBConnectionPool.getDBConnection("AccessControlList.isAllowFirst");
669
      serialNumber=conn.getCheckOutSerialNumber();
670

    
671
      //select permission order from database
672
      pStmt = conn.prepareStatement(sql);
673

    
674
      //check every name in the array
675
      for (int i=0; i<lengthOfArray;i++)
676
      {
677
        //bind value
678
        pStmt.setString(1, principals[i]);//user name
679
        pStmt.setString(2, guid);//guid
680
        
681
        // if subtree, we need set subtree id
682
        if (!topLever)
683
        {
684
          pStmt.setLong(3, startId);
685
        }
686

    
687
        pStmt.execute();
688
        ResultSet rs = pStmt.getResultSet();
689
        hasRow=rs.next();
690
        if (hasRow)
691
        {
692
          //get the permission order from data base
693
          String permissionOrder=rs.getString(1);
694
          //if the permission order is "allowFirst
695
          if (permissionOrder.equalsIgnoreCase(AccessControlInterface.ALLOWFIRST))
696
          {
697
            pStmt.close();
698
            return true;
699
          }
700
          else
701
          {
702
            pStmt.close();
703
            return false;
704
          }
705
        }//if
706
      }//for
707
    }//try
708
    catch (SQLException e)
709
    {
710
      throw e;
711
    }
712
    finally
713
    {
714
      try
715
      {
716
        pStmt.close();
717
      }
718
      finally
719
      {
720
        DBConnectionPool.returnDBConnection(conn, serialNumber);
721
      }
722
    }
723

    
724
    //if reach here, means there is no permssion record for given names and
725
    //docid. So throw a exception.
726

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

    
730
  }//isAllowFirst
731

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

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

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

    
805
        //permission is ok
806
        //the user have a permission to access the file
807
        if (( permissionValueInTable & permissionValue )== permissionValue )
808
        {
809

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

    
837

    
838

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

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

    
883
   try
884
   {
885
     //check out DBConnection
886
     conn=DBConnectionPool.getDBConnection("PermissionControl.hasExplicitDeny");
887
     serialNumber=conn.getCheckOutSerialNumber();
888

    
889
     pStmt = conn.prepareStatement(sql);
890
    //bind docid, perm_type
891
    pStmt.setString(1, guid);
892
    pStmt.setString(3, AccessControlInterface.DENY);
893

    
894
    // subtree level need to set up subtreeid
895
    if (!topLevel)
896
    {
897
      pStmt.setLong(4, startId);
898
    }
899

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

    
910
        //permission is ok the user doesn't have permission to access the file
911
        if (( permissionValueInTable & permissionValue )== permissionValue )
912

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

    
938

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

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

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

    
1033

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

    
1051
     return inlineDataList;
1052
   }
1053

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

    
1072
    return inlineDataList;
1073
  }
1074

    
1075

    
1076
   /**
1077
    * This method will get hashtable which contains a unaccessable distribution
1078
    * inlinedata object list
1079
    *
1080
    */
1081
   private static Hashtable<String, String> getUnAccessableInlineDataIdList(String docid,
1082
                               String user, String[] groups, String permission)
1083
                             throws McdbException
1084
   {
1085
       Hashtable<String, String> unAccessibleIdList = new Hashtable();
1086
       if (user == null) {
1087
           return unAccessibleIdList;
1088
       }
1089

    
1090
       Hashtable allIdList;
1091
       try {
1092
           allIdList = getAllInlineDataIdList(docid);
1093
       } catch (SQLException e) {
1094
           throw new McdbException(e.getMessage());
1095
       }
1096
       Enumeration<String> en = allIdList.keys();
1097
      while (en.hasMoreElements())
1098
      {
1099
        String subTreeId = (String) en.nextElement();
1100
        String fileId = (String) allIdList.get(subTreeId);
1101
        //Here fileid is internal file id for line data. It stored in guid
1102
        // field in xml_access table. 
1103
        PermissionController controller = new PermissionController(docid);
1104
        if (!controller.hasPermissionForInlineData(user, groups, permission, fileId))
1105
        {
1106
            
1107
            logMetacat.info("PermissionController.getUnAccessableInlineDataIdList - Put subtree id " + subTreeId +
1108
                                     " and " + "inline data file name " +
1109
                                     fileId + " into " + "un" + permission +
1110
                                     " hash");
1111
            unAccessibleIdList.put(subTreeId, fileId);
1112

    
1113
                        
1114
        }
1115
      }
1116
      return unAccessibleIdList;
1117
   }
1118

    
1119

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