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-12-10 14:05:00 -0800 (Tue, 10 Dec 2002) $'
12
 * '$Revision: 1336 $'
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
          // if acl is not in object list
341
          //should insert permission for aclid itself into database
342
          /*if (!aclObjects.contains(aclid))
343
          {
344
            DocumentIdentifier aclIdItself = new DocumentIdentifier(aclid);
345
            String aclIdString = aclIdItself.getIdentifier();
346
            insertPermissions(aclIdString,leavingTagName);
347
          }*/
348
          
349

    
350
        } catch (SQLException sqle) {
351
          throw new SAXException(sqle);
352
        } catch (Exception e) {
353
          throw new SAXException(e);
354
        }
355
      }
356

    
357
      // reset the allow/deny permission
358
      principal = new Vector();
359
      permission = 0;
360
      beginTime = null;
361
      endTime = null;
362
      ticketCount = 0;
363
    
364
    }
365

    
366
  }
367

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

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

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

    
401
  /**
402
   * Get the document name.
403
   */
404
  public String getDocname() {
405
    return docname;
406
  }
407

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

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

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

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

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

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

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

    
652
    return txtPerm.append("\"").toString();
653
  }
654

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

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

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

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

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

    
1524
        acfid = rs.getString(1);
1525
        principal = rs.getString(2);
1526
        permission = rs.getInt(3);
1527
        permType = rs.getString(4);
1528
        permOrder = rs.getString(5);
1529
        beginTime = rs.getString(6);
1530
        endTime = rs.getString(7);
1531
        ticketCount = rs.getInt(8);
1532

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

    
1617
      // close <allow> or <deny> if anything left in outTemp var
1618
      output.append(outTemp.toString());
1619

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

    
1634
      return output.toString();
1635

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

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

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

    
1765
}
(1-1/48)