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: berkley $'
11
 *     '$Date: 2001-11-20 08:10:33 -0800 (Tue, 20 Nov 2001) $'
12
 * '$Revision: 869 $'
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 String sysdate = MetaCatUtil.dbAdapter.getDateTimeFunction();
62
  private static String isnull = MetaCatUtil.dbAdapter.getIsNULLFunction();
63

    
64
  private Connection conn;
65
  private String parserName;
66
  private Stack elementStack;
67
  private String server;
68
  private String sep;
69

    
70
  private boolean	processingDTD;
71
  private String  user;
72
  private String[] groups;
73
  private String  aclid;
74
  private int     rev;
75
  private String 	docname;
76
  private String 	doctype;
77
  private String 	systemid;
78

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

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

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

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

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

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

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

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

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

    
195
    // Set the ErrorHandler to this instance
196
    parser.setErrorHandler((ErrorHandler)this);
197

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

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

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

    
270
      if (currentTag.equals("principal")) {
271

    
272
        principal.addElement(inputString);
273

    
274
      } else if (currentTag.equals("permission")) {
275

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

    
288
      } else if ( currentTag.equals("startDate") && beginTime == null ) {
289
        beginTime = inputString.trim();
290

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

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

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

    
315
    if ( leavingTagName.equals("allow") ||
316
         leavingTagName.equals("deny")    ) {
317
      
318
      if ( permission > 0 ) {
319

    
320
        // insert into db calculated permission for the list of principals
321
        try {
322
// System.out.println("before insertPermission " +leavingTagName);
323
          // go through the objects in xml_relation about this acl doc
324
          for (int i=0; i < aclObjects.size(); i++) {
325
            // docid of the current object
326
            String docid = (String)aclObjects.elementAt(i); 
327
            DocumentIdentifier docID = new DocumentIdentifier(docid);
328
            docid = docID.getIdentifier();
329
// System.out.println(docid);
330
            insertPermissions(docid,leavingTagName);
331
          }
332

    
333
        } catch (SQLException sqle) {
334
          throw new SAXException(sqle);
335
        } catch (Exception e) {
336
          throw new SAXException(e);
337
        }
338
      }
339

    
340
      // reset the allow/deny permission
341
      principal = new Vector();
342
      permission = 0;
343
      beginTime = null;
344
      endTime = null;
345
      ticketCount = 0;
346
    
347
    }
348

    
349
  }
350

    
351
  /** 
352
    * SAX Handler that receives notification of DOCTYPE. Sets the DTD.
353
    * @param name name of the DTD
354
    * @param publicId Public Identifier of the DTD
355
    * @param systemId System Identifier of the DTD
356
    */
357
  public void startDTD(String name, String publicId, String systemId) 
358
              throws SAXException {
359
    docname = name;
360
    doctype = publicId;
361
    systemid = systemId;
362
  }
363

    
364
  /** 
365
   * SAX Handler that receives notification of the start of entities.
366
   * @param name name of the entity
367
   */
368
  public void startEntity(String name) throws SAXException {
369
    if (name.equals("[dtd]")) {
370
      processingDTD = true;
371
    }
372
  }
373

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

    
384
  /**
385
   * Get the document name.
386
   */
387
  public String getDocname() {
388
    return docname;
389
  }
390

    
391
  /**
392
   * Get the document processing state.
393
   */
394
  public boolean processingDTD() {
395
    return processingDTD;
396
  }
397
  
398
  /* Get all objects associated with @aclid from db.*/
399
  private Vector getACLObjects(String aclid) 
400
          throws SQLException 
401
  {
402
    Vector aclObjects = new Vector();
403
    // delete all acl records for resources related to @aclid if any
404
    PreparedStatement pstmt = conn.prepareStatement(
405
                             "SELECT object FROM xml_relation " +
406
                             "WHERE subject = ? ");
407
    pstmt.setString(1,aclid);
408
    pstmt.execute();
409
    ResultSet rs = pstmt.getResultSet();
410
    boolean hasRows = rs.next();
411
    while (hasRows) {
412
      aclObjects.addElement(rs.getString(1));
413
      hasRows = rs.next();
414
    }
415
    
416
    pstmt.close();
417
    
418
    return aclObjects;
419
  }
420

    
421
  /* Delete from db all permission for resources related to @aclid if any.*/
422
  private void deletePermissionsForRelatedResources(String aclid) 
423
          throws SQLException 
424
  {
425
    // delete all acl records for resources related to @aclid if any
426
    Statement stmt = conn.createStatement();
427
    stmt.execute("DELETE FROM xml_access WHERE accessfileid = '" + aclid + "'");
428
    stmt.close();
429
  }
430

    
431
  /* Insert into db calculated permission for the list of principals */
432
  private void insertPermissions(String docid, String permType ) 
433
          throws SQLException 
434
  {
435
    PreparedStatement pstmt;
436
 
437
    try {
438
      pstmt = conn.prepareStatement(
439
              "INSERT INTO xml_access " + 
440
              "(docid, principal_name, permission, perm_type, perm_order," +
441
              "begin_time,end_time,ticket_count, accessfileid) VALUES " +
442
              "(?,?,?,?,?,to_date(?,'mm/dd/yy'),to_date(?,'mm/dd/yy'),?,?)");
443
      // Bind the values to the query
444
      pstmt.setString(1, docid);
445
      pstmt.setInt(3, permission);
446
      pstmt.setString(4, permType);
447
      pstmt.setString(5, permOrder);
448
      pstmt.setString(6, beginTime);
449
      pstmt.setString(7, endTime);
450
      pstmt.setString(9, aclid);
451
      if ( ticketCount > 0 ) {
452
        pstmt.setString(8, "" + ticketCount);
453
      } else {
454
        pstmt.setString(8, "");
455
      }
456

    
457
      String prName;
458
      for ( int j = 0; j < principal.size(); j++ ) {
459
        prName = (String)principal.elementAt(j);
460
        pstmt.setString(2, prName);
461
        pstmt.execute();
462
      /*    
463
        // check if there are conflict with permission's order
464
        String permOrderOpos = permOrder;
465
        int perm = getPermissions(permission, prName, docid, permOrder);
466
        if (  perm != 0 ) {
467
          if ( permOrder.equals("allowFirst") ) {
468
            permOrderOpos = "denyFirst";
469
          } else if ( permOrder.equals("denyFirst") ) {
470
            permOrderOpos = "allowFirst";
471
          }
472
          throw new SQLException("Permission(s) " + txtValue(perm) + 
473
                    " for \"" + prName + "\" on document #" + docid +
474
                    " has/have been used with \"" + permOrderOpos + "\"");
475
        }
476
      */
477
      }
478
      pstmt.close();
479

    
480
    } catch (SQLException e) {
481
      throw new 
482
      SQLException("AccessControlList.insertPermissions(): " + e.getMessage());
483
    }
484
  }
485

    
486
  /* Get permissions with permission order different than @permOrder. */
487
  private int getPermissions(int permission, String principal,
488
                             String docid, String permOrder)
489
          throws SQLException 
490
  {
491
    PreparedStatement pstmt;
492
    pstmt = conn.prepareStatement(
493
            "SELECT permission FROM xml_access " +
494
            "WHERE docid = ? " +
495
            "AND principal_name = ? " +
496
            "AND perm_order NOT = ?");
497
    pstmt.setString(1, docid);
498
    pstmt.setString(2, principal);
499
    pstmt.setString(3, permOrder);
500
    pstmt.execute();
501
    ResultSet rs = pstmt.getResultSet();
502
    boolean hasRow = rs.next();
503
    int perm = 0;
504
    while ( hasRow ) {
505
      perm = rs.getInt(1);
506
      perm = permission & perm;
507
      if ( perm != 0 ) {
508
        pstmt.close();
509
        return perm;
510
      }
511
      hasRow = rs.next();
512
    }
513
    pstmt.close();
514
    return 0;
515
  }
516

    
517
  /* Get the int value of READ, WRITE or ALL. */
518
  private int intValue ( String permission )
519
  {
520
    if ( permission.equals("READ") ) {
521
      return READ;
522
    } else if ( permission.equals("WRITE") ) {
523
      return WRITE;
524
    } else if ( permission.equals("ALL") ) {
525
      return ALL;
526
    }
527
    
528
    return -1;
529
  }
530

    
531
  /* Get the text value of READ, WRITE or ALL. */
532
  private String txtValue ( int permission )
533
  {
534
    StringBuffer txtPerm = new StringBuffer("\"");
535
    if ( (permission & READ) != 0 ) {
536
      txtPerm.append("read");
537
    } 
538
    if ( (permission & WRITE) != 0 ) {
539
      if ( txtPerm.length() > 0 ) txtPerm.append(",");
540
      txtPerm.append("write");
541
    }
542
    if ( (permission & ALL) != 0 ) {
543
      if ( txtPerm.length() > 0 ) txtPerm.append(",");
544
      txtPerm.append("all");
545
    }
546

    
547
    return txtPerm.append("\"").toString();
548
  }
549

    
550
  /**
551
    * Check from db connection if at least one of the list of @principals
552
    * has @permission on @docid.
553
    * @param permission permission type to check for
554
    * @param principals list of names of principals to check for @permission
555
    * @param docid document identifier to check on
556
    */
557
  public boolean hasPermission(String permission, String user,
558
                               String[] groups, String docid )
559
                 throws SQLException
560
  {
561
    // b' of the command line invocation
562
    if ( (user == null) && (groups == null || groups.length == 0) ) {
563
      return true;
564
    }
565
    
566
    // Check for @permission on @docid for @user and/or @groups
567
    boolean hasPermission = hasPermission(permission,user,docid);
568
    int i = 0;
569
    if ( groups != null ) {
570
      while ( !hasPermission && i<groups.length ) {
571
        hasPermission = hasPermission(permission,groups[i++],docid);
572
      }
573
    }
574
    // Check for @permission for "public" user
575
    if ( !hasPermission ) {
576
      hasPermission = hasPermission(permission,"public",docid);
577
    }
578
    
579
    return hasPermission;
580
  }
581

    
582
  /**
583
    * Check from db connection if @principal has @permission on @docid.
584
    * @param permission permission type to check for
585
    * @param principal name of principal to check for @permission
586
    * @param docid document identifier to check on
587
    */
588
  private boolean hasPermission(String permission,
589
                                String principal, String docid)
590
                 throws SQLException
591
  {
592
    System.out.println("Does " + principal + " have " + permission + " on " + docid);
593
    PreparedStatement pstmt;
594
    // check public access to @docid from xml_documents table
595
    if ( permission.equals("READ") ) {
596
      try {
597
        pstmt = conn.prepareStatement(
598
                "SELECT 'x' FROM xml_documents " +
599
                "WHERE docid = ? AND public_access = 1");
600
        // Bind the values to the query
601
        pstmt.setString(1, docid);
602

    
603
        pstmt.execute();
604
        ResultSet rs = pstmt.getResultSet();
605
        boolean hasRow = rs.next();
606
        pstmt.close();
607
        if (hasRow) {
608
          return true;
609
        }
610

    
611

    
612
      } catch (SQLException e) {
613
        throw new 
614
        SQLException("AccessControlList.hasPermission(). " +
615
                     "Error checking public access for document #"+docid+
616
                     ". " + e.getMessage());
617
      }
618
    }
619
    
620
    // since owner of resource has all permission on it,
621
    // check if @principal is owner of @docid in xml_documents table
622
    if ( principal != null ) {
623
      try {
624
        pstmt = conn.prepareStatement(
625
                "SELECT 'x' FROM xml_documents " +
626
                "WHERE docid = ? AND user_owner = ?");
627
        // Bind the values to the query
628
        pstmt.setString(1, docid);
629
        pstmt.setString(2, principal);
630

    
631
        pstmt.execute();
632
        ResultSet rs = pstmt.getResultSet();
633
        boolean hasRow = rs.next();
634
        pstmt.close();
635
        if (hasRow) {
636
          return true;
637
        }    
638
     
639
      } catch (SQLException e) {
640
        throw new 
641
        SQLException("AccessControlList.hasPermission(). " +
642
                     "Error checking ownership for " + principal +
643
                     " on document #" + docid + ". " + e.getMessage());
644
      }
645
      
646
      //check to see if the file we are checking is an access file and if the
647
      //user that is trying to update it has ALL permissions
648
      try
649
      {
650
        pstmt = conn.prepareStatement("select 'x' from xml_access where " +
651
                                      "accessfileid like '" + docid + 
652
                                      "' and principal_name like '" + principal +
653
                                      "' and perm_type like 'allow' and " +
654
                                      "permission = 7");
655
        pstmt.execute();
656
        ResultSet rs = pstmt.getResultSet();
657
        boolean hasRow = rs.next();
658
        pstmt.close();
659
        if(hasRow)
660
        {
661
          return true;
662
        }
663
      }
664
      catch(SQLException e)
665
      {
666
        throw new SQLException("AccessControlList.hasPermission():2 " +
667
                     "Error checking ownership for " + principal +
668
                     " on document #" + docid + ". " + e.getMessage());
669
      }
670

    
671
      // check @principal's @permission on @docid from xml_access table
672
      int accessValue = 0;
673
      int ticketCount = 0;
674
      String permOrder = "";
675
      try {
676
        pstmt = conn.prepareStatement(
677
                "SELECT permission, perm_order, ticket_count " +
678
                "FROM xml_access " +
679
                "WHERE docid = ? " + 
680
                "AND principal_name = ? " +
681
                "AND perm_type = ? " +
682
                "AND " + sysdate + 
683
                " BETWEEN " + isnull + "(begin_time," + sysdate + ") " +
684
                     "AND " + isnull + "(end_time," + sysdate + ")");
685
        // check if it is "deny" with "allowFirst" first
686
        // Bind the values to the query
687
        pstmt.setString(1, docid);
688
        pstmt.setString(2, principal);
689
        pstmt.setString(3, "deny");
690

    
691
        pstmt.execute();
692
        ResultSet rs = pstmt.getResultSet();
693
        boolean hasRows = rs.next();
694
        while ( hasRows ) {
695
          accessValue = rs.getInt(1);
696
          permOrder = rs.getString(2);
697
          ticketCount = rs.getInt(3);
698
          if ( ( accessValue & intValue(permission) ) == intValue(permission) &&
699
               ( permOrder.equals("allowFirst") ) &&
700
               ( rs.wasNull() || ticketCount > 0 ) ) {
701
            if ( !rs.wasNull() && ticketCount > 0 ) {
702
              decreaseNumberOfAccess(accessValue,principal,docid,"deny","allowFirst");
703
            }
704
            pstmt.close();
705
            return false;
706
          }
707
          hasRows = rs.next();
708
        }
709
//System.out.println("Passed the check for \"deny\" access with \"allowFirst\"");      
710

    
711
        // it is not denied then check if it is "allow"
712
        // Bind the values to the query
713
        pstmt.setString(1, docid);
714
        pstmt.setString(2, principal);
715
        pstmt.setString(3, "allow");
716

    
717
        pstmt.execute();
718
        rs = pstmt.getResultSet();
719
        hasRows = rs.next();
720
        while ( hasRows ) {
721
          accessValue = rs.getInt(1);
722
          permOrder = rs.getString(2);
723
          ticketCount = rs.getInt(3);
724
          if ( ( accessValue & intValue(permission) )==intValue(permission) &&
725
               ( rs.wasNull() || ticketCount > 0 ) ) {
726
            if ( !rs.wasNull() && ticketCount > 0 ) {
727
              decreaseNumberOfAccess(accessValue,principal,docid,"allow",permOrder);
728
            }
729
            pstmt.close();
730
            return true;
731
          }
732
          hasRows = rs.next();
733
        }
734
//System.out.println("Passed the check for \"allow\" access");      
735

    
736
        // it is not allowed then check if it is "deny" with "denyFirst"
737
        // Bind the values to the query
738
        pstmt.setString(1, docid);
739
        pstmt.setString(2, principal);
740
        pstmt.setString(3, "deny");
741

    
742
        pstmt.execute();
743
        rs = pstmt.getResultSet();
744
        hasRows = rs.next();
745
        while ( hasRows ) {
746
          accessValue = rs.getInt(1);
747
          permOrder = rs.getString(2);
748
          ticketCount = rs.getInt(3);
749
          if ( ( accessValue & intValue(permission) ) == intValue(permission) &&
750
               ( permOrder.equals("denyFirst") ) &&
751
               ( rs.wasNull() || ticketCount > 0 ) ) {
752
            if ( !rs.wasNull() && ticketCount > 0 ) {
753
              decreaseNumberOfAccess(accessValue,principal,docid,"deny","denyFirst");
754
            }
755
            pstmt.close();
756
            return false;
757
          }
758
          hasRows = rs.next();
759
        }
760
//System.out.println("Passed the check for \"deny\" access wirh \"denyFirst\"");      
761
      
762
        pstmt.close();
763
        return false;
764
  
765
      } catch (SQLException e) {
766
        throw new 
767
        SQLException("AccessControlList.hasPermission(). " +
768
                     "Error checking " + permission + " permission for " +
769
                     principal + " on document #" + docid + ". " +
770
                     e.getMessage());
771
      }
772
    }
773
    
774
    return false;
775
  }
776

    
777
  /* Decrease the number of access to @docid for @principal in db. */
778
  private void decreaseNumberOfAccess(int permission, String principal,
779
                                      String docid, String permType, 
780
                                      String permOrder) 
781
               throws SQLException
782
  {
783
    PreparedStatement pstmt;
784
    pstmt = conn.prepareStatement(
785
            "UPDATE xml_access SET ticket_count = ticket_count - 1 " +
786
            "WHERE docid = ? " +
787
            "AND principal_name = ? " +
788
            "AND permission = ? " +
789
            "AND perm_type = ? " +
790
            "AND perm_order = ? " +
791
            "AND " + sysdate + 
792
            " BETWEEN " + isnull + "(begin_time," + sysdate + ") " +
793
                 "AND " + isnull + "(end_time," + sysdate + ")");
794
    // Bind the values to the query
795
    pstmt.setString(1, docid);
796
    pstmt.setString(2, principal);
797
    pstmt.setInt(3, permission);
798
    pstmt.setString(4, permType);
799
    pstmt.setString(5, permOrder);
800

    
801
    pstmt.execute();
802
    pstmt.close();
803
  }
804
 
805
 
806
  /**
807
    * Get Access Control List information for document from db connetion.
808
    * User or Group should have permissions for reading
809
    * access control information for a document specified by @docid.
810
    * @param docid document identifier which acl info to get
811
    * @param user name of user connected to Metacat system
812
    * @param groups names of user's groups to which user belongs
813
    */
814
  public String getACL(String docid, String user, String[] groups) 
815
          throws SQLException 
816
  {
817
    StringBuffer output = new StringBuffer();
818
    StringBuffer outTemp = new StringBuffer();
819
    MetaCatUtil util = new MetaCatUtil();
820
    String accDoctype = util.getOption("accessdoctype");
821
    String server = util.getOption("server");
822
    String docurl = "metacat://" + server + "/?docid=" + docid;
823
    String systemID;
824
    boolean isOwned = false;
825
    boolean hasPermission = false;
826
    String publicAcc;
827
    
828
    String acfid = "";
829
    String acfid_prev = "";
830
    String principal;
831
    Vector principalArr = new Vector();
832
    int permission;
833
    int perm_prev = -1;
834
    String permType;
835
    String permOrder = "";
836
    String permOrder_prev = "";
837
    String beginTime = "";
838
    String begin_prev = "";
839
    String endTime = "";
840
    String end_prev = "";
841
    int ticketCount = -1;
842
    int ticket_prev = -1;
843
    
844
    try {
845
      
846
      isOwned = isOwned(docid, user);
847
      systemID = getSystemID(accDoctype);
848
      publicAcc = getPublicAccess(docid);
849
        
850
      output.append("<?xml version=\"1.0\"?>\n");
851
      output.append("<!DOCTYPE acl PUBLIC \"" + accDoctype + "\" \"" +
852
                    systemID + "\">\n");
853
      output.append("<acl authSystem=\"\">\n");
854

    
855
      PreparedStatement pstmt;
856
      pstmt = conn.prepareStatement(
857
              "SELECT distinct accessfileid, principal_name, permission, " +
858
              "perm_type, perm_order, to_char(begin_time,'mm/dd/yyyy'), " +
859
              "to_char(end_time,'mm/dd/yyyy'), ticket_count " +
860
              "FROM xml_access WHERE docid = ? " +
861
              "ORDER BY accessfileid, perm_order, perm_type, permission");
862
      // Bind the values to the query
863
      pstmt.setString(1, docid);
864
      pstmt.execute();
865
      ResultSet rs = pstmt.getResultSet();
866
      boolean hasRows = rs.next();
867
      while (hasRows) {
868

    
869
        acfid = rs.getString(1);
870
        principal = rs.getString(2);
871
        permission = rs.getInt(3);
872
        permType = rs.getString(4);
873
        permOrder = rs.getString(5);
874
        beginTime = rs.getString(6);
875
        endTime = rs.getString(7);
876
        ticketCount = rs.getInt(8);
877

    
878
        // if @docid is not owned by @user, only ACL info from that
879
        // access files to which @user/@groups has "read" permission
880
        // is extracted
881
        if ( !isOwned ) {
882
          if ( !acfid.equals(acfid_prev) ) {
883
            acfid_prev = acfid;
884
            hasPermission = this.hasPermission("READ",user,groups,acfid);
885
          }
886
          if ( !hasPermission ) {
887
            rs.next();
888
            continue;
889
          }
890
        }
891
        
892
        // open <resource> tag
893
        if ( !permOrder.equals(permOrder_prev) ) {
894
          // close </resource> tag if any was opened 
895
          output.append(outTemp.toString());
896
          outTemp = new StringBuffer();
897
          if ( !permOrder_prev.equals("") ) {
898
            output.append("  </resource>\n");
899
          }
900
          output.append("  <resource order=\"" + permOrder + "\" public=\"" +
901
                        publicAcc + "\">\n");
902
          output.append("    <resourceIdentifier>" + docurl + 
903
                        "</resourceIdentifier>\n");
904
          permOrder_prev = permOrder;
905
        }
906
        
907
        // close </allow> or </deny> tag then open new one
908
        if ( permission != perm_prev ||
909
             (endTime == null) && (end_prev != null) ||
910
             (beginTime == null) && (begin_prev != null) ||
911
             endTime != null && !endTime.equals(end_prev)  ||
912
             beginTime != null && !beginTime.equals(begin_prev) ||
913
             ticketCount != ticket_prev )  {
914
          output.append(outTemp.toString());
915
          outTemp = new StringBuffer();
916
          principalArr.removeAllElements();
917
          output.append("    <" + permType + ">\n");
918
        }
919
        
920
        // put all principals here for the same 
921
        // permission, duration and ticket_count
922
        if ( !principalArr.contains(principal) ) {
923
          principalArr.addElement(principal);
924
          output.append("      <principal>" + principal + "</principal>\n");
925
        } 
926
        
927
        // prepare <permission> tags, <duration> and <ticketCount>
928
        // if any to put within <allow> (<deny>) by next cicle
929
        if ( permission != perm_prev || 
930
             (endTime == null) && (end_prev != null) ||
931
             (beginTime == null) && (begin_prev != null) ||
932
             endTime != null && !endTime.equals(end_prev)  ||
933
             beginTime != null && !beginTime.equals(begin_prev) ||
934
             ticketCount != ticket_prev )  {
935
          if ( (permission & READ) != 0 ) {
936
            outTemp.append("      <permission>read</permission>\n");
937
          }
938
          if ( (permission & WRITE) != 0 ) {
939
            outTemp.append("      <permission>write</permission>\n");
940
          }
941
          if ( (permission & ALL) != 0 ) {
942
            outTemp.append("      <permission>all</permission>\n");
943
          }
944
          if ( (beginTime != null) || (endTime != null) ) {
945
            outTemp.append("      <duration>" + beginTime + " " + endTime +
946
                          "</duration>\n");
947
          }
948
          if ( ticketCount > 0 ) {
949
            outTemp.append("      <ticketCount>" + ticketCount + 
950
                          "</ticketCount>\n");
951
          }
952
          outTemp.append("    </" + permType + ">\n");
953
          perm_prev = permission;
954
          ticket_prev = ticketCount;
955
          begin_prev = beginTime;
956
          end_prev = endTime;
957
        }
958
        
959
        hasRows = rs.next();
960
      }
961

    
962
      // close <allow> or <deny> if anything left in outTemp var
963
      output.append(outTemp.toString());
964

    
965
      // If there are no any acl info for @docid accessible by @user/@group,
966
      // extract only the following information
967
      if ( permOrder.equals("") ) {
968
        output.append("  <resource public=\"" + publicAcc + "\">\n");
969
        output.append("    <resourceIdentifier>" + docurl + 
970
                      "</resourceIdentifier>\n");
971
      }
972
      
973
      // always close them
974
      output.append("  </resource>\n");
975
      output.append("</acl>\n");
976
      
977
      pstmt.close();
978

    
979
      return output.toString();
980

    
981
    } catch (SQLException e) {
982
      throw new 
983
      SQLException("AccessControlList.getACL(). " + e.getMessage());
984
    }
985
  }
986
  
987
  /* Check if @user is owner of @docid from db conn. */
988
  private boolean isOwned(String docid, String user) throws SQLException {
989
    
990
    PreparedStatement pstmt;
991
    pstmt = conn.prepareStatement("SELECT 'x' FROM xml_documents " +
992
                                  "WHERE docid = ? " + 
993
                                  "AND user_owner = ?");
994
    pstmt.setString(1, docid);
995
    pstmt.setString(2, user);
996
    pstmt.execute();
997
    ResultSet rs = pstmt.getResultSet();
998
    boolean hasRow = rs.next();
999
    
1000
    return hasRow;
1001
  }
1002

    
1003
  /* Get the flag for public "read" access for @docid from db conn. */
1004
  private String getPublicAccess(String docid) throws SQLException {
1005
    
1006
    int publicAcc = 0;
1007
    PreparedStatement pstmt;
1008
    pstmt = conn.prepareStatement("SELECT public_access FROM xml_documents " +
1009
                                  "WHERE docid = ?");
1010
    pstmt.setString(1, docid);
1011
    pstmt.execute();
1012
    ResultSet rs = pstmt.getResultSet();
1013
    boolean hasRow = rs.next();
1014
    if ( hasRow ) {
1015
      publicAcc = rs.getInt(1);
1016
    }
1017
    
1018
    return (publicAcc == 1) ? "yes" : "no";
1019
  }
1020

    
1021
  /* Get SystemID for @publicID from Metacat DB Catalog. */
1022
  private String getSystemID(String publicID) throws SQLException {
1023
    
1024
    String systemID = "";
1025
    PreparedStatement pstmt;
1026
    pstmt = conn.prepareStatement("SELECT system_id FROM xml_catalog " +
1027
                                  "WHERE entry_type = 'DTD' " + 
1028
                                  "AND public_id = ?");
1029
    pstmt.setString(1, publicID);
1030
    pstmt.execute();
1031
    ResultSet rs = pstmt.getResultSet();
1032
    boolean hasRow = rs.next();
1033
    if ( hasRow ) {
1034
      systemID = rs.getString(1);
1035
    }
1036
    
1037
    return systemID;
1038
  }
1039

    
1040
}
(1-1/40)