Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that loads eml-access.xml file containing ACL 
4
 *             for a metadata document into relational DB
5
 *  Copyright: 2000 Regents of the University of California and the
6
 *             National Center for Ecological Analysis and Synthesis
7
 *    Authors: Jivka Bojilova
8
 *    Release: @release@
9
 *
10
 *   '$Author: tao $'
11
 *     '$Date: 2002-06-13 11:37:01 -0700 (Thu, 13 Jun 2002) $'
12
 * '$Revision: 1214 $'
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

    
29
package edu.ucsb.nceas.metacat;
30

    
31
import java.io.*;
32
import java.sql.*;
33
import java.util.Stack;
34
import java.util.Vector;
35
import java.util.Hashtable;
36
import java.net.URL;
37
import java.net.MalformedURLException;
38

    
39
import org.xml.sax.Attributes;
40
import org.xml.sax.InputSource;
41
import org.xml.sax.ContentHandler;
42
import org.xml.sax.EntityResolver;
43
import org.xml.sax.ErrorHandler;
44
import org.xml.sax.SAXException;
45
import org.xml.sax.SAXParseException;
46
import org.xml.sax.XMLReader;
47
import org.xml.sax.helpers.XMLReaderFactory;
48
import org.xml.sax.helpers.DefaultHandler;
49

    
50
/** 
51
 * A Class that loads eml-access.xml file containing ACL for a metadata
52
 * document into relational DB. It extends DefaultHandler class to handle
53
 * SAX parsing events when processing the XML stream.
54
 */
55
public class AccessControlList extends DefaultHandler {
56

    
57
  private static final int CHMOD = 1;
58
  private static final int WRITE = 2;
59
  private static final int READ = 4;
60
  private static final int ALL = 7;
61
  private static final String ALLOWFIRST="allowFirst";
62
  private static final String DENYFIRST="denyFirst";
63
  private static final String ALLOW="allow";
64
  private static final String DENY="deny";
65
  private static final String PUBLIC="public";
66
  private static String sysdate = MetaCatUtil.dbAdapter.getDateTimeFunction();
67
  private static String isnull = MetaCatUtil.dbAdapter.getIsNULLFunction();
68
  
69
  private DBConnection connection;
70
  private String parserName;
71
  private Stack elementStack;
72
  private String server;
73
  private String sep;
74
 
75
  private boolean	processingDTD;
76
  private String  user;
77
  private String[] groups;
78
  private String  aclid;
79
  private int     rev;
80
  private String 	docname;
81
  private String 	doctype;
82
  private String 	systemid;
83

    
84
  private String docurl;
85
  private Vector resourceURL;
86
  private Vector resourceID;
87
  private Vector principal;
88
  private int    permission;
89
  private String permType;
90
  private String permOrder;
91
//  private String publicAcc;
92
  private String beginTime;
93
  private String endTime;
94
  private int    ticketCount;
95
  private int    serverCode = 1;
96

    
97
  private Vector aclObjects = new Vector();
98
  private boolean instarttag = true;
99
  private String tagName = "";
100
  /**
101
   * Construct an instance of the AccessControlList class.
102
   * It is used by the permission check up from DBQuery or DocumentImpl
103
   * and from "getaccesscontrol" action
104
   *
105
   * @param conn the JDBC connection where acl info is get
106
   */
107
  public AccessControlList(DBConnection conn) throws SQLException
108
  {
109
    this.connection = conn;
110
  }
111
  
112

    
113
  
114

    
115
  /**
116
   * Construct an instance of the AccessControlList class.
117
   * It parse acl file and loads acl data into db connection.
118
   *
119
   * @param conn the JDBC connection where acl data are loaded
120
   * @param aclid the Accession# of the document with the acl data
121
   * @param acl the acl file containing acl data
122
   * @param user the user connected to MetaCat servlet and owns the document
123
   * @param groups the groups to which user belongs
124
   * @param serverCode the serverid from xml_replication on which this document
125
   *        resides.
126
   */
127
  public AccessControlList(DBConnection conn, String aclid, //Reader acl,
128
                           String user, String[] groups, int serverCode)
129
                  throws SAXException, IOException, McdbException
130
  {
131
    String parserName = MetaCatUtil.getOption("saxparser");
132
    this.server = MetaCatUtil.getOption("server");
133
    this.sep = MetaCatUtil.getOption("accNumSeparator");
134

    
135
    this.connection = conn;
136
    this.parserName = parserName;
137
    this.processingDTD = false;
138
    this.elementStack = new Stack();
139
    
140
    this.user = user;
141
    this.groups = groups;
142
    this.aclid = aclid;
143
    this.resourceURL = new Vector();
144
    this.resourceID = new Vector();
145
    this.principal = new Vector();
146
    this.permission = 0;
147
    this.ticketCount = 0;
148
  //  this.publicAcc = null;
149
    this.serverCode = serverCode;
150
    
151
    // read the access file from db connection
152
    DocumentImpl acldoc = new DocumentImpl(aclid);
153
    String acl = acldoc.toString();
154
    this.rev = acldoc.getRev();
155

    
156
    // Initialize the parse
157
    XMLReader parser = initializeParser();
158
    // parse the access file and write the info to xml_access
159
    parser.parse(new InputSource(new StringReader(acl)));
160
    
161
  }
162

    
163
// NOT USED
164
//  /**
165
//   * Construct an instance of the AccessControlList class.
166
//   * It parses eml-access file and loads acl data into db connection.
167
//   * It is used from command line execution.
168
//   *
169
//   * @param conn the JDBC connection where acl data are loaded
170
//   * @param docid the Accession# of the document with the acl data
171
//   * @param aclfilename the name of acl file containing acl data
172
//   * @param user the user connected to MetaCat servlet and owns the document
173
//   * @param groups the groups to which user belongs
174
//   */
175
//  public AccessControlList( Connection conn, String aclid, String aclfilename,
176
//                           String user, String[] groups )
177
//                  throws SAXException, IOException, McdbException
178
//  {
179
//    this(conn, aclid, new FileReader(new File(aclfilename).toString()), 
180
//         user, groups, 1);
181
//  }
182
  
183
  /* Set up the SAX parser for reading the XML serialized ACL */
184
  private XMLReader initializeParser() throws SAXException 
185
  {
186
    XMLReader parser = null;
187

    
188
    // Get an instance of the parser
189
    parser = XMLReaderFactory.createXMLReader(parserName);
190

    
191
    // Turn off validation
192
    parser.setFeature("http://xml.org/sax/features/validation", true);
193
      
194
    // Set Handlers in the parser
195
    // Set the ContentHandler to this instance
196
    parser.setContentHandler((ContentHandler)this);
197

    
198
    // make a DBEntityResolver instance
199
    // Set the EntityReslover to DBEntityResolver instance
200
    EntityResolver eresolver = new DBEntityResolver(connection,this,null);
201
    parser.setEntityResolver((EntityResolver)eresolver);
202

    
203
    // Set the ErrorHandler to this instance
204
    parser.setErrorHandler((ErrorHandler)this);
205

    
206
    return parser; 
207
  }
208
  
209
  /**
210
   * Callback method used by the SAX Parser when beginning of the document
211
   */
212
  public void startDocument() throws SAXException 
213
  {
214
    //delete all previously submitted permissions @ relations
215
    //this happens only on UPDATE of the access file
216
    try {
217
      this.aclObjects = getACLObjects(aclid);
218

    
219
      //delete all permissions for resources related to @aclid if any
220
      if ( aclid != null ) {
221
        deletePermissionsForRelatedResources(aclid);
222
      }
223
    } catch (SQLException sqle) {
224
      throw new SAXException(sqle);
225
    }
226
  }
227
  
228
  /**
229
   * Callback method used by the SAX Parser when the start tag of an 
230
   * element is detected. Used in this context to parse and store
231
   * the acl information in class variables.
232
   */
233
  public void startElement (String uri, String localName, 
234
                            String qName, Attributes atts) 
235
         throws SAXException 
236
  {
237
    instarttag = true;
238
    if(localName.equals("allow"))
239
    {
240
      tagName = "allow";
241
    }
242
    else if(localName.equals("deny"))
243
    {
244
      tagName = "deny";
245
    }
246
    BasicNode currentNode = new BasicNode(localName);
247
    if (atts != null) {
248
      int len = atts.getLength();
249
      for (int i = 0; i < len; i++) {
250
        currentNode.setAttribute(atts.getLocalName(i), atts.getValue(i));
251
      }
252
    }
253
    if ( currentNode.getTagName().equals("acl") ) {
254
      permOrder = currentNode.getAttribute("order");
255
    //  publicAcc = currentNode.getAttribute("public");
256
    }
257
    elementStack.push(currentNode); 
258
  }
259

    
260
  /**
261
   * Callback method used by the SAX Parser when the text sequences of an 
262
   * xml stream are detected. Used in this context to parse and store
263
   * the acl information in class variables.
264
   */ 
265
  public void characters(char ch[], int start, int length)
266
         throws SAXException 
267
  {
268
    if(!instarttag)
269
    {
270
      return;
271
    }
272
    String inputString = new String(ch, start, length);
273
    inputString = inputString.trim(); 
274
    //System.out.println("==============inputString: " + inputString);
275
    BasicNode currentNode = (BasicNode)elementStack.peek(); 
276
    String currentTag = currentNode.getTagName();
277

    
278
      if (currentTag.equals("principal")) {
279

    
280
        principal.addElement(inputString);
281

    
282
      } else if (currentTag.equals("permission")) {
283

    
284
        if ( inputString.trim().toUpperCase().equals("READ") ) {
285
          permission = permission | READ;
286
        } else if ( inputString.trim().toUpperCase().equals("WRITE") ) {
287
          permission = permission | WRITE;
288
        } else if ( inputString.trim().toUpperCase().equals("CHANGEPERMISSION")) 
289
        {
290
          permission = permission | CHMOD;
291
        } else if ( inputString.trim().toUpperCase().equals("ALL") ) {
292
          permission = permission | ALL;
293
        }/*else{
294
          throw new SAXException("Unknown permission type: " + inputString);
295
        }*/
296

    
297
      } else if ( currentTag.equals("startDate") && beginTime == null ) {
298
        beginTime = inputString.trim();
299

    
300
      } else if ( currentTag.equals("stopDate") && endTime == null) {
301
        endTime = inputString.trim();
302

    
303
      } else if (currentTag.equals("ticketCount") && ticketCount == 0 ) {
304
        try {
305
          ticketCount = (new Integer(inputString.trim())).intValue();
306
        } catch (NumberFormatException nfe) {
307
          throw new SAXException("Wrong integer format for:" + inputString);
308
        }
309
      }
310
  }
311

    
312
  /**
313
   * Callback method used by the SAX Parser when the end tag of an 
314
   * element is detected. Used in this context to parse and store
315
   * the acl information in class variables.
316
   */
317
  public void endElement (String uri, String localName, String qName)
318
         throws SAXException 
319
  {
320
    instarttag = false;
321
    BasicNode leaving = (BasicNode)elementStack.pop();
322
    String leavingTagName = leaving.getTagName();
323

    
324
    if ( leavingTagName.equals("allow") ||
325
         leavingTagName.equals("deny")    ) {
326
      
327
      if ( permission > 0 ) {
328

    
329
        // insert into db calculated permission for the list of principals
330
        try {
331
          // go through the objects in xml_relation about this acl doc
332
          for (int i=0; i < aclObjects.size(); i++) {
333
            // docid of the current object
334
            String docid = (String)aclObjects.elementAt(i); 
335
            DocumentIdentifier docID = new DocumentIdentifier(docid);
336
            docid = docID.getIdentifier();
337
            insertPermissions(docid,leavingTagName);
338
          }
339
          
340
          //should insert permission for aclid itself into database
341
          DocumentIdentifier aclIdItself = new DocumentIdentifier(aclid);
342
          String aclIdString = aclIdItself.getIdentifier();
343
          insertPermissions(aclIdString,leavingTagName);
344
          
345

    
346
        } catch (SQLException sqle) {
347
          throw new SAXException(sqle);
348
        } catch (Exception e) {
349
          throw new SAXException(e);
350
        }
351
      }
352

    
353
      // reset the allow/deny permission
354
      principal = new Vector();
355
      permission = 0;
356
      beginTime = null;
357
      endTime = null;
358
      ticketCount = 0;
359
    
360
    }
361

    
362
  }
363

    
364
  /** 
365
    * SAX Handler that receives notification of DOCTYPE. Sets the DTD.
366
    * @param name name of the DTD
367
    * @param publicId Public Identifier of the DTD
368
    * @param systemId System Identifier of the DTD
369
    */
370
  public void startDTD(String name, String publicId, String systemId) 
371
              throws SAXException {
372
    docname = name;
373
    doctype = publicId;
374
    systemid = systemId;
375
  }
376

    
377
  /** 
378
   * SAX Handler that receives notification of the start of entities.
379
   * @param name name of the entity
380
   */
381
  public void startEntity(String name) throws SAXException {
382
    if (name.equals("[dtd]")) {
383
      processingDTD = true;
384
    }
385
  }
386

    
387
  /** 
388
   * SAX Handler that receives notification of the end of entities.
389
   * @param name name of the entity
390
   */
391
  public void endEntity(String name) throws SAXException {
392
    if (name.equals("[dtd]")) {
393
      processingDTD = false;
394
    }
395
  }
396

    
397
  /**
398
   * Get the document name.
399
   */
400
  public String getDocname() {
401
    return docname;
402
  }
403

    
404
  /**
405
   * Get the document processing state.
406
   */
407
  public boolean processingDTD() {
408
    return processingDTD;
409
  }
410
  
411
  /* Get all objects associated with @aclid from db.*/
412
  private Vector getACLObjects(String aclid) 
413
          throws SQLException 
414
  {
415
    Vector aclObjects = new Vector();
416
    DBConnection conn = null;
417
    int serialNumber = -1;
418
    PreparedStatement pstmt = null;
419
    try
420
    {
421
      //get connection from DBConnectionPool
422
      conn=DBConnectionPool.getDBConnection("AccessControlList.getACLObject");
423
      serialNumber=conn.getCheckOutSerialNumber();
424
      
425
      // delete all acl records for resources related to @aclid if any
426
      pstmt = conn.prepareStatement(
427
                             "SELECT object FROM xml_relation " +
428
                             "WHERE subject = ? ");
429
      pstmt.setString(1,aclid);
430
      pstmt.execute();
431
      ResultSet rs = pstmt.getResultSet();
432
      boolean hasRows = rs.next();
433
      while (hasRows) {
434
        aclObjects.addElement(rs.getString(1));
435
        hasRows = rs.next();
436
      }//whil
437
    }
438
    catch (SQLException e)
439
    {
440
      throw e;
441
    }
442
    finally
443
    {
444
      try
445
      {
446
        pstmt.close();
447
      }
448
      finally
449
      {
450
        //retrun DBConnection
451
        DBConnectionPool.returnDBConnection(conn,serialNumber);
452
      }
453
    }
454
    
455
    return aclObjects;
456
  }
457

    
458
  /* Delete from db all permission for resources related to @aclid if any.*/
459
  private void deletePermissionsForRelatedResources(String aclid) 
460
          throws SQLException 
461
  {
462
    //DBConnection conn = null;
463
    //int serialNumber = -1;
464
    Statement stmt = null;
465
    try
466
    {
467
      //check out DBConenction
468
      //conn=DBConnectionPool.getDBConnection("AccessControlList.deltePerm");
469
      //serialNumber=conn.getCheckOutSerialNumber();
470
      // delete all acl records for resources related to @aclid if any
471
      stmt = connection.createStatement();
472
      // Increase DBConnection usage count
473
      connection.increaseUsageCount(1);
474
      stmt.execute("DELETE FROM xml_access WHERE accessfileid = '" + aclid 
475
                                                                      + "'");
476
      //increase usageCount!!!!!!
477
      //conn.increaseUsageCount(1);
478
    }
479
    catch (SQLException e)
480
    {
481
      throw e;
482
    }
483
    finally
484
    {
485
      stmt.close();
486
      //retrun DBConnection
487
      //DBConnectionPool.returnDBConnection(conn,serialNumber);
488
    }
489
  }
490

    
491
  /* Insert into db calculated permission for the list of principals 
492
   * The DBConnection it is use is class field. Because we want to keep rollback
493
   * features and it need use same connection
494
  */
495
  
496
  private void insertPermissions(String docid, String permType ) 
497
                                            throws SQLException 
498
  {
499
    PreparedStatement pstmt = null;
500
    //DBConnection conn = null;
501
    //int serialNumber = -1;
502
    try {
503
      //Check out DBConnection
504
      //conn=DBConnectionPool.getDBConnection("AccessControlList.insertPerm");
505
      //serialNumber=conn.getCheckOutSerialNumber();
506
      
507
      pstmt = connection.prepareStatement(
508
              "INSERT INTO xml_access " + 
509
              "(docid, principal_name, permission, perm_type, perm_order," +
510
              "begin_time,end_time,ticket_count, accessfileid) VALUES " +
511
              "(?,?,?,?,?,to_date(?,'mm/dd/yy'),to_date(?,'mm/dd/yy'),?,?)");
512
      // Increase DBConnection usage count
513
      connection.increaseUsageCount(1);
514
      // Bind the values to the query
515
      pstmt.setString(1, docid);
516
      pstmt.setInt(3, permission);
517
      pstmt.setString(4, permType);
518
      pstmt.setString(5, permOrder);
519
      pstmt.setString(6, beginTime);
520
      pstmt.setString(7, endTime);
521
      pstmt.setString(9, aclid);
522
      if ( ticketCount > 0 ) {
523
        pstmt.setString(8, "" + ticketCount);
524
      } else {
525
        pstmt.setString(8, null);
526
      }
527
      
528
      //incrase usagecount for DBConnection
529
      //conn.increaseUsageCount(1);
530
      String prName;
531
      for ( int j = 0; j < principal.size(); j++ ) {
532
        prName = (String)principal.elementAt(j);
533
        pstmt.setString(2, prName);
534
        pstmt.execute();
535
      /*    
536
        // check if there are conflict with permission's order
537
        String permOrderOpos = permOrder;
538
        int perm = getPermissions(permission, prName, docid, permOrder);
539
        if (  perm != 0 ) {
540
          if ( permOrder.equals("allowFirst") ) {
541
            permOrderOpos = "denyFirst";
542
          } else if ( permOrder.equals("denyFirst") ) {
543
            permOrderOpos = "allowFirst";
544
          }
545
          throw new SQLException("Permission(s) " + txtValue(perm) + 
546
                    " for \"" + prName + "\" on document #" + docid +
547
                    " has/have been used with \"" + permOrderOpos + "\"");
548
        }
549
      */
550
      }
551
      pstmt.close();
552

    
553
    } catch (SQLException e) {
554
      throw new 
555
      SQLException("AccessControlList.insertPermissions(): " + e.getMessage());
556
    }
557
    finally
558
    {
559
      pstmt.close();
560
      //return the DBConnection
561
      //DBConnectionPool.returnDBConnection(conn, serialNumber);
562
    }
563
  }
564

    
565
  /* Get permissions with permission order different than @permOrder. */
566
  private int getPermissions(int permission, String principal,
567
                             String docid, String permOrder)
568
          throws SQLException 
569
  {
570
    PreparedStatement pstmt = null;
571
    DBConnection conn = null;
572
    int serialNumber = -1;
573
    try
574
    {
575
      //check out DBConnection
576
      conn=DBConnectionPool.getDBConnection("AccessControlList.getPermissions");
577
      serialNumber=conn.getCheckOutSerialNumber();
578
      pstmt = conn.prepareStatement(
579
            "SELECT permission FROM xml_access " +
580
            "WHERE docid = ? " +
581
            "AND principal_name = ? " +
582
            "AND perm_order NOT = ?");
583
      pstmt.setString(1, docid);
584
      pstmt.setString(2, principal);
585
      pstmt.setString(3, permOrder);
586
      pstmt.execute();
587
      ResultSet rs = pstmt.getResultSet();
588
      boolean hasRow = rs.next();
589
      int perm = 0;
590
      while ( hasRow ) {
591
        perm = rs.getInt(1);
592
        perm = permission & perm;
593
        if ( perm != 0 ) {
594
          pstmt.close();
595
          return perm;
596
        }
597
        hasRow = rs.next();
598
      }
599
    }//try
600
    catch (SQLException e)
601
    {
602
      throw e;
603
    }
604
    finally
605
    {
606
      try
607
      {
608
        pstmt.close();
609
      }
610
      finally
611
      {
612
        DBConnectionPool.returnDBConnection(conn, serialNumber);
613
      }
614
    }
615
    return 0;
616
  }
617

    
618
  /* Get the int value of READ, WRITE or ALL. */
619
  private static int intValue ( String permission )
620
  {
621
    if ( permission.equals("READ") ) {
622
      return READ;
623
    } else if ( permission.equals("WRITE") ) {
624
      return WRITE;
625
    } else if ( permission.equals("ALL") ) {
626
      return ALL;
627
    }
628
    
629
    return -1;
630
  }
631

    
632
  /* Get the text value of READ, WRITE or ALL. */
633
  private String txtValue ( int permission )
634
  {
635
    StringBuffer txtPerm = new StringBuffer("\"");
636
    if ( (permission & READ) != 0 ) {
637
      txtPerm.append("read");
638
    } 
639
    if ( (permission & WRITE) != 0 ) {
640
      if ( txtPerm.length() > 0 ) txtPerm.append(",");
641
      txtPerm.append("write");
642
    }
643
    if ( (permission & ALL) != 0 ) {
644
      if ( txtPerm.length() > 0 ) txtPerm.append(",");
645
      txtPerm.append("all");
646
    }
647

    
648
    return txtPerm.append("\"").toString();
649
  }
650

    
651
  
652
  /**
653
    * Check if a document id is a access document. Access document need user
654
    * has "all" permission to access it.
655
    * @param docId, the document id need to be checked
656
    */
657
    private static boolean isAccessDocument(String docId) throws SQLException
658
    {
659
      //detele the rev number if docid contains it
660
      docId=MetaCatUtil.getDocIdFromString(docId);
661
      PreparedStatement pStmt=null;
662
      DBConnection conn = null;
663
      int serialNumber = -1;
664
      try
665
      {
666
        //check out DBConnection
667
        conn=DBConnectionPool.getDBConnection("AccessControlList.isAccessDoc");
668
        serialNumber=conn.getCheckOutSerialNumber();
669
        pStmt = conn.prepareStatement("select 'x' from xml_access where " +
670
                                      "accessfileid like '" + docId +  "'");
671
        pStmt.execute();
672
        ResultSet rs = pStmt.getResultSet();
673
        boolean hasRow = rs.next();
674
        pStmt.close();
675
        if(hasRow)
676
        {
677
          return true;
678
        }
679
      }
680
      catch(SQLException e)
681
      {
682
       
683
        throw new SQLException("AccessControlList.isAccessDocument " +
684
                     "Error checking" +
685
                     " on document " + docId + ". " + e.getMessage());
686
      }
687
      finally
688
      {
689
        try
690
        {
691
           pStmt.close();
692
        }
693
        finally
694
        {
695
          DBConnectionPool.returnDBConnection(conn, serialNumber);
696
        }
697
      }
698
      return false;
699
    }//isAccessDocument
700
     
701
  /**
702
    * To create a part of query: "docid like '" +str1+ "', " +"docid like '" 
703
    * +str2+"'" ... We need to check user, group and public together for the 
704
    * permission. So we need the principal in an array and according the array
705
    * to create a part of query which will be used in other methods
706
    * @param principals, a string array storing the username, groups name and
707
    * public.
708
    */
709
   private String partQueryAboutDocId( String [] principals)
710
   {
711
     String partQuery="";
712
     int lengthOfArray=principals.length;
713
     
714
     for (int i=0;i<(lengthOfArray-1);i++)
715
     {
716
        partQuery=partQuery+"docid like '"+principals[i]+"',";
717
     }
718
     
719
     //the last one dosen't has "'"
720
     partQuery=partQuery+"docid like '"+principals[(lengthOfArray-1)]+"'";
721
     return partQuery;
722
     
723
   }
724
  
725
  /**
726
    * Check if a stirng array contains a given documents' owner
727
    * @param principals, a string array storing the username, groups name and
728
    * public.
729
    * @param docid, the id of given documents 
730
    */ 
731
  private static boolean containDocumentOwner( String [] principals, 
732
                                                              String docId)
733
                    throws SQLException
734
  {
735
    int lengthOfArray=principals.length;
736
    boolean hasRow; 
737
    PreparedStatement pStmt=null;
738
    DBConnection conn = null;
739
    int serialNumber = -1;
740
    
741
    try
742
    {
743
      //check out DBConnection
744
     conn=DBConnectionPool.getDBConnection("AccessControlList.containDocOnwer");
745
      serialNumber=conn.getCheckOutSerialNumber();
746
      pStmt = conn.prepareStatement(
747
                "SELECT 'x' FROM xml_documents " +
748
                "WHERE docid = ? AND user_owner = ?"); 
749
      //check every element in the string array too see if it conatains
750
      //the owner of document
751
      for (int i=0; i<lengthOfArray; i++)
752
      {
753
             
754
        // Bind the values to the query
755
        pStmt.setString(1, docId);
756
        pStmt.setString(2, principals[i]);
757

    
758
        pStmt.execute();
759
        ResultSet rs = pStmt.getResultSet();
760
        hasRow = rs.next();
761
        if (hasRow) 
762
        {
763
          pStmt.close();
764
          return true;
765
        }//if    
766
     
767
      }//for
768
    }//try
769
    catch (SQLException e) 
770
    {
771
        pStmt.close();
772
       
773
        throw new 
774
        SQLException("AccessControlList.hasPermission(). " +
775
                     "Error checking ownership for " + principals[0] +
776
                     " on document #" + docId + ". " + e.getMessage());
777
    }//catch
778
    finally
779
    {
780
      try
781
      {
782
        pStmt.close();
783
      }
784
      finally
785
      {
786
        DBConnectionPool.returnDBConnection(conn, serialNumber);
787
      }
788
    }
789
    return false; 
790
  }//containDocumentOwner
791
  
792
  /**
793
    * Check if the permission order for user at that documents is allowFirst
794
    * @param principals, list of names of principals to check for 
795
    * @param docid, document identifier to check for
796
    */
797
  private static boolean isAllowFirst(String [] principals, String docId)
798
                  throws SQLException, Exception
799
  {
800
    int lengthOfArray=principals.length;
801
    boolean hasRow;
802
    PreparedStatement pStmt = null;
803
    DBConnection conn = null;
804
    int serialNumber = -1;
805
    try
806
    {
807
      //check out DBConnection
808
      conn=DBConnectionPool.getDBConnection("AccessControlList.isAllowFirst");
809
      serialNumber=conn.getCheckOutSerialNumber();
810
    
811
      //select permission order from database
812
      pStmt = conn.prepareStatement(
813
                "SELECT perm_order FROM xml_access " +
814
                "WHERE principal_name= ? AND docid = ?");
815
   
816
      //check every name in the array
817
      for (int i=0; i<lengthOfArray;i++)
818
      {
819
        //bind value
820
        pStmt.setString(1, principals[i]);//user name
821
        pStmt.setString(2, docId);//docid
822
    
823
        pStmt.execute();
824
        ResultSet rs = pStmt.getResultSet();
825
        hasRow=rs.next();
826
        if (hasRow)
827
        {
828
          //get the permission order from data base
829
          String permissionOrder=rs.getString(1);
830
          //if the permission order is "allowFirst
831
          if (permissionOrder.equalsIgnoreCase(ALLOWFIRST))
832
          {
833
            pStmt.close();
834
            return true;
835
          }
836
          else
837
          {
838
            pStmt.close();
839
            return false;
840
          }
841
        }//if
842
      }//for
843
    }//try
844
    catch (SQLException e)
845
    {
846
      throw e;
847
    }
848
    finally
849
    {
850
      try
851
      {
852
        pStmt.close();
853
      }
854
      finally
855
      {
856
        DBConnectionPool.returnDBConnection(conn, serialNumber);
857
      }
858
    }
859
    
860
    //if reach here, means there is no permssion record for given names and 
861
    //docid. So throw a exception.
862
    
863
    throw new Exception("There is no permission record for user"+principals[0]+
864
                        "at document "+docId);
865
        
866
  }//isAllowFirst
867
  
868
  /**
869
    * Check if the users array has allow rules for given users, docid and 
870
    * permission.
871
    * If it has permission rule and ticket count is greater than 0, the ticket
872
    * number will decrease one for every allow rule
873
    * @param principals, list of names of principals to check for 
874
    * @param docid, document identifier to check for
875
    * @param permission, the permssion need to check
876
    */
877
  private static boolean hasAllowRule(String [] principals, String docId, 
878
                                  String permission)
879
                  throws SQLException, Exception
880
 {
881
   int lengthOfArray=principals.length;
882
   boolean allow=false;//initial value is no allow rule
883
   ResultSet rs;
884
   PreparedStatement pStmt = null;
885
   int permissionValue=intValue(permission);
886
   int permissionValueInTable;
887
   int ticketCount;
888
   DBConnection conn = null;
889
   int serialNumber = -1;
890
   try
891
   {
892
     //check out DBConnection
893
     conn=DBConnectionPool.getDBConnection("AccessControlList.hasAllowRule");
894
     serialNumber=conn.getCheckOutSerialNumber();
895
    //This sql statement will select entry with 
896
    //begin_time<=currentTime<=end_time in xml_access table
897
    //If begin_time or end_time is null in table, isnull(begin_time, sysdate)
898
    //function will assign begin_time=sysdate
899
    pStmt = conn.prepareStatement(
900
                "SELECT permission, ticket_count " +
901
                "FROM xml_access " +
902
                "WHERE docid = ? " + 
903
                "AND principal_name = ? " +
904
                "AND perm_type = ? " +
905
                "AND " + sysdate + 
906
                " BETWEEN " + isnull + "(begin_time," + sysdate + ") " +
907
                     "AND " + isnull + "(end_time," + sysdate + ")");
908
    //bind docid, perm_type
909
    pStmt.setString(1, docId);
910
    pStmt.setString(3, ALLOW);
911
   
912
    //bind every elenment in user name array
913
    for (int i=0;i<lengthOfArray; i++)
914
    {
915
      pStmt.setString(2, principals[i]);
916
      pStmt.execute();
917
      rs=pStmt.getResultSet();
918
      while (rs.next())//check every entry for one user
919
      {
920
        permissionValueInTable=rs.getInt(1);
921
        ticketCount=rs.getInt(2);
922
       
923
        //permission is ok and ticketcount geat than 0 or ticket is null, 
924
        //the user have a permission to access the file
925
        if ((( permissionValueInTable & permissionValue )== permissionValue )
926
              && (rs.wasNull()||ticketCount > 0))
927
        {
928
           //ticket count should minus one 
929
           //ticketCount isnot null and greater than 0, order is allowfirst
930
           if (!rs.wasNull() && ticketCount>0 && isAllowFirst(principals,docId))
931
           {
932
              decreaseNumberOfAccess(permissionValueInTable, principals[i],
933
                                              docId, ALLOW, ALLOWFIRST);
934
            }
935
           //ticketCount isnot null and greater than 0, order is not allowfirst
936
           if (!rs.wasNull() &&ticketCount>0 && !isAllowFirst(principals,docId))
937
           {
938
              decreaseNumberOfAccess(permissionValueInTable, principals[i],
939
                                              docId, ALLOW, DENYFIRST);
940
           }
941
          
942
           allow=true;//has allow rule entry
943
         }//if
944
      }//while
945
    }//for
946
   }//try
947
   catch (SQLException sqlE)
948
   {
949
     throw sqlE;
950
   }
951
   catch (Exception e)
952
   {
953
     throw e;
954
   }
955
   finally
956
   {
957
     try
958
     {
959
       pStmt.close();
960
     }
961
     finally
962
     {
963
       DBConnectionPool.returnDBConnection(conn, serialNumber);
964
     }
965
   }
966
    return allow;
967
 }//hasAllowRule
968
 
969
 
970
   
971
   /**
972
    * Check if the users array has explicit deny rules for given users, docid 
973
    * and permission. That means the perm_type is deny and current time is
974
    * less than end_time and greater than begin time, or no time limit.
975
    * @param principals, list of names of principals to check for 
976
    * @param docid, document identifier to check for
977
    * @param permission, the permssion need to check
978
    */
979
  private static boolean hasExplicitDenyRule(String [] principals, String docId, 
980
                                  String permission)
981
                  throws SQLException
982
 {
983
   int lengthOfArray=principals.length;
984
   ResultSet rs;
985
   PreparedStatement pStmt = null;
986
   int permissionValue=intValue(permission);
987
   int permissionValueInTable;
988
   DBConnection conn = null;
989
   int serialNumber = -1;
990
   
991
   try
992
   {
993
     //check out DBConnection
994
     conn=DBConnectionPool.getDBConnection("AccessControlList.hasExplicitDeny");
995
     serialNumber=conn.getCheckOutSerialNumber();
996
   
997
    //This sql statement will select entry with 
998
    //begin_time<=currentTime<=end_time in xml_access table
999
    //If begin_time or end_time is null in table, isnull(begin_time, sysdate)
1000
    //function will assign begin_time=sysdate
1001
    pStmt = conn.prepareStatement(
1002
                "SELECT permission " +
1003
                "FROM xml_access " +
1004
                "WHERE docid = ? " + 
1005
                "AND principal_name = ? " +
1006
                "AND perm_type = ? " +
1007
                "AND " + sysdate + 
1008
                " BETWEEN " + isnull + "(begin_time," + sysdate + ") " +
1009
                     "AND " + isnull + "(end_time," + sysdate + ")");
1010
    //bind docid, perm_type
1011
    pStmt.setString(1, docId);
1012
    pStmt.setString(3, DENY);
1013
   
1014
    //bind every elenment in user name array
1015
    for (int i=0;i<lengthOfArray; i++)
1016
    {
1017
      pStmt.setString(2, principals[i]);
1018
      pStmt.execute();
1019
      rs=pStmt.getResultSet();
1020
      while (rs.next())//check every entry for one user
1021
      {
1022
        permissionValueInTable=rs.getInt(1);
1023
        
1024
        //permission is ok the user doesn't have permission to access the file
1025
        if (( permissionValueInTable & permissionValue )== permissionValue )
1026
             
1027
        {
1028
           pStmt.close();
1029
           return true;
1030
         }//if
1031
      }//while
1032
    }//for
1033
   }//try
1034
   catch (SQLException e)
1035
   {
1036
     throw e;
1037
   }//catch
1038
   finally
1039
   {
1040
     try
1041
     {
1042
       pStmt.close();
1043
     }
1044
     finally
1045
     {
1046
       DBConnectionPool.returnDBConnection(conn, serialNumber);
1047
     }
1048
   }//finally
1049
   return false;//no deny rule
1050
  }//hasExplicitDenyRule 
1051
   
1052
   /**
1053
    * Check if the users array has implicit deny rules for given users, docid 
1054
    * and permission. That means the though perm_type is "allow" but current 
1055
    * time is less than begin_time or greater than end time, or ticket count
1056
    * is 0.
1057
    * @param principals, list of names of principals to check for 
1058
    * @param docid, document identifier to check for
1059
    * @param permission, the permssion need to check
1060
    */
1061
  private static boolean hasImplicitDenyRule(String [] principals, String docId, 
1062
                                  String permission)
1063
                  throws SQLException
1064
 {
1065
   int lengthOfArray=principals.length;
1066
   ResultSet rs;
1067
   PreparedStatement pStmt = null;
1068
   int permissionValue=intValue(permission);
1069
   int permissionValueInTable;
1070
   DBConnection conn = null;
1071
   int serialNumber = -1;
1072
   
1073
 
1074
   try
1075
   {
1076
    //check out DBConnection
1077
    conn=DBConnectionPool.getDBConnection("AccessControlList.hasImplicitDeny");
1078
    serialNumber=conn.getCheckOutSerialNumber();
1079
    //This sql statement will select entry with  perm_type =allow and
1080
    //currentTime is less than begin_time or greater than end time
1081
    //in xml_access table. This is an implicit deny rule (allow is out of date) 
1082
    pStmt = conn.prepareStatement(
1083
                "SELECT permission " +
1084
                "FROM xml_access " +
1085
                "WHERE docid = ? " + 
1086
                "AND principal_name = ? " +
1087
                "AND perm_type = ? " +
1088
                "AND " + sysdate + 
1089
                " < " + isnull + "(begin_time," + sysdate + ") " +
1090
                "OR " + sysdate + " > "+ isnull + "(end_time," + sysdate + ")");
1091
    //bind docid, perm_type
1092
    pStmt.setString(1, docId);
1093
    pStmt.setString(3, ALLOW);//It is allow
1094
   
1095
    //bind every elenment in user name array
1096
    for (int i=0;i<lengthOfArray; i++)
1097
    {
1098
      pStmt.setString(2, principals[i]);
1099
      pStmt.execute();
1100
      rs=pStmt.getResultSet();
1101
      while (rs.next())//check every entry for one user
1102
      {
1103
        permissionValueInTable=rs.getInt(1);
1104
        
1105
        //permission is ok the user doesn't have permission to access the file
1106
        if (( permissionValueInTable & permissionValue )== permissionValue )
1107
             
1108
        {
1109
           pStmt.close();
1110
           //has a implicit deny rule: allow is out of date
1111
           return true;
1112
         }//if
1113
      }//while
1114
    }//for
1115
    pStmt.close();
1116
    
1117
    //Now, there is no implicit deny rule which is allow is out of date
1118
    //another implicit deny rule need to be check: allow is out of ticketCount
1119
    //ticketCount=0
1120
    pStmt = conn.prepareStatement(
1121
                "SELECT permission " +
1122
                "FROM xml_access " +
1123
                "WHERE docid = ? " + 
1124
                "AND principal_name = ? " +
1125
                "AND perm_type = ? " +
1126
                "AND ticket_count = ?");
1127
    //bind docid, perm_type, ticket_count
1128
    pStmt.setString(1, docId);
1129
    pStmt.setString(3, ALLOW);//It is allow!
1130
    pStmt.setInt(4,0);
1131
    
1132
    //Because this DBConnection used twice in this method. But we only count one
1133
    //when it checked out. So we should increase another one
1134
    conn.increaseUsageCount(1);
1135
    
1136
    //bind every elenment in user name array
1137
    for (int i=0;i<lengthOfArray; i++)
1138
    {
1139
      pStmt.setString(2, principals[i]);
1140
      pStmt.execute();
1141
      rs=pStmt.getResultSet();
1142
      while (rs.next())//check every entry for one user
1143
      {
1144
        permissionValueInTable=rs.getInt(1);
1145
        
1146
        //permission is ok the user doesn't have permission to access the file
1147
        if (( permissionValueInTable & permissionValue )== permissionValue )
1148
             
1149
        {
1150
           
1151
           pStmt.close();
1152
           //has a implicit deny rule: allow is out of ticketCount
1153
           return true;
1154
         }//if
1155
      }//while
1156
    }//for
1157
   }//try
1158
   finally
1159
   {
1160
     
1161
     try
1162
     {
1163
       pStmt.close();
1164
     }
1165
     finally
1166
     {
1167
       DBConnectionPool.returnDBConnection(conn, serialNumber);
1168
     }
1169
   }//finally
1170
    return false;//no implicit deny rule
1171
  }//hasImplicitDenyRule
1172
  
1173
  /**
1174
    * Creat a users pakages to check permssion rule, user itself, public and
1175
    * the gourps the user belong will be include in this package
1176
    * @param user, the name of user
1177
    * @param groups, the string array of the groups that user belong to
1178
    */
1179
  private static String [] createUsersPackage(String user, String [] groups)
1180
  {
1181
    String [] usersPackage=null;
1182
    int lengthOfPackage;
1183
    
1184
    if (groups!=null)
1185
    {
1186
      //if gouprs is not null and user is not public, we should create a array 
1187
      //to store the groups and user and public. 
1188
      //So the length of userPackage is the length of group plus two
1189
      if (!user.equalsIgnoreCase(PUBLIC))
1190
      {
1191
        lengthOfPackage=(groups.length)+2;
1192
        usersPackage=new String [lengthOfPackage];
1193
        //the first two elements is user self and public
1194
        usersPackage[0]=user;
1195
        usersPackage[1]=PUBLIC;
1196
        //put groups element from index 0 to lengthOfPackage-3 into userPackage
1197
        //from index 2 to lengthOfPackage-1
1198
        for (int i=2; i<lengthOfPackage; i++)
1199
        {
1200
          usersPackage[i]=groups[i-2];
1201
        } //for
1202
      }//if user!=public
1203
      else//use=public
1204
      {
1205
        lengthOfPackage=(groups.length)+1;
1206
        usersPackage=new String [lengthOfPackage];
1207
        //the first lements is public
1208
        usersPackage[0]=PUBLIC;
1209
        //put groups element from index 0 to lengthOfPackage-2 into userPackage
1210
        //from index 1 to lengthOfPackage-1
1211
        for (int i=1; i<lengthOfPackage; i++)
1212
        {
1213
          usersPackage[i]=groups[i-1];
1214
        } //for
1215
      }//else user=public
1216
       
1217
    }//if groups!=null
1218
    else
1219
    {
1220
      //because no groups, the userPackage only need two elements
1221
      //one is for user, the other is for public
1222
      if (!user.equalsIgnoreCase(PUBLIC))
1223
      {
1224
        lengthOfPackage=2;
1225
        usersPackage=new String [lengthOfPackage];
1226
        usersPackage[0]=user;
1227
        usersPackage[1]=PUBLIC;
1228
      }//if user!=public
1229
      else //user==public
1230
      {
1231
        //only put public into array
1232
        lengthOfPackage=1;
1233
        usersPackage=new String [lengthOfPackage];
1234
        usersPackage[0]=PUBLIC;
1235
      }
1236
    }//else groups==null
1237
    return usersPackage;
1238
  }//createUsersPackage
1239
 
1240
  /**
1241
    * This method will return a data set id for given access id.
1242
    * @param accessDocId, the accessDocId which need to be found data set id
1243
   */
1244
  private static String getDataSetId(String accessDocId) 
1245
                              throws SQLException
1246
  {
1247
    String dataSetId=null;
1248
    PreparedStatement pStmt=null;
1249
    ResultSet rs=null;
1250
    DBConnection conn=null;
1251
    int serialNumber=-1;
1252
    String query="SELECT docId from xml_relation where subject = ? or "
1253
                                                +"object = ?";
1254
    
1255
    try
1256
    {
1257
      //check out DBConnection
1258
      conn=DBConnectionPool.getDBConnection("AccessControlList.getDataSetId");
1259
      serialNumber=conn.getCheckOutSerialNumber();
1260
      
1261
      pStmt=conn.prepareStatement(query);
1262
      //bind the value to query
1263
      pStmt.setString(1, accessDocId);
1264
      pStmt.setString(2, accessDocId);
1265
      //execute the query
1266
      pStmt.execute();
1267
      rs=pStmt.getResultSet();
1268
      //process the result
1269
      if (rs.next()) //There are some records for the data set id for access id
1270
      {
1271
        dataSetId=rs.getString(1);
1272
      }
1273
      else //No data set id for the given access id in xml_relation table
1274
      {
1275
        dataSetId=null;
1276
      }
1277
    }//try
1278
    finally
1279
    {
1280
      try
1281
      {
1282
        pStmt.close();
1283
      }
1284
      finally
1285
      {
1286
        DBConnectionPool.returnDBConnection(conn, serialNumber);
1287
      }
1288
    }
1289
    return dataSetId;
1290
  }//getDataPackageId() 
1291
  
1292
  /**
1293
    * Check from db connection if at least one of the list of @principals
1294
    * has @permission on @docid.
1295
    * @param permission permission type to check for
1296
    * @param principals list of names of principals to check for @permission
1297
    * @param docid document identifier to check on
1298
    */
1299
  public static boolean hasPermission(String permission, String user,
1300
                               String[] groups, String docId )
1301
                 throws SQLException, Exception
1302
  {
1303
    //detele the rev number if docid contains it
1304
    docId=MetaCatUtil.getDocIdFromString(docId);
1305
    boolean hasPermission=false;
1306
    String [] userPackage=null;
1307
    //for the commnad line invocation
1308
    if ((user==null) && (groups==null || groups.length==0))
1309
    {
1310
      return true;
1311
    }
1312
   
1313
    //create a userpackage including user, public and group member
1314
    userPackage=createUsersPackage(user, groups);
1315
    
1316
    //if the requested document is access documents and requested permission
1317
    //is "write", the user should have "all" right
1318
    if (isAccessDocument(docId) && (intValue(permission)==WRITE))
1319
    {
1320
      hasPermission = hasPermission(userPackage,docId, "ALL");
1321
    }//if
1322
    else //in other situation, just check the request permission
1323
    {
1324
    
1325
      // Check for @permission on @docid for @user and/or @groups
1326
      hasPermission = hasPermission(userPackage,docId, permission);
1327
     
1328
    }//else
1329
    
1330
    return hasPermission;
1331
  }
1332
 
1333
  /**
1334
    * Check from db connection if the users in String array @principals has
1335
    * @permission on @docid* 
1336
    * @param principals, names in userPakcage need to check for @permission
1337
    * @param docid, document identifier to check on
1338
    * @param permission, permission (write or all...) to check for 
1339
    */
1340
  private static boolean hasPermission(String [] principals, String docId,
1341
                                            String permission)
1342
                         throws SQLException
1343
  {
1344
    try 
1345
    {
1346
      //first, if there is a docid owner in user package, return true
1347
      //because doc owner has all permssion 
1348
      if (containDocumentOwner(principals, docId))
1349
      {
1350
          
1351
          return true;
1352
      }
1353
      
1354
      //If there is no owner in user package, checking the table
1355
      //check perm_order
1356
      if (isAllowFirst(principals, docId))
1357
      {
1358
        
1359
        if (hasExplicitDenyRule(principals, docId, permission)||
1360
                            hasImplicitDenyRule(principals, docId, permission))
1361
        {
1362
          //if it is allowfirst and has deny rule(either explicit or implicit)
1363
          //deny access
1364
          return false;
1365
        }//if
1366
        else if ( hasAllowRule(principals, docId, permission))
1367
        {
1368
          //if it is allowfirst and hasn't deny rule and has allow rule
1369
          //allow access
1370
          return true;
1371
        }//else if
1372
        else
1373
        {
1374
          //other situation deny access
1375
          return false;
1376
        }//else
1377
     }//if isAllowFirst
1378
     else //denyFirst
1379
     {
1380
       if (hasAllowRule(principals, docId, permission))
1381
       {
1382
         //if it is denyFirst and has allow rule, allow access
1383
         return true;
1384
       }
1385
       else
1386
       {
1387
         //if it is denyfirst but no allow rule, deny access
1388
         return false;
1389
       }
1390
     }//else denyfirst
1391
    }//try
1392
    catch (Exception e)
1393
    {
1394
      MetaCatUtil.debugMessage("There is a exception in hasPermission method: "
1395
                         +e.getMessage(), 50);
1396
    }
1397
   
1398
    return false;
1399
  }//hasPermission
1400
 
1401

    
1402
  /* Decrease the number of access to @docid for @principal in db. */
1403
  private static void decreaseNumberOfAccess(int permission, String principal,
1404
                                      String docid, String permType, 
1405
                                      String permOrder) 
1406
               throws SQLException
1407
  {
1408
    PreparedStatement pstmt = null;
1409
    DBConnection conn = null;
1410
    int serialNumber = -1;
1411
    try
1412
    {
1413
      //check out DBConnection
1414
      conn=DBConnectionPool.getDBConnection("AccessControlList.decreaseNumOfA");
1415
      serialNumber=conn.getCheckOutSerialNumber();
1416
      
1417
      pstmt = conn.prepareStatement(
1418
            "UPDATE xml_access SET ticket_count = ticket_count - 1 " +
1419
            "WHERE docid = ? " +
1420
            "AND principal_name = ? " +
1421
            "AND permission = ? " +
1422
            "AND perm_type = ? " +
1423
            "AND perm_order = ? " +
1424
            "AND " + sysdate + 
1425
            " BETWEEN " + isnull + "(begin_time," + sysdate + ") " +
1426
                 "AND " + isnull + "(end_time," + sysdate + ")");
1427
      // Bind the values to the query
1428
      pstmt.setString(1, docid);
1429
      pstmt.setString(2, principal);
1430
      pstmt.setInt(3, permission);
1431
      pstmt.setString(4, permType);
1432
      pstmt.setString(5, permOrder);
1433

    
1434
      pstmt.execute();
1435
    }//try
1436
    finally
1437
    {
1438
      try
1439
      {
1440
        pstmt.close();
1441
      }
1442
      finally
1443
      {
1444
        DBConnectionPool.returnDBConnection(conn, serialNumber);
1445
      }
1446
    }//finally
1447
  }
1448
 
1449
 
1450
  /**
1451
    * Get Access Control List information for document from db connetion.
1452
    * User or Group should have permissions for reading
1453
    * access control information for a document specified by @docid.
1454
    * @param docid document identifier which acl info to get
1455
    * @param user name of user connected to Metacat system
1456
    * @param groups names of user's groups to which user belongs
1457
    */
1458
  public String getACL(String docid, String user, String[] groups) 
1459
          throws SQLException, Exception
1460
  {
1461
    StringBuffer output = new StringBuffer();
1462
    StringBuffer outTemp = new StringBuffer();
1463
    MetaCatUtil util = new MetaCatUtil();
1464
    String accDoctype = util.getOption("accessdoctype");
1465
    String server = util.getOption("server");
1466
    String docurl = "metacat://" + server + "/?docid=" + docid;
1467
    String systemID;
1468
    boolean isOwned = false;
1469
    boolean hasPermission = false;
1470
    String publicAcc;
1471
    
1472
    String acfid = "";
1473
    String acfid_prev = "";
1474
    String principal;
1475
    Vector principalArr = new Vector();
1476
    int permission;
1477
    int perm_prev = -1;
1478
    String permType;
1479
    String permOrder = "";
1480
    String permOrder_prev = "";
1481
    String beginTime = "";
1482
    String begin_prev = "";
1483
    String endTime = "";
1484
    String end_prev = "";
1485
    int ticketCount = -1;
1486
    int ticket_prev = -1;
1487
    DBConnection conn = null;
1488
    int serialNumber = -1;
1489
    PreparedStatement pstmt = null;
1490
    try {
1491
      
1492
      //check out DBConnection
1493
      conn=DBConnectionPool.getDBConnection("AccessControlList.getACL");
1494
      serialNumber=conn.getCheckOutSerialNumber();
1495
      
1496
      isOwned = isOwned(docid, user);
1497
      systemID = getSystemID((String)MetaCatUtil.
1498
                                      getOptionList(accDoctype).elementAt(0));
1499
      publicAcc = getPublicAccess(docid);
1500
        
1501
      output.append("<?xml version=\"1.0\"?>\n");
1502
      output.append("<!DOCTYPE acl PUBLIC \"" + accDoctype + "\" \"" +
1503
                    systemID + "\">\n");
1504
      output.append("<acl authSystem=\"\">\n");
1505

    
1506
      
1507
      pstmt = conn.prepareStatement(
1508
              "SELECT distinct accessfileid, principal_name, permission, " +
1509
              "perm_type, perm_order, to_char(begin_time,'mm/dd/yyyy'), " +
1510
              "to_char(end_time,'mm/dd/yyyy'), ticket_count " +
1511
              "FROM xml_access WHERE docid = ? " +
1512
              "ORDER BY accessfileid, perm_order, perm_type, permission");
1513
      // Bind the values to the query
1514
      pstmt.setString(1, docid);
1515
      pstmt.execute();
1516
      ResultSet rs = pstmt.getResultSet();
1517
      boolean hasRows = rs.next();
1518
      while (hasRows) {
1519

    
1520
        acfid = rs.getString(1);
1521
        principal = rs.getString(2);
1522
        permission = rs.getInt(3);
1523
        permType = rs.getString(4);
1524
        permOrder = rs.getString(5);
1525
        beginTime = rs.getString(6);
1526
        endTime = rs.getString(7);
1527
        ticketCount = rs.getInt(8);
1528

    
1529
        // if @docid is not owned by @user, only ACL info from that
1530
        // access files to which @user/@groups has "read" permission
1531
        // is extracted
1532
        if ( !isOwned ) {
1533
          if ( !acfid.equals(acfid_prev) ) {
1534
            acfid_prev = acfid;
1535
            hasPermission = this.hasPermission("READ",user,groups,acfid);
1536
          }
1537
          if ( !hasPermission ) {
1538
            rs.next();
1539
            continue;
1540
          }
1541
        }
1542
        
1543
        // open <resource> tag
1544
        if ( !permOrder.equals(permOrder_prev) ) {
1545
          // close </resource> tag if any was opened 
1546
          output.append(outTemp.toString());
1547
          outTemp = new StringBuffer();
1548
          if ( !permOrder_prev.equals("") ) {
1549
            output.append("  </resource>\n");
1550
          }
1551
          output.append("  <resource order=\"" + permOrder + "\" public=\"" +
1552
                        publicAcc + "\">\n");
1553
          output.append("    <resourceIdentifier>" + docurl + 
1554
                        "</resourceIdentifier>\n");
1555
          permOrder_prev = permOrder;
1556
        }
1557
        
1558
        // close </allow> or </deny> tag then open new one
1559
        if ( permission != perm_prev ||
1560
             (endTime == null) && (end_prev != null) ||
1561
             (beginTime == null) && (begin_prev != null) ||
1562
             endTime != null && !endTime.equals(end_prev)  ||
1563
             beginTime != null && !beginTime.equals(begin_prev) ||
1564
             ticketCount != ticket_prev )  {
1565
          output.append(outTemp.toString());
1566
          outTemp = new StringBuffer();
1567
          principalArr.removeAllElements();
1568
          output.append("    <" + permType + ">\n");
1569
        }
1570
        
1571
        // put all principals here for the same 
1572
        // permission, duration and ticket_count
1573
        if ( !principalArr.contains(principal) ) {
1574
          principalArr.addElement(principal);
1575
          output.append("      <principal>" + principal + "</principal>\n");
1576
        } 
1577
        
1578
        // prepare <permission> tags, <duration> and <ticketCount>
1579
        // if any to put within <allow> (<deny>) by next cicle
1580
        if ( permission != perm_prev || 
1581
             (endTime == null) && (end_prev != null) ||
1582
             (beginTime == null) && (begin_prev != null) ||
1583
             endTime != null && !endTime.equals(end_prev)  ||
1584
             beginTime != null && !beginTime.equals(begin_prev) ||
1585
             ticketCount != ticket_prev )  {
1586
          if ( (permission & READ) != 0 ) {
1587
            outTemp.append("      <permission>read</permission>\n");
1588
          }
1589
          if ( (permission & WRITE) != 0 ) {
1590
            outTemp.append("      <permission>write</permission>\n");
1591
          }
1592
          if ( (permission & ALL) != 0 ) {
1593
            outTemp.append("      <permission>all</permission>\n");
1594
          }
1595
          if ( (beginTime != null) || (endTime != null) ) {
1596
            outTemp.append("      <duration>" + beginTime + " " + endTime +
1597
                          "</duration>\n");
1598
          }
1599
          if ( ticketCount > 0 ) {
1600
            outTemp.append("      <ticketCount>" + ticketCount + 
1601
                          "</ticketCount>\n");
1602
          }
1603
          outTemp.append("    </" + permType + ">\n");
1604
          perm_prev = permission;
1605
          ticket_prev = ticketCount;
1606
          begin_prev = beginTime;
1607
          end_prev = endTime;
1608
        }
1609
        
1610
        hasRows = rs.next();
1611
      }
1612

    
1613
      // close <allow> or <deny> if anything left in outTemp var
1614
      output.append(outTemp.toString());
1615

    
1616
      // If there are no any acl info for @docid accessible by @user/@group,
1617
      // extract only the following information
1618
      if ( permOrder.equals("") ) {
1619
        output.append("  <resource public=\"" + publicAcc + "\">\n");
1620
        output.append("    <resourceIdentifier>" + docurl + 
1621
                      "</resourceIdentifier>\n");
1622
      }
1623
      
1624
      // always close them
1625
      output.append("  </resource>\n");
1626
      output.append("</acl>\n");
1627
      
1628
      pstmt.close();
1629

    
1630
      return output.toString();
1631

    
1632
    } catch (SQLException e) {
1633
      throw new 
1634
      SQLException("AccessControlList.getACL(). " + e.getMessage());
1635
    }
1636
    finally
1637
    {
1638
      try
1639
      {
1640
        pstmt.close();
1641
      }
1642
      finally
1643
      {
1644
        DBConnectionPool.returnDBConnection(conn, serialNumber);
1645
      }
1646
    }
1647
  }
1648
  
1649
  /* Check if @user is owner of @docid from db conn. */
1650
  private boolean isOwned(String docid, String user) throws SQLException {
1651
    
1652
    PreparedStatement pstmt = null;
1653
    DBConnection conn = null;
1654
    int serialNumber = -1;
1655
    try
1656
    {
1657
      //check out DBConnection
1658
      conn=DBConnectionPool.getDBConnection("AccessControlList.isOwned");
1659
      serialNumber=conn.getCheckOutSerialNumber();
1660
      pstmt = conn.prepareStatement("SELECT 'x' FROM xml_documents " +
1661
                                  "WHERE docid = ? " + 
1662
                                  "AND user_owner = ?");
1663
      pstmt.setString(1, docid);
1664
      pstmt.setString(2, user);
1665
      pstmt.execute();
1666
      ResultSet rs = pstmt.getResultSet();
1667
      boolean hasRow = rs.next();
1668
      return hasRow;
1669
    }
1670
    finally
1671
    {
1672
      try
1673
      {
1674
        pstmt.close();
1675
      }
1676
      finally
1677
      {
1678
        DBConnectionPool.returnDBConnection(conn, serialNumber);
1679
      }
1680
    }
1681
  }
1682

    
1683
  /* Get the flag for public "read" access for @docid from db conn. */
1684
  private String getPublicAccess(String docid) throws SQLException {
1685
    
1686
    int publicAcc = 0;
1687
    PreparedStatement pstmt = null;
1688
    DBConnection conn = null;
1689
    int serialNumber = -1;
1690
    try
1691
    {
1692
      //check out DBConnection
1693
      conn=DBConnectionPool.getDBConnection("AccessControlList.getPublicAcces");
1694
      serialNumber=conn.getCheckOutSerialNumber();
1695
      pstmt = conn.prepareStatement("SELECT public_access FROM xml_documents " +
1696
                                  "WHERE docid = ?");
1697
      pstmt.setString(1, docid);
1698
      pstmt.execute();
1699
      ResultSet rs = pstmt.getResultSet();
1700
      boolean hasRow = rs.next();
1701
      if ( hasRow ) {
1702
        publicAcc = rs.getInt(1);
1703
      }
1704
    
1705
      return (publicAcc == 1) ? "yes" : "no";
1706
    }
1707
    finally
1708
    {
1709
      try
1710
      {
1711
        pstmt.close();
1712
      }
1713
      finally
1714
      {
1715
        DBConnectionPool.returnDBConnection(conn, serialNumber);
1716
      }
1717
    }
1718
  }
1719

    
1720
  /* Get SystemID for @publicID from Metacat DB Catalog. */
1721
  private String getSystemID(String publicID) throws SQLException {
1722
    
1723
    String systemID = "";
1724
    PreparedStatement pstmt = null;
1725
    DBConnection conn = null;
1726
    int serialNumber = -1;
1727
    
1728
    try
1729
    {
1730
      //check out DBConnection
1731
      conn=DBConnectionPool.getDBConnection("AccessControlList.getSystemID");
1732
      serialNumber=conn.getCheckOutSerialNumber();
1733
    
1734
      pstmt = conn.prepareStatement("SELECT system_id FROM xml_catalog " +
1735
                                  "WHERE entry_type = 'DTD' " + 
1736
                                  "AND public_id = ?");
1737
      pstmt.setString(1, publicID);
1738
      pstmt.execute();
1739
      ResultSet rs = pstmt.getResultSet();
1740
      boolean hasRow = rs.next();
1741
      if ( hasRow ) {
1742
        systemID = rs.getString(1);
1743
      }
1744
    
1745
      return systemID;
1746
    }//try
1747
    finally
1748
    {
1749
      
1750
      try
1751
      {
1752
        pstmt.close();
1753
      }
1754
      finally
1755
      {
1756
        DBConnectionPool.returnDBConnection(conn, serialNumber);
1757
      }
1758
    }//finally
1759
  }
1760

    
1761
}
(1-1/43)