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: bojilova $'
11
 *     '$Date: 2001-07-31 19:34:27 -0700 (Tue, 31 Jul 2001) $'
12
 * '$Revision: 815 $'
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
//import edu.ucsb.nceas.dbadapter.AbstractDatabase;
51

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

    
59
  private static final int ALL = 1;
60
  private static final int WRITE = 2;
61
  private static final int READ = 4;
62
  private static String sysdate = MetaCatUtil.dbAdapter.getDateTimeFunction();
63
  private static String isnull = MetaCatUtil.dbAdapter.getIsNULLFunction();
64

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

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

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

    
93
  private Vector aclObjects = new Vector();
94
  
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, ClassNotFoundException,
122
                         Exception
123
  {
124
    String parserName = MetaCatUtil.getOption("saxparser");
125
    this.server = MetaCatUtil.getOption("server");
126
    this.sep = MetaCatUtil.getOption("accNumSeparator");
127

    
128
    this.conn = conn;
129
    this.parserName = parserName;
130
    this.processingDTD = false;
131
    this.elementStack = new Stack();
132
    
133
    this.user = user;
134
    this.groups = groups;
135
    this.aclid = aclid;
136
    DocumentImpl aclinfo = new DocumentImpl(conn,aclid,false);
137
    this.rev = aclinfo.getRev();
138
    this.resourceURL = new Vector();
139
    this.resourceID = new Vector();
140
    this.principal = new Vector();
141
    this.permission = 0;
142
    this.ticketCount = 0;
143
  //  this.publicAcc = null;
144
    this.serverCode = serverCode;
145
    
146
    // Initialize the parser and read the queryspec
147
    XMLReader parser = initializeParser();
148
    parser.parse(new InputSource(acl));
149

    
150
  }
151

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

    
177
    // Get an instance of the parser
178
    parser = XMLReaderFactory.createXMLReader(parserName);
179

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

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

    
192
    // Set the ErrorHandler to this instance
193
    parser.setErrorHandler((ErrorHandler)this);
194

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

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

    
242
  /**
243
   * Callback method used by the SAX Parser when the text sequences of an 
244
   * xml stream are detected. Used in this context to parse and store
245
   * the acl information in class variables.
246
   */
247
  public void characters(char ch[], int start, int length)
248
         throws SAXException 
249
  {
250
    String inputString = new String(ch, start, length);
251
    BasicNode currentNode = (BasicNode)elementStack.peek(); 
252
    String currentTag = currentNode.getTagName();
253

    
254
      if (currentTag.equals("principal")) {
255

    
256
        principal.addElement(inputString);
257

    
258
      } else if (currentTag.equals("permission")) {
259

    
260
        if ( inputString.trim().toUpperCase().equals("READ") ) {
261
          permission = permission | READ;
262
        } else if ( inputString.trim().toUpperCase().equals("WRITE") ) {
263
          permission = permission | WRITE;
264
        } else if ( inputString.trim().toUpperCase().equals("ALL") ) {
265
          permission = permission | ALL;
266
        } else {
267
          throw new SAXException("Unknown permission type: " + inputString);
268
        }
269

    
270
      } else if ( currentTag.equals("startDate") && beginTime == null ) {
271
        beginTime = inputString.trim();
272

    
273
      } else if ( currentTag.equals("stopDate") && endTime == null) {
274
        endTime = inputString.trim();
275

    
276
      } else if (currentTag.equals("ticketCount") && ticketCount == 0 ) {
277
        try {
278
          ticketCount = (new Integer(inputString.trim())).intValue();
279
        } catch (NumberFormatException nfe) {
280
          throw new SAXException("Wrong integer format for:" + inputString);
281
        }
282
      }
283
  }
284

    
285
  /**
286
   * Callback method used by the SAX Parser when the end tag of an 
287
   * element is detected. Used in this context to parse and store
288
   * the acl information in class variables.
289
   */
290
  public void endElement (String uri, String localName, String qName)
291
         throws SAXException 
