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: tao $'
11
 *     '$Date: 2016-09-29 14:39:28 -0700 (Thu, 29 Sep 2016) $'
12
 * '$Revision: 9999 $'
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.Arrays;
34
import java.util.Enumeration;
35
import java.util.Hashtable;
36
import java.util.Vector;
37

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

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

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

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

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

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

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

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

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

    
94
	}
95

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

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

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

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

    
169
    logMetacat.debug("Checking permission on " + this.guid + " for user: " + user + " and groups: " + Arrays.toString(groups));
170
    
171
    //create a userpackage including user, public and group member
172
    userPackage=createUsersPackage(user, groups);
173

    
174
    //if the requested document is access documents and requested permission
175
    //is "write", the user should have "all" right
176

    
177
    if (isAccessDocument() && (permission == AccessControlInterface.WRITE))
178
    {
179

    
180
      hasPermission = hasPermission(userPackage, 7);// 7 is all permission
181
    }//if
182
    else //in other situation, just check the request permission
183
    {
184
      // Check for @permission on @docid for @user and/or @groups
185
      hasPermission = hasPermission(userPackage, permission);
186
    }//else
187

    
188
    return hasPermission;
189
  }
190

    
191

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

    
210
          return true;
211
      }
212

    
213
      //If there is no owner in user package, checking the table
214
      //check perm_order
215
      if (isAllowFirst(principals, startId))
216
      {
217

    
218
        if (hasExplicitDenyRule(principals, permission, startId))
219
        {
220
          //if it is allowfirst and has deny rule(either explicit )
221
          //deny access
222

    
223
          return false;
224
        }//if
225
        else if ( hasAllowRule(principals, permission, startId))
226
        {
227
          //if it is allowfirst and hasn't deny rule and has allow rule
228
          //allow access
229

    
230
          return true;
231
        }//else if
232
        else
233
        {
234
          //other situation deny access
235

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

    
259
    return false;
260
  }//hasPermission
261

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

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

    
337
    //for the commnad line invocation return null(no unaccessable subtree)
338
    if ((user==null) && (groups==null || groups.length==0))
339
    {
340
      return resultUnaccessableSubTree;
341
    }
342

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

    
358
    // go through every subtree which has access control
359
    for (int i = 0; i< subTreeList.size(); i++)
360
    {
361
      SubTree tree = (SubTree)subTreeList.elementAt(i);
362
      long startId = tree.getStartNodeId();
363

    
364

    
365
        try
366
        {
367
          if (isAllowFirst(principals, startId))
368
          {
369

    
370
            if (hasExplicitDenyRule(principals, permission, startId ))
371
            {
372

    
373
             //if it is allowfirst and has deny rule
374
              // put the subtree into unaccessable vector
375
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
376
              {
377
                resultUnaccessableSubTree.put(new Long(startId), tree);
378
              }
379
            }//if
380
            else if ( hasAllowRule(principals, permission, startId))
381
            {
382
              //if it is allowfirst and hasn't deny rule and has allow rule
383
              //allow access do nothing
384

    
385
            }//else if
386
            else
387
            {
388
              //other situation deny access
389
              if (!resultUnaccessableSubTree.containsKey(new Long(startId)))
390
              {
391
                resultUnaccessableSubTree.put(new Long(startId), tree);
392
              }
393

    
394
            }//else
395
          }//if isAllowFirst
396
          else //denyFirst
397
          {
398
            if (hasAllowRule(principals, permission,startId))
399
            {
400
              //if it is denyFirst and has allow rule, allow access, do nothing
401

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

    
421
    }//for
422
    // merge the subtree if a subtree is another subtree'subtree
423
    resultUnaccessableSubTree = mergeEquivalentSubtree(resultUnaccessableSubTree);
424
    return resultUnaccessableSubTree;
425
  }//hasUnaccessableSubtree
426

    
427

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

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

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

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

    
520
			}
521
			pStmt.close();
522

    
523
			// if it is an access document
524
			if (doctype != null
525
					&& ((MetacatUtil.getOptionList(PropertyService
526
							.getProperty("xml.accessdoctype")).contains(doctype)))) {
527

    
528
				return true;
529
			}
530

    
531
		} catch (SQLException e) {
532
			throw new SQLException("PermissionControl.isAccessDocument "
533
					+ "Error checking" + " on document " + docId + ". " + e.getMessage());
534
		} catch (PropertyNotFoundException pnfe) {
535
			throw new SQLException("PermissionControl.isAccessDocument "
536
					+ "Error checking" + " on document " + docId + ". " + pnfe.getMessage());
537
		} finally {
538
			try {
539
				pStmt.close();
540
			} finally {
541
				DBConnectionPool.returnDBConnection(conn, serialNumber);
542
			}
543
		}
544

    
545
		return false;
546
	}// isAccessDocument
547

    
548

    
549

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

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

    
594
        // Bind the values to the query
595
        pStmt.setString(1, docId);
596
        pStmt.setString(2, principals[i]);
597
        pStmt.setString(3, docId);
598
        pStmt.setString(4, principals[i]);
599
        logMetacat.info("PermissionController.containDocumentOwner - the principle stack is : " +
600
                                  principals[i]);
601

    
602
        pStmt.execute();
603
        ResultSet rs = pStmt.getResultSet();
604
        hasRow = rs.next();
605
        if (hasRow)
606
        {
607
          pStmt.close();
608
           logMetacat.info("PermissionController.containDocumentOwner - find the owner");
609
          return true;
610
        }//if
611

    
612
      }//for
613
    }//try
614
    catch (SQLException e)
615
    {
616
        pStmt.close();
617

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

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

    
671
    try
672
    {
673
      //check out DBConnection
674
      conn=DBConnectionPool.getDBConnection("AccessControlList.isAllowFirst");
675
      serialNumber=conn.getCheckOutSerialNumber();
676

    
677
      //select permission order from database
678
      pStmt = conn.prepareStatement(sql);
679

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

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

    
730
    //if reach here, means there is no permssion record for given names and
731
    //docid. So throw a exception.
732

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

    
736
  }//isAllowFirst
737

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

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

    
801
    //bind every elenment in user name array
802
    for (int i=0;i<lengthOfArray; i++)
803
    {
804
        logMetacat.debug("Checking permission for principal: " + principals[i] );
805
        logMetacat.debug("SQL: " + pStmt.toString());
806
        
807
      pStmt.setString(2, principals[i]);
808
      pStmt.execute();
809
      rs=pStmt.getResultSet();
810
      while (rs.next())//check every entry for one user
811
      {
812
        permissionValueInTable=rs.getInt(1);
813

    
814
        //permission is ok
815
        //the user have a permission to access the file
816
        if (( permissionValueInTable & permissionValue )== permissionValue )
817
        {
818

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

    
846

    
847

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

    
870
   // decide top level or subtree level
871
   if (startId == TOPLEVELSTARTNODEID)
872
   {
873
     topLevel = true;
874
     sql = "SELECT permission " +
875
     		"FROM xml_access " +
876
     		"WHERE guid = ? " +
877
	     	"AND lower(principal_name) = ? " +
878
	     	"AND perm_type = ? " +
879
	     	"AND startnodeid is NULL";
880
   }
881
   else
882
   {
883
     topLevel = false;
884
     sql = "SELECT permission " +
885
		"FROM xml_access " +
886
		"WHERE guid = ? " +
887
	  	"AND lower(principal_name) = ? " +
888
	  	"AND perm_type = ? " +
889
	  	"AND startnodeid = ?";
890
   }
891

    
892
   try
893
   {
894
     //check out DBConnection
895
     conn=DBConnectionPool.getDBConnection("PermissionControl.hasExplicitDeny");
896
     serialNumber=conn.getCheckOutSerialNumber();
897

    
898
     pStmt = conn.prepareStatement(sql);
899
    //bind docid, perm_type
900
    pStmt.setString(1, guid);
901
    pStmt.setString(3, AccessControlInterface.DENY);
902

    
903
    // subtree level need to set up subtreeid
904
    if (!topLevel)
905
    {
906
      pStmt.setLong(4, startId);
907
    }
908

    
909
    //bind every elenment in user name array
910
    for (int i=0;i<lengthOfArray; i++)
911
    {
912
      pStmt.setString(2, principals[i]);
913
      pStmt.execute();
914
      rs=pStmt.getResultSet();
915
      while (rs.next())//check every entry for one user
916
      {
917
        permissionValueInTable=rs.getInt(1);
918

    
919
        //permission is ok the user doesn't have permission to access the file
920
        if (( permissionValueInTable & permissionValue )== permissionValue )
921

    
922
        {
923
           pStmt.close();
924
           return true;
925
         }//if
926
      }//while
927
    }//for
928
   }//try
929
   catch (SQLException e)
930
   {
931
     throw e;
932
   }//catch
933
   finally
934
   {
935
     try
936
     {
937
       pStmt.close();
938
     }
939
     finally
940
     {
941
       DBConnectionPool.returnDBConnection(conn, serialNumber);
942
     }
943
   }//finally
944
   return false;//no deny rule
945
  }//hasExplicitDenyRule
946

    
947

    
948
  /**
949
    * Creat a users pakages to check permssion rule, user itself, public and
950
    * the gourps the user belong will be include in this package
951
    * @param user, the name of user
952
    * @param groups, the string array of the groups that user belong to
953
    */
954
  private String[] createUsersPackage(String user, String [] groups)
955
  {
956
    String [] usersPackage=null;
957
    int lengthOfPackage;
958

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

    
1012
    }//if groups!=null
1013
    else
1014
    {
1015
      //because no groups, the userPackage only need two elements
1016
      //one is for user, the other is for public
1017
      if (!user.equalsIgnoreCase(AccessControlInterface.PUBLIC))
1018
      {
1019
        lengthOfPackage=2;
1020
        usersPackage=new String [lengthOfPackage];
1021
        if (user != null)
1022
        {
1023
          usersPackage[0]=user.toLowerCase();
1024
        }
1025
        else
1026
        {
1027
          usersPackage[0]=user;
1028
        }
1029
        usersPackage[1]=AccessControlInterface.PUBLIC;
1030
      }//if user!=public
1031
      else //user==public
1032
      {
1033
        //only put public into array
1034
        lengthOfPackage=1;
1035
        usersPackage=new String [lengthOfPackage];
1036
        usersPackage[0]=AccessControlInterface.PUBLIC;
1037
      }
1038
    }//else groups==null
1039
    return usersPackage;
1040
  }//createUsersPackage
1041

    
1042

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

    
1060
     return inlineDataList;
1061
   }
1062

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

    
1081
    return inlineDataList;
1082
  }
1083

    
1084

    
1085
   /**
1086
    * This method will get hashtable which contains a unaccessable distribution
1087
    * inlinedata object list
1088
    *
1089
    */
1090
   private static Hashtable<String, String> getUnAccessableInlineDataIdList(String docid,
1091
                               String user, String[] groups, String permission)
1092
                             throws McdbException
1093
   {
1094
       Hashtable<String, String> unAccessibleIdList = new Hashtable();
1095
       if (user == null) {
1096
           return unAccessibleIdList;
1097
       }
1098

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

    
1122
                        
1123
        }
1124
      }
1125
      return unAccessibleIdList;
1126
   }
1127

    
1128

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