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: 2003-01-16 17:22:50 -0800 (Thu, 16 Jan 2003) $'
12
 * '$Revision: 1368 $'
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
                               implements AccessControlInterface 
57
{
58

    
59
 
60
  private static String sysdate = MetaCatUtil.dbAdapter.getDateTimeFunction();
61
  private static String isnull = MetaCatUtil.dbAdapter.getIsNULLFunction();
62
  
63
  private DBConnection connection;
64
  private String parserName;
65
  private Stack elementStack;
66
  private String server;
67
  private String sep;
68
 
69
  private boolean	processingDTD;
70
  private String  user;
71
  private String[] groups;
72
  private String  aclid;
73
  private int     rev;
74
  private String 	docname;
75
  private String 	doctype;
76
  private String 	systemid;
77

    
78
  private String docurl;
79
  private Vector resourceURL;
80
  private Vector resourceID;
81
  private Vector principal;
82
  private int    permission;
83
  private String permType;
84
  private String permOrder;
85
//  private String publicAcc;
86
  private String beginTime;
87
  private String endTime;
88
  private int    ticketCount;
89
  private int    serverCode = 1;
90

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

    
107
  
108

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

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

    
150
    // Initialize the parse
151
    XMLReader parser = initializeParser();
152
    // parse the access file and write the info to xml_access
153
    parser.parse(new InputSource(new StringReader(acl)));
154
    
155
  }
156

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

    
182
    // Get an instance of the parser
183
    parser = XMLReaderFactory.createXMLReader(parserName);
184

    
185
    // Turn off validation
186
    parser.setFeature("http://xml.org/sax/features/validation", true);
187
      
188
    // Set Handlers in the parser
189
    // Set the ContentHandler to this instance
190
    parser.setContentHandler((ContentHandler)this);
191

    
192
    // make a DBEntityResolver instance
193
    // Set the EntityReslover to DBEntityResolver instance
194
    EntityResolver eresolver = new DBEntityResolver(connection,this,null);
195
    parser.setEntityResolver((EntityResolver)eresolver);
196

    
197
    // Set the ErrorHandler to this instance
198
    parser.setErrorHandler((ErrorHandler)this);
199

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

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

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

    
272
      if (currentTag.equals("principal")) {
273

    
274
        principal.addElement(inputString);
275

    
276
      } else if (currentTag.equals("permission")) {
277

    
278
        if ( inputString.trim().toUpperCase().equals("READ") ) {
279
          permission = permission | READ;
280
        } else if ( inputString.trim().toUpperCase().equals("WRITE") ) {
281
          permission = permission | WRITE;
282
        } else if ( inputString.trim().toUpperCase().equals("CHANGEPERMISSION")) 
283
        {
284
          permission = permission | CHMOD;
285
        } else if ( inputString.trim().toUpperCase().equals("ALL") ) {
286
          permission = permission | ALL;
287
        }/*else{
288
          throw new SAXException("Unknown permission type: " + inputString);
289
        }*/
290

    
291
      } else if ( currentTag.equals("startDate") && beginTime == null ) {
292
        beginTime = inputString.trim();
293

    
294
      } else if ( currentTag.equals("stopDate") && endTime == null) {
295
        endTime = inputString.trim();
296

    
297
      } else if (currentTag.equals("ticketCount") && ticketCount == 0 ) {
298
        try {
299
          ticketCount = (new Integer(inputString.trim())).intValue();
300
        } catch (NumberFormatException nfe) {
301
          throw new SAXException("Wrong integer format for:" + inputString);
302
        }
303
      }
304
  }
305

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

    
318
    if ( leavingTagName.equals("allow") ||
319
         leavingTagName.equals("deny")    ) {
320
      
321
      if ( permission > 0 ) {
322

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

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

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

    
360
  }
361

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

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

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

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

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

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

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

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

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

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

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

    
646
    return txtPerm.append("\"").toString();
647
  }
648

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

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

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

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

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

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

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

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

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

    
1628
      return output.toString();
1629

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

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

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

    
1759
}
(3-3/50)