292
  {
293
    BasicNode leaving = (BasicNode)elementStack.pop();
294
    String leavingTagName = leaving.getTagName();
295

    
296
    if ( leavingTagName.equals("allow") ||
297
         leavingTagName.equals("deny")    ) {
298
      
299
      if ( permission > 0 ) {
300

    
301
        // insert into db calculated permission for the list of principals
302
        try {
303
// System.out.println("before insertPermission " +leavingTagName);
304
          // go through the objects in xml_relation about this acl doc
305
          for (int i=0; i < aclObjects.size(); i++) {
306
            // docid of the current object
307
            String docid = (String)aclObjects.elementAt(i); 
308
            DocumentIdentifier docID = new DocumentIdentifier(docid);
309
            docid = docID.getIdentifier();
310
// System.out.println(docid);
311
            insertPermissions(docid,leavingTagName);
312
          }
313

    
314
        } catch (SQLException sqle) {
315
          throw new SAXException(sqle);
316
        } catch (Exception e) {
317
          throw new SAXException(e);
318
        }
319
      }
320

    
321
      // reset the allow/deny permission
322
      principal = new Vector();
323
      permission = 0;
324
      beginTime = null;
325
      endTime = null;
326
      ticketCount = 0;
327
    
328
    }
329

    
330
  }
331

    
332
  /** 
333
    * SAX Handler that receives notification of DOCTYPE. Sets the DTD.
334
    * @param name name of the DTD
335
    * @param publicId Public Identifier of the DTD
336
    * @param systemId System Identifier of the DTD
337
    */
338
  public void startDTD(String name, String publicId, String systemId) 
339
              throws SAXException {
340
    docname = name;
341
    doctype = publicId;
342
    systemid = systemId;
343
  }
344

    
345
  /** 
346
   * SAX Handler that receives notification of the start of entities.
347
   * @param name name of the entity
348
   */
349
  public void startEntity(String name) throws SAXException {
350
    if (name.equals("[dtd]")) {
351
      processingDTD = true;
352
    }
353
  }
354

    
355
  /** 
356
   * SAX Handler that receives notification of the end of entities.
357
   * @param name name of the entity
358
   */
359
  public void endEntity(String name) throws SAXException {
360
    if (name.equals("[dtd]")) {
361
      processingDTD = false;
362
    }
363
  }
364

    
365
  /**
366
   * Get the document name.
367
   */
368
  public String getDocname() {
369
    return docname;
370
  }
371

    
372
  /**
373
   * Get the document processing state.
374
   */
375
  public boolean processingDTD() {
376
    return processingDTD;
377
  }
378
  
379
  /* Get all objects associated with @aclid from db.*/
380
  private Vector getACLObjects(String aclid) 
381
          throws SQLException 
382
  {
383
    Vector aclObjects = new Vector();
384
    // delete all acl records for resources related to @aclid if any
385
    PreparedStatement pstmt = conn.prepareStatement(
386
                             "SELECT object FROM xml_relation " +
387
                             "WHERE subject = ? " + 
388
                             "AND relationship = 'isRelatedTo'");
389
    pstmt.setString(1,aclid);
390
    pstmt.execute();
391
    ResultSet rs = pstmt.getResultSet();
392
    boolean hasRows = rs.next();
393
    while (hasRows) {
394
      aclObjects.addElement(rs.getString(1));
395
      hasRows = rs.next();
396
    }
397
    
398
    pstmt.close();
399
    
400
    return aclObjects;
401
  }
402

    
403
  /* Delete from db all permission for resources related to @aclid if any.*/
404
  private void deletePermissionsForRelatedResources(String aclid) 
405
          throws SQLException 
406
  {
407
    // delete all acl records for resources related to @aclid if any
408
    Statement stmt = conn.createStatement();
409
    stmt.execute("DELETE FROM xml_access WHERE accessfileid = '" + aclid + "'");
410
    stmt.close();
411
  }
412

    
413
  /* Insert into db calculated permission for the list of principals */
414
  private void insertPermissions(String docid, String permType ) 
415
          throws SQLException 
416
  {
417
    PreparedStatement pstmt;
418
 
419
    try {
420
      pstmt = conn.prepareStatement(
421
              "INSERT INTO xml_access " + 
422
              "(docid, principal_name, permission, perm_type, perm_order," +
423
              "begin_time,end_time,ticket_count, accessfileid) VALUES " +
424
              "(?,?,?,?,?,to_date(?,'mm/dd/yy'),to_date(?,'mm/dd/yy'),?,?)");
425
      // Bind the values to the query
426
      pstmt.setString(1, docid);
427
      pstmt.setInt(3, permission);
428
      pstmt.setString(4, permType);
429
      pstmt.setString(5, permOrder);
430
      pstmt.setString(6, beginTime);
431
      pstmt.setString(7, endTime);
432
      pstmt.setString(9, aclid);
433
      if ( ticketCount > 0 ) {
434
        pstmt.setString(8, "" + ticketCount);
435
      } else {
436
        pstmt.setString(8, "");
437
      }
438

    
439
      String prName;
440
      for ( int j = 0; j < principal.size(); j++ ) {
441
        prName = (String)principal.elementAt(j);
442
        pstmt.setString(2, prName);
443
        pstmt.execute();
444
      /*    
445
        // check if there are conflict with permission's order
446
        String permOrderOpos = permOrder;
447
        int perm = getPermissions(permission, prName, docid, permOrder);
448
        if (  perm != 0 ) {
449
          if ( permOrder.equals("allowFirst") ) {
450
            permOrderOpos = "denyFirst";
451
          } else if ( permOrder.equals("denyFirst") ) {
452
            permOrderOpos = "allowFirst";
453
          }
454
          throw new SQLException("Permission(s) " + txtValue(perm) + 
455
                    " for \"" + prName + "\" on document #" + docid +
456
                    " has/have been used with \"" + permOrderOpos + "\"");
457
        }
458
      */
459
      }
460
      pstmt.close();
461

    
462
    } catch (SQLException e) {
463
      throw new 
464
      SQLException("AccessControlList.insertPermissions(): " + e.getMessage());
465
    }
466
  }
467

    
468
  /* Get permissions with permission order different than @permOrder. */
469
  private int getPermissions(int permission, String principal,
470
                             String docid, String permOrder)
471
          throws SQLException 
472
  {
473
    PreparedStatement pstmt;
474
    pstmt = conn.prepareStatement(
475
            "SELECT permission FROM xml_access " +
476
            "WHERE docid = ? " +
477
            "AND principal_name = ? " +
478
            "AND perm_order NOT = ?");
479
    pstmt.setString(1, docid);
480
    pstmt.setString(2, principal);
481
    pstmt.setString(3, permOrder);
482
    pstmt.execute();
483
    ResultSet rs = pstmt.getResultSet();
484
    boolean hasRow = rs.next();
485
    int perm = 0;
486
    while ( hasRow ) {
487
      perm = rs.getInt(1);
488
      perm = permission & perm;
489
      if ( perm != 0 ) {
490
        pstmt.close();
491
        return perm;
492
      }
493
      hasRow = rs.next();
494
    }
495
    pstmt.close();
496
    return 0;
497
  }
498

    
499
  /* Get the int value of READ, WRITE or ALL. */
500
  private int intValue ( String permission )
501
  {
502
    if ( permission.equals("READ") ) {
503
      return READ;
504
    } else if ( permission.equals("WRITE") ) {
505
      return WRITE;
506
    } else if ( permission.equals("ALL") ) {
507
      return ALL;
508
    }
509
    
510
    return -1;
511
  }
512

    
513
  /* Get the text value of READ, WRITE or ALL. */
514
  private String txtValue ( int permission )
515
  {
516
    StringBuffer txtPerm = new StringBuffer("\"");
517
    if ( (permission & READ) != 0 ) {
518
      txtPerm.append("read");
519
    } 
520
    if ( (permission & WRITE) != 0 ) {
521
      if ( txtPerm.length() > 0 ) txtPerm.append(",");
522
      txtPerm.append("write");
523
    }
524
    if ( (permission & ALL) != 0 ) {
525
      if ( txtPerm.length() > 0 ) txtPerm.append(",");
526
      txtPerm.append("all");
527
    }
528

    
529
    return txtPerm.append("\"").toString();
530
  }
531

    
532
  /**
533
    * Check from db connection if at least one of the list of @principals
534
    * has @permission on @docid.
535
    * @param permission permission type to check for
536
    * @param principals list of names of principals to check for @permission
537
    * @param docid document identifier to check on
538
    */
539
  public boolean hasPermission(String permission, String user,
540
                               String[] groups, String docid )
541
                 throws SQLException
542
  {
543
    // b' of the command line invocation
544
    if ( (user == null) && (groups == null || groups.length == 0) ) {
545
      return true;
546
    }
547
    
548
    // Check for @permission on @docid for @user and/or @groups
549
    boolean hasPermission = hasPermission(permission,user,docid);
550
    int i = 0;
551
    if ( groups != null ) {
552
      while ( !hasPermission && i<groups.length ) {
553
        hasPermission = hasPermission(permission,groups[i++],docid);
554
      }
555
    }
556
    // Check for @permission for "public" user
557
    if ( !hasPermission ) {
558
      hasPermission = hasPermission(permission,"public",docid);
559
    }
560
    
561
    return hasPermission;
562
  }
563

    
564
  /**
565
    * Check from db connection if @principal has @permission on @docid.
566
    * @param permission permission type to check for
567
    * @param principal name of principal to check for @permission
568
    * @param docid document identifier to check on
569
    */
570
  private boolean hasPermission(String permission,
571
                                String principal, String docid)
572
                 throws SQLException
573
  {
574
    PreparedStatement pstmt;
575
    // check public access to @docid from xml_documents table
576
    if ( permission.equals("READ") ) {
577
      try {
578
        pstmt = conn.prepareStatement(
579
                "SELECT 'x' FROM xml_documents " +
580
                "WHERE docid = ? AND public_access = 1");
581
        // Bind the values to the query
582
        pstmt.setString(1, docid);
583

    
584
        pstmt.execute();
585
        ResultSet rs = pstmt.getResultSet();
586
        boolean hasRow = rs.next();
587
        pstmt.close();
588
        if (hasRow) {
589
          return true;
590
        }
591
//System.out.println("Passed the check for public access");      
592

    
593
      } catch (SQLException e) {
594
        throw new 
595
        SQLException("AccessControlList.hasPermission(). " +
596
                     "Error checking public access for document #"+docid+
597
                     ". " + e.getMessage());
598
      }
599
    }
600
    
601
    // since owner of resource has all permission on it,
602
    // check if @principal is owner of @docid in xml_documents table
603
    if ( principal != null ) {
604
      try {
605
        pstmt = conn.prepareStatement(
606
                "SELECT 'x' FROM xml_documents " +
607
                "WHERE docid = ? AND user_owner = ?");
608
        // Bind the values to the query
609
        pstmt.setString(1, docid);
610
        pstmt.setString(2, principal);
611

    
612
        pstmt.execute();
613
        ResultSet rs = pstmt.getResultSet();
614
        boolean hasRow = rs.next();
615
        pstmt.close();
616
        if (hasRow) {
617
          return true;
618
        }
619
//System.out.println("Passed the check for ownership");      
620
     
621
      } catch (SQLException e) {
622
        throw new 
623
        SQLException("AccessControlList.hasPermission(). " +
624
                     "Error checking ownership for " + principal +
625
                     " on document #" + docid + ". " + e.getMessage());
626
      }
627

    
628
      // check @principal's @permission on @docid from xml_access table
629
      int accessValue = 0;
630
      int ticketCount = 0;
631
      String permOrder = "";
632
      try {
633
        pstmt = conn.prepareStatement(
634
                "SELECT permission, perm_order, ticket_count " +
635
                "FROM xml_access " +
636
                "WHERE docid = ? " + 
637
                "AND principal_name = ? " +
638
                "AND perm_type = ? " +
639
                "AND " + sysdate + 
640
                " BETWEEN " + isnull + "(begin_time," + sysdate + ") " +
641
                     "AND " + isnull + "(end_time," + sysdate + ")");
642
        // check if it is "deny" with "allowFirst" first
643
        // Bind the values to the query
644
        pstmt.setString(1, docid);
645
        pstmt.setString(2, principal);
646
        pstmt.setString(3, "deny");
647

    
648
        pstmt.execute();
649
        ResultSet rs = pstmt.getResultSet();
650
        boolean hasRows = rs.next();
651
        while ( hasRows ) {
652
          accessValue = rs.getInt(1);
653
          permOrder = rs.getString(2);
654
          ticketCount = rs.getInt(3);
655
          if ( ( accessValue & intValue(permission) ) == intValue(permission) &&
656
               ( permOrder.equals("allowFirst") ) &&
657
               ( rs.wasNull() || ticketCount > 0 ) ) {
658
            if ( !rs.wasNull() && ticketCount > 0 ) {
659
              decreaseNumberOfAccess(accessValue,principal,docid,"deny","allowFirst");
660
            }
661
            pstmt.close();
662
            return false;
663
          }
664
          hasRows = rs.next();
665
        }
666
//System.out.println("Passed the check for \"deny\" access with \"allowFirst\"");      
667

    
668
        // it is not denied then check if it is "allow"
669
        // Bind the values to the query
670
        pstmt.setString(1, docid);
671
        pstmt.setString(2, principal);
672
        pstmt.setString(3, "allow");
673

    
674
        pstmt.execute();
675
        rs = pstmt.getResultSet();
676
        hasRows = rs.next();
677
        while ( hasRows ) {
678
          accessValue = rs.getInt(1);
679
          permOrder = rs.getString(2);
680
          ticketCount = rs.getInt(3);
681
          if ( ( accessValue & intValue(permission) )==intValue(permission) &&
682
               ( rs.wasNull() || ticketCount > 0 ) ) {
683
            if ( !rs.wasNull() && ticketCount > 0 ) {
684
              decreaseNumberOfAccess(accessValue,principal,docid,"allow",permOrder);
685
            }
686
            pstmt.close();
687
            return true;
688
          }
689
          hasRows = rs.next();
690
        }
691
//System.out.println("Passed the check for \"allow\" access");      
692

    
693
        // it is not allowed then check if it is "deny" with "denyFirst"
694
        // Bind the values to the query
695
        pstmt.setString(1, docid);
696
        pstmt.setString(2, principal);
697
        pstmt.setString(3, "deny");
698

    
699
        pstmt.execute();
700
        rs = pstmt.getResultSet();
701
        hasRows = rs.next();
702
        while ( hasRows ) {
703
          accessValue = rs.getInt(1);
704
          permOrder = rs.getString(2);
705
          ticketCount = rs.getInt(3);
706
          if ( ( accessValue & intValue(permission) ) == intValue(permission) &&
707
               ( permOrder.equals("denyFirst") ) &&
708
               ( rs.wasNull() || ticketCount > 0 ) ) {
709
            if ( !rs.wasNull() && ticketCount > 0 ) {
710
              decreaseNumberOfAccess(accessValue,principal,docid,"deny","denyFirst");
711
            }
712
            pstmt.close();
713
            return false;
714
          }
715
          hasRows = rs.next();
716
        }
717
//System.out.println("Passed the check for \"deny\" access wirh \"denyFirst\"");      
718
      
719
        pstmt.close();
720
        return false;
721
  
722
      } catch (SQLException e) {
723
        throw new 
724
        SQLException("AccessControlList.hasPermission(). " +
725
                     "Error checking " + permission + " permission for " +
726
                     principal + " on document #" + docid + ". " +
727
                     e.getMessage());
728
      }
729
    }
730
    
731
    return false;
732
  }
733

    
734
  /* Decrease the number of access to @docid for @principal in db. */
735
  private void decreaseNumberOfAccess(int permission, String principal,
736
                                      String docid, String permType, 
737
                                      String permOrder) 
738
               throws SQLException
739
  {
740
    PreparedStatement pstmt;
741
    pstmt = conn.prepareStatement(
742
            "UPDATE xml_access SET ticket_count = ticket_count - 1 " +
743
            "WHERE docid = ? " +
744
            "AND principal_name = ? " +
745
            "AND permission = ? " +
746
            "AND perm_type = ? " +
747
            "AND perm_order = ? " +
748
            "AND " + sysdate + 
749
            " BETWEEN " + isnull + "(begin_time," + sysdate + ") " +
750
                 "AND " + isnull + "(end_time," + sysdate + ")");
751
    // Bind the values to the query
752
    pstmt.setString(1, docid);
753
    pstmt.setString(2, principal);
754
    pstmt.setInt(3, permission);
755
    pstmt.setString(4, permType);
756
    pstmt.setString(5, permOrder);
757

    
758
    pstmt.execute();
759
    pstmt.close();
760
  }
761
 
762
 
763
  /**
764
    * Get Access Control List information for document from db connetion.
765
    * User or Group should have permissions for reading
766
    * access control information for a document specified by @docid.
767
    * @param docid document identifier which acl info to get
768
    * @param user name of user connected to Metacat system
769
    * @param groups names of user's groups to which user belongs
770
    */
771
  public String getACL(String docid, String user, String[] groups) 
772
          throws SQLException 
773
  {
774
    StringBuffer output = new StringBuffer();
775
    StringBuffer outTemp = new StringBuffer();
776
    MetaCatUtil util = new MetaCatUtil();
777
    String accDoctype = util.getOption("accessdoctype");
778
    String server = util.getOption("server");
779
    String docurl = "metacat://" + server + "/?docid=" + docid;
780
    String systemID;
781
    boolean isOwned = false;
782
    boolean hasPermission = false;
783
    String publicAcc;
784
    
785
    String acfid = "";
786
    String acfid_prev = "";
787
    String principal;
788
    Vector principalArr = new Vector();
789
    int permission;
790
    int perm_prev = -1;
791
    String permType;
792
    String permOrder = "";
793
    String permOrder_prev = "";
794
    String beginTime = "";
795
    String begin_prev = "";
796
    String endTime = "";
797
    String end_prev = "";
798
    int ticketCount = -1;
799
    int ticket_prev = -1;
800
    
801
    try {
802
      
803
      isOwned = isOwned(docid, user);
804
      systemID = getSystemID(accDoctype);
805
      publicAcc = getPublicAccess(docid);
806
        
807
      output.append("<?xml version=\"1.0\"?>\n");
808
      output.append("<!DOCTYPE acl PUBLIC \"" + accDoctype + "\" \"" +
809
                    systemID + "\">\n");
810
      output.append("<acl authSystem=\"\">\n");
811

    
812
      PreparedStatement pstmt;
813
      pstmt = conn.prepareStatement(
814
              "SELECT distinct accessfileid, principal_name, permission, " +
815
              "perm_type, perm_order, to_char(begin_time,'mm/dd/yyyy'), " +
816
              "to_char(end_time,'mm/dd/yyyy'), ticket_count " +
817
              "FROM xml_access WHERE docid = ? " +
818
              "ORDER BY accessfileid, perm_order, perm_type, permission");
819
      // Bind the values to the query
820
      pstmt.setString(1, docid);
821
      pstmt.execute();
822
      ResultSet rs = pstmt.getResultSet();
823
      boolean hasRows = rs.next();
824
      while (hasRows) {
825

    
826
        acfid = rs.getString(1);
827
        principal = rs.getString(2);
828
        permission = rs.getInt(3);
829
        permType = rs.getString(4);
830
        permOrder = rs.getString(5);
831
        beginTime = rs.getString(6);
832
        endTime = rs.getString(7);
833
        ticketCount = rs.getInt(8);
834

    
835
        // if @docid is not owned by @user, only ACL info from that
836
        // access files to which @user/@groups has "read" permission
837
        // is extracted
838
        if ( !isOwned ) {
839
          if ( !acfid.equals(acfid_prev) ) {
840
            acfid_prev = acfid;
841
            hasPermission = this.hasPermission("READ",user,groups,acfid);
842
          }
843
          if ( !hasPermission ) {
844
            rs.next();
845
            continue;
846
          }
847
        }
848
        
849
        // open <resource> tag
850
        if ( !permOrder.equals(permOrder_prev) ) {
851
          // close </resource> tag if any was opened 
852
          output.append(outTemp.toString());
853
          outTemp = new StringBuffer();
854
          if ( !permOrder_prev.equals("") ) {
855
            output.append("  </resource>\n");
856
          }
857
          output.append("  <resource order=\"" + permOrder + "\" public=\"" +
858
                        publicAcc + "\">\n");
859
          output.append("    <resourceIdentifier>" + docurl + 
860
                        "</resourceIdentifier>\n");
861
          permOrder_prev = permOrder;
862
        }
863
        
864
        // close </allow> or </deny> tag then open new one
865
        if ( permission != perm_prev ||
866
             (endTime == null) && (end_prev != null) ||
867
             (beginTime == null) && (begin_prev != null) ||
868
             endTime != null && !endTime.equals(end_prev)  ||
869
             beginTime != null && !beginTime.equals(begin_prev) ||
870
             ticketCount != ticket_prev )  {
871
          output.append(outTemp.toString());
872
          outTemp = new StringBuffer();
873
          principalArr.removeAllElements();
874
          output.append("    <" + permType + ">\n");
875
        }
876
        
877
        // put all principals here for the same 
878
        // permission, duration and ticket_count
879
        if ( !principalArr.contains(principal) ) {
880
          principalArr.addElement(principal);
881
          output.append("      <principal>" + principal + "</principal>\n");
882
        } 
883
        
884
        // prepare <permission> tags, <duration> and <ticketCount>
885
        // if any to put within <allow> (<deny>) by next cicle
886
        if ( permission != perm_prev || 
887
             (endTime == null) && (end_prev != null) ||
888
             (beginTime == null) && (begin_prev != null) ||
889
             endTime != null && !endTime.equals(end_prev)  ||
890
             beginTime != null && !beginTime.equals(begin_prev) ||
891
             ticketCount != ticket_prev )  {
892
          if ( (permission & READ) != 0 ) {
893
            outTemp.append("      <permission>read</permission>\n");
894
          }
895
          if ( (permission & WRITE) != 0 ) {
896
            outTemp.append("      <permission>write</permission>\n");
897
          }
898
          if ( (permission & ALL) != 0 ) {
899
            outTemp.append("      <permission>all</permission>\n");
900
          }
901
          if ( (beginTime != null) || (endTime != null) ) {
902
            outTemp.append("      <duration>" + beginTime + " " + endTime +
903
                          "</duration>\n");
904
          }
905
          if ( ticketCount > 0 ) {
906
            outTemp.append("      <ticketCount>" + ticketCount + 
907
                          "</ticketCount>\n");
908
          }
909
          outTemp.append("    </" + permType + ">\n");
910
          perm_prev = permission;
911
          ticket_prev = ticketCount;
912
          begin_prev = beginTime;
913
          end_prev = endTime;
914
        }
915
        
916
        hasRows = rs.next();
917
      }
918

    
919
      // close <allow> or <deny> if anything left in outTemp var
920
      output.append(outTemp.toString());
921

    
922
      // If there are no any acl info for @docid accessible by @user/@group,
923
      // extract only the following information
924
      if ( permOrder.equals("") ) {
925
        output.append("  <resource public=\"" + publicAcc + "\">\n");
926
        output.append("    <resourceIdentifier>" + docurl + 
927
                      "</resourceIdentifier>\n");
928
      }
929
      
930
      // always close them
931
      output.append("  </resource>\n");
932
      output.append("</acl>\n");
933
      
934
      pstmt.close();
935

    
936
      return output.toString();
937

    
938
    } catch (SQLException e) {
939
      throw new 
940
      SQLException("AccessControlList.getACL(). " + e.getMessage());
941
    }
942
  }
943
  
944
  /* Check if @user is owner of @docid from db conn. */
945
  private boolean isOwned(String docid, String user) throws SQLException {
946
    
947
    PreparedStatement pstmt;
948
    pstmt = conn.prepareStatement("SELECT 'x' FROM xml_documents " +
949
                                  "WHERE docid = ? " + 
950
                                  "AND user_owner = ?");
951
    pstmt.setString(1, docid);
952
    pstmt.setString(2, user);
953
    pstmt.execute();
954
    ResultSet rs = pstmt.getResultSet();
955
    boolean hasRow = rs.next();
956
    
957
    return hasRow;
958
  }
959

    
960
  /* Get the flag for public "read" access for @docid from db conn. */
961
  private String getPublicAccess(String docid) throws SQLException {
962
    
963
    int publicAcc = 0;
964
    PreparedStatement pstmt;
965
    pstmt = conn.prepareStatement("SELECT public_access FROM xml_documents " +
966
                                  "WHERE docid = ?");
967
    pstmt.setString(1, docid);
968
    pstmt.execute();
969
    ResultSet rs = pstmt.getResultSet();
970
    boolean hasRow = rs.next();
971
    if ( hasRow ) {
972
      publicAcc = rs.getInt(1);
973
    }
974
    
975
    return (publicAcc == 1) ? "yes" : "no";
976
  }
977

    
978
  /* Get SystemID for @publicID from Metacat DB Catalog. */
979
  private String getSystemID(String publicID) throws SQLException {
980
    
981
    String systemID = "";
982
    PreparedStatement pstmt;
983
    pstmt = conn.prepareStatement("SELECT system_id FROM xml_catalog " +
984
                                  "WHERE entry_type = 'DTD' " + 
985
                                  "AND public_id = ?");
986
    pstmt.setString(1, publicID);
987
    pstmt.execute();
988
    ResultSet rs = pstmt.getResultSet();
989
    boolean hasRow = rs.next();
990
    if ( hasRow ) {
991
      systemID = rs.getString(1);
992
    }
993
    
994
    return systemID;
995
  }
996

    
997
}
(1-1/40)