Project

General

Profile

1 555 bojilova
/**
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$'
11
 *     '$Date$'
12
 * '$Revision$'
13 669 jones
 *
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 555 bojilova
 */
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 652 bojilova
import java.util.Hashtable;
36
import java.net.URL;
37
import java.net.MalformedURLException;
38 555 bojilova
39
import org.xml.sax.Attributes;
40
import org.xml.sax.InputSource;
41 598 bojilova
import org.xml.sax.ContentHandler;
42
import org.xml.sax.EntityResolver;
43
import org.xml.sax.ErrorHandler;
44 555 bojilova
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 774 bojilova
  private static final int ALL = 1;
58
  private static final int WRITE = 2;
59
  private static final int READ = 4;
60
  private static String sysdate = MetaCatUtil.dbAdapter.getDateTimeFunction();
61
  private static String isnull = MetaCatUtil.dbAdapter.getIsNULLFunction();
62 555 bojilova
63 570 bojilova
  private Connection conn;
64 555 bojilova
  private String parserName;
65
  private Stack elementStack;
66 645 bojilova
  private String server;
67 802 bojilova
  private String sep;
68 555 bojilova
69 598 bojilova
  private boolean	processingDTD;
70 645 bojilova
  private String  user;
71 802 bojilova
  private String[] groups;
72 638 bojilova
  private String  aclid;
73 802 bojilova
  private int     rev;
74 598 bojilova
  private String 	docname;
75
  private String 	doctype;
76
  private String 	systemid;
77
78 665 bojilova
  private String docurl;
79
  private Vector resourceURL;
80
  private Vector resourceID;
81 660 bojilova
  private Vector principal;
82 570 bojilova
  private int    permission;
83
  private String permType;
84
  private String permOrder;
85 802 bojilova
//  private String publicAcc;
86 570 bojilova
  private String beginTime;
87
  private String endTime;
88
  private int    ticketCount;
89 684 bojilova
  private int    serverCode = 1;
90 802 bojilova
91
  private Vector aclObjects = new Vector();
92 859 berkley
  private boolean instarttag = true;
93
  private String tagName = "";
94 555 bojilova
  /**
95
   * Construct an instance of the AccessControlList class.
96 688 bojilova
   * It is used by the permission check up from DBQuery or DocumentImpl
97
   * and from "getaccesscontrol" action
98 570 bojilova
   *
99 684 bojilova
   * @param conn the JDBC connection where acl info is get
100 570 bojilova
   */
101 684 bojilova
  public AccessControlList(Connection conn) throws SQLException
102 570 bojilova
  {
103 613 bojilova
    this.conn = conn;
104 570 bojilova
  }
105
106
  /**
107
   * Construct an instance of the AccessControlList class.
108 555 bojilova
   * It parse acl file and loads acl data into db connection.
109
   *
110
   * @param conn the JDBC connection where acl data are loaded
111 684 bojilova
   * @param aclid the Accession# of the document with the acl data
112 570 bojilova
   * @param acl the acl file containing acl data
113 684 bojilova
   * @param user the user connected to MetaCat servlet and owns the document
114 802 bojilova
   * @param groups the groups to which user belongs
115 684 bojilova
   * @param serverCode the serverid from xml_replication on which this document
116
   *        resides.
117 555 bojilova
   */
118 819 bojilova
  public AccessControlList(Connection conn, String aclid, //Reader acl,
119 802 bojilova
                           String user, String[] groups, int serverCode)
120 819 bojilova
                  throws SAXException, IOException, McdbException
121 555 bojilova
  {
122 802 bojilova
    String parserName = MetaCatUtil.getOption("saxparser");
123
    this.server = MetaCatUtil.getOption("server");
124
    this.sep = MetaCatUtil.getOption("accNumSeparator");
125 555 bojilova
126
    this.conn = conn;
127
    this.parserName = parserName;
128 598 bojilova
    this.processingDTD = false;
129 570 bojilova
    this.elementStack = new Stack();
130
131 645 bojilova
    this.user = user;
132 802 bojilova
    this.groups = groups;
133 638 bojilova
    this.aclid = aclid;
134 665 bojilova
    this.resourceURL = new Vector();
135
    this.resourceID = new Vector();
136 660 bojilova
    this.principal = new Vector();
137 570 bojilova
    this.permission = 0;
138
    this.ticketCount = 0;
139 802 bojilova
  //  this.publicAcc = null;
140 684 bojilova
    this.serverCode = serverCode;
141 638 bojilova
142 819 bojilova
    // read the access file from db connection
143
    DocumentImpl acldoc = new DocumentImpl(conn, aclid);
144
    String acl = acldoc.toString();
145
    this.rev = acldoc.getRev();
146
147
    // Initialize the parse
148 555 bojilova
    XMLReader parser = initializeParser();
149 819 bojilova
    // parse the access file and write the info to xml_access
150
    parser.parse(new InputSource(new StringReader(acl)));
151
152 555 bojilova
  }
153
154 819 bojilova
// NOT USED
155
//  /**
156
//   * Construct an instance of the AccessControlList class.
157
//   * It parses eml-access file and loads acl data into db connection.
158
//   * It is used from command line execution.
159
//   *
160
//   * @param conn the JDBC connection where acl data are loaded
161
//   * @param docid the Accession# of the document with the acl data
162
//   * @param aclfilename the name of acl file containing acl data
163
//   * @param user the user connected to MetaCat servlet and owns the document
164
//   * @param groups the groups to which user belongs
165
//   */
166
//  public AccessControlList( Connection conn, String aclid, String aclfilename,
167
//                           String user, String[] groups )
168
//                  throws SAXException, IOException, McdbException
169
//  {
170
//    this(conn, aclid, new FileReader(new File(aclfilename).toString()),
171
//         user, groups, 1);
172
//  }
173 638 bojilova
174 660 bojilova
  /* Set up the SAX parser for reading the XML serialized ACL */
175 555 bojilova
  private XMLReader initializeParser() throws SAXException
176
  {
177
    XMLReader parser = null;
178
179
    // Get an instance of the parser
180
    parser = XMLReaderFactory.createXMLReader(parserName);
181
182 598 bojilova
    // Turn off validation
183 638 bojilova
    parser.setFeature("http://xml.org/sax/features/validation", true);
184 598 bojilova
185
    // Set Handlers in the parser
186 555 bojilova
    // Set the ContentHandler to this instance
187 598 bojilova
    parser.setContentHandler((ContentHandler)this);
188 555 bojilova
189 598 bojilova
    // make a DBEntityResolver instance
190
    // Set the EntityReslover to DBEntityResolver instance
191
    EntityResolver eresolver = new DBEntityResolver(conn,this,null);
192
    parser.setEntityResolver((EntityResolver)eresolver);
193
194 555 bojilova
    // Set the ErrorHandler to this instance
195 598 bojilova
    parser.setErrorHandler((ErrorHandler)this);
196 555 bojilova
197
    return parser;
198
  }
199
200
  /**
201 688 bojilova
   * Callback method used by the SAX Parser when beginning of the document
202 645 bojilova
   */
203
  public void startDocument() throws SAXException
204
  {
205
    //delete all previously submitted permissions @ relations
206
    //this happens only on UPDATE of the access file
207
    try {
208 819 bojilova
      this.aclObjects = getACLObjects(aclid);
209 802 bojilova
210 819 bojilova
      //delete all permissions for resources related to @aclid if any
211 645 bojilova
      if ( aclid != null ) {
212
        deletePermissionsForRelatedResources(aclid);
213
      }
214
    } catch (SQLException sqle) {
215
      throw new SAXException(sqle);
216
    }
217
  }
218
219
  /**
220 688 bojilova
   * Callback method used by the SAX Parser when the start tag of an
221 555 bojilova
   * 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 859 berkley
    instarttag = true;
229
    if(localName.equals("allow"))
230
    {
231
      tagName = "allow";
232
    }
233
    else if(localName.equals("deny"))
234
    {
235
      tagName = "deny";
236
    }
237 555 bojilova
    BasicNode currentNode = new BasicNode(localName);
238
    if (atts != null) {
239
      int len = atts.getLength();
240
      for (int i = 0; i < len; i++) {
241
        currentNode.setAttribute(atts.getLocalName(i), atts.getValue(i));
242
      }
243
    }
244 802 bojilova
    if ( currentNode.getTagName().equals("acl") ) {
245 570 bojilova
      permOrder = currentNode.getAttribute("order");
246 802 bojilova
    //  publicAcc = currentNode.getAttribute("public");
247 570 bojilova
    }
248 555 bojilova
    elementStack.push(currentNode);
249
  }
250
251
  /**
252 688 bojilova
   * Callback method used by the SAX Parser when the text sequences of an
253 555 bojilova
   * xml stream are detected. Used in this context to parse and store
254
   * the acl information in class variables.
255 859 berkley
   */
256 555 bojilova
  public void characters(char ch[], int start, int length)
257
         throws SAXException
258
  {
259 859 berkley
    if(!instarttag)
260
    {
261
      return;
262
    }
263 555 bojilova
    String inputString = new String(ch, start, length);
264 859 berkley
    inputString = inputString.trim();
265 555 bojilova
    BasicNode currentNode = (BasicNode)elementStack.peek();
266
    String currentTag = currentNode.getTagName();
267
268 802 bojilova
      if (currentTag.equals("principal")) {
269 645 bojilova
270 802 bojilova
        principal.addElement(inputString);
271 645 bojilova
272 802 bojilova
      } else if (currentTag.equals("permission")) {
273 645 bojilova
274 802 bojilova
        if ( inputString.trim().toUpperCase().equals("READ") ) {
275
          permission = permission | READ;
276
        } else if ( inputString.trim().toUpperCase().equals("WRITE") ) {
277
          permission = permission | WRITE;
278
        } else if ( inputString.trim().toUpperCase().equals("ALL") ) {
279
          permission = permission | ALL;
280 859 berkley
        } /*else{
281 802 bojilova
          throw new SAXException("Unknown permission type: " + inputString);
282 859 berkley
        }*/
283 645 bojilova
284 802 bojilova
      } else if ( currentTag.equals("startDate") && beginTime == null ) {
285
        beginTime = inputString.trim();
286 645 bojilova
287 802 bojilova
      } else if ( currentTag.equals("stopDate") && endTime == null) {
288
        endTime = inputString.trim();
289 645 bojilova
290 802 bojilova
      } else if (currentTag.equals("ticketCount") && ticketCount == 0 ) {
291
        try {
292
          ticketCount = (new Integer(inputString.trim())).intValue();
293
        } catch (NumberFormatException nfe) {
294
          throw new SAXException("Wrong integer format for:" + inputString);
295
        }
296 555 bojilova
      }
297
  }
298
299
  /**
300 688 bojilova
   * Callback method used by the SAX Parser when the end tag of an
301 555 bojilova
   * element is detected. Used in this context to parse and store
302
   * the acl information in class variables.
303
   */
304
  public void endElement (String uri, String localName, String qName)
305
         throws SAXException
306
  {
307 859 berkley
    instarttag = false;
308 688 bojilova
    BasicNode leaving = (BasicNode)elementStack.pop();
309
    String leavingTagName = leaving.getTagName();
310
311 802 bojilova
    if ( leavingTagName.equals("allow") ||
312
         leavingTagName.equals("deny")    ) {
313 570 bojilova
314
      if ( permission > 0 ) {
315 555 bojilova
316 570 bojilova
        // insert into db calculated permission for the list of principals
317
        try {
318 802 bojilova
// System.out.println("before insertPermission " +leavingTagName);
319
          // go through the objects in xml_relation about this acl doc
320
          for (int i=0; i < aclObjects.size(); i++) {
321
            // docid of the current object
322
            String docid = (String)aclObjects.elementAt(i);
323
            DocumentIdentifier docID = new DocumentIdentifier(docid);
324
            docid = docID.getIdentifier();
325
// System.out.println(docid);
326
            insertPermissions(docid,leavingTagName);
327
          }
328
329 570 bojilova
        } catch (SQLException sqle) {
330
          throw new SAXException(sqle);
331 802 bojilova
        } catch (Exception e) {
332
          throw new SAXException(e);
333 570 bojilova
        }
334
      }
335
336 688 bojilova
      // reset the allow/deny permission
337 660 bojilova
      principal = new Vector();
338 555 bojilova
      permission = 0;
339 570 bojilova
      beginTime = null;
340
      endTime = null;
341
      ticketCount = 0;
342 555 bojilova
343 570 bojilova
    }
344 555 bojilova
345
  }
346 598 bojilova
347 688 bojilova
  /**
348
    * SAX Handler that receives notification of DOCTYPE. Sets the DTD.
349
    * @param name name of the DTD
350
    * @param publicId Public Identifier of the DTD
351
    * @param systemId System Identifier of the DTD
352
    */
353 598 bojilova
  public void startDTD(String name, String publicId, String systemId)
354
              throws SAXException {
355
    docname = name;
356
    doctype = publicId;
357
    systemid = systemId;
358
  }
359
360
  /**
361 688 bojilova
   * SAX Handler that receives notification of the start of entities.
362
   * @param name name of the entity
363 598 bojilova
   */
364
  public void startEntity(String name) throws SAXException {
365
    if (name.equals("[dtd]")) {
366
      processingDTD = true;
367
    }
368
  }
369
370
  /**
371 688 bojilova
   * SAX Handler that receives notification of the end of entities.
372
   * @param name name of the entity
373 598 bojilova
   */
374
  public void endEntity(String name) throws SAXException {
375
    if (name.equals("[dtd]")) {
376
      processingDTD = false;
377
    }
378
  }
379
380
  /**
381 688 bojilova
   * Get the document name.
382 598 bojilova
   */
383
  public String getDocname() {
384
    return docname;
385
  }
386
387
  /**
388 688 bojilova
   * Get the document processing state.
389 598 bojilova
   */
390
  public boolean processingDTD() {
391
    return processingDTD;
392
  }
393
394 802 bojilova
  /* Get all objects associated with @aclid from db.*/
395
  private Vector getACLObjects(String aclid)
396 638 bojilova
          throws SQLException
397
  {
398 802 bojilova
    Vector aclObjects = new Vector();
399 645 bojilova
    // delete all acl records for resources related to @aclid if any
400 802 bojilova
    PreparedStatement pstmt = conn.prepareStatement(
401
                             "SELECT object FROM xml_relation " +
402 830 jones
                             "WHERE subject = ? ");
403 802 bojilova
    pstmt.setString(1,aclid);
404
    pstmt.execute();
405
    ResultSet rs = pstmt.getResultSet();
406
    boolean hasRows = rs.next();
407
    while (hasRows) {
408
      aclObjects.addElement(rs.getString(1));
409
      hasRows = rs.next();
410
    }
411
412
    pstmt.close();
413
414
    return aclObjects;
415 638 bojilova
  }
416
417 802 bojilova
  /* Delete from db all permission for resources related to @aclid if any.*/
418
  private void deletePermissionsForRelatedResources(String aclid)
419 638 bojilova
          throws SQLException
420
  {
421 802 bojilova
    // delete all acl records for resources related to @aclid if any
422 645 bojilova
    Statement stmt = conn.createStatement();
423 802 bojilova
    stmt.execute("DELETE FROM xml_access WHERE accessfileid = '" + aclid + "'");
424 645 bojilova
    stmt.close();
425
  }
426
427 688 bojilova
  /* Insert into db calculated permission for the list of principals */
428 802 bojilova
  private void insertPermissions(String docid, String permType )
429 555 bojilova
          throws SQLException
430
  {
431 672 bojilova
    PreparedStatement pstmt;
432 638 bojilova
433 555 bojilova
    try {
434
      pstmt = conn.prepareStatement(
435
              "INSERT INTO xml_access " +
436 645 bojilova
              "(docid, principal_name, permission, perm_type, perm_order," +
437
              "begin_time,end_time,ticket_count, accessfileid) VALUES " +
438
              "(?,?,?,?,?,to_date(?,'mm/dd/yy'),to_date(?,'mm/dd/yy'),?,?)");
439 555 bojilova
      // Bind the values to the query
440 802 bojilova
      pstmt.setString(1, docid);
441 570 bojilova
      pstmt.setInt(3, permission);
442
      pstmt.setString(4, permType);
443
      pstmt.setString(5, permOrder);
444
      pstmt.setString(6, beginTime);
445
      pstmt.setString(7, endTime);
446 645 bojilova
      pstmt.setString(9, aclid);
447 570 bojilova
      if ( ticketCount > 0 ) {
448
        pstmt.setString(8, "" + ticketCount);
449
      } else {
450 555 bojilova
        pstmt.setString(8, "");
451 570 bojilova
      }
452 802 bojilova
453 688 bojilova
      String prName;
454 802 bojilova
      for ( int j = 0; j < principal.size(); j++ ) {
455
        prName = (String)principal.elementAt(j);
456
        pstmt.setString(2, prName);
457
        pstmt.execute();
458
      /*
459
        // check if there are conflict with permission's order
460
        String permOrderOpos = permOrder;
461
        int perm = getPermissions(permission, prName, docid, permOrder);
462
        if (  perm != 0 ) {
463
          if ( permOrder.equals("allowFirst") ) {
464
            permOrderOpos = "denyFirst";
465
          } else if ( permOrder.equals("denyFirst") ) {
466
            permOrderOpos = "allowFirst";
467 688 bojilova
          }
468 802 bojilova
          throw new SQLException("Permission(s) " + txtValue(perm) +
469
                    " for \"" + prName + "\" on document #" + docid +
470
                    " has/have been used with \"" + permOrderOpos + "\"");
471 665 bojilova
        }
472 802 bojilova
      */
473 570 bojilova
      }
474 672 bojilova
      pstmt.close();
475 555 bojilova
476 570 bojilova
    } catch (SQLException e) {
477
      throw new
478
      SQLException("AccessControlList.insertPermissions(): " + e.getMessage());
479 672 bojilova
    }
480
  }
481
482 688 bojilova
  /* Get permissions with permission order different than @permOrder. */
483
  private int getPermissions(int permission, String principal,
484
                             String docid, String permOrder)
485
          throws SQLException
486
  {
487
    PreparedStatement pstmt;
488
    pstmt = conn.prepareStatement(
489
            "SELECT permission FROM xml_access " +
490 765 bojilova
            "WHERE docid = ? " +
491
            "AND principal_name = ? " +
492
            "AND perm_order NOT = ?");
493 688 bojilova
    pstmt.setString(1, docid);
494
    pstmt.setString(2, principal);
495
    pstmt.setString(3, permOrder);
496
    pstmt.execute();
497
    ResultSet rs = pstmt.getResultSet();
498
    boolean hasRow = rs.next();
499
    int perm = 0;
500
    while ( hasRow ) {
501
      perm = rs.getInt(1);
502
      perm = permission & perm;
503
      if ( perm != 0 ) {
504
        pstmt.close();
505
        return perm;
506
      }
507
      hasRow = rs.next();
508
    }
509
    pstmt.close();
510
    return 0;
511
  }
512
513
  /* Get the int value of READ, WRITE or ALL. */
514
  private int intValue ( String permission )
515
  {
516
    if ( permission.equals("READ") ) {
517
      return READ;
518
    } else if ( permission.equals("WRITE") ) {
519
      return WRITE;
520
    } else if ( permission.equals("ALL") ) {
521
      return ALL;
522
    }
523
524
    return -1;
525
  }
526
527
  /* Get the text value of READ, WRITE or ALL. */
528
  private String txtValue ( int permission )
529
  {
530
    StringBuffer txtPerm = new StringBuffer("\"");
531
    if ( (permission & READ) != 0 ) {
532
      txtPerm.append("read");
533
    }
534
    if ( (permission & WRITE) != 0 ) {
535
      if ( txtPerm.length() > 0 ) txtPerm.append(",");
536
      txtPerm.append("write");
537
    }
538
    if ( (permission & ALL) != 0 ) {
539
      if ( txtPerm.length() > 0 ) txtPerm.append(",");
540
      txtPerm.append("all");
541
    }
542
543
    return txtPerm.append("\"").toString();
544
  }
545
546 802 bojilova
  /**
547
    * Check from db connection if at least one of the list of @principals
548
    * has @permission on @docid.
549
    * @param permission permission type to check for
550
    * @param principals list of names of principals to check for @permission
551
    * @param docid document identifier to check on
552
    */
553
  public boolean hasPermission(String permission, String user,
554
                               String[] groups, String docid )
555
                 throws SQLException
556 688 bojilova
  {
557 802 bojilova
    // b' of the command line invocation
558
    if ( (user == null) && (groups == null || groups.length == 0) ) {
559
      return true;
560
    }
561
562
    // Check for @permission on @docid for @user and/or @groups
563
    boolean hasPermission = hasPermission(permission,user,docid);
564
    int i = 0;
565
    if ( groups != null ) {
566
      while ( !hasPermission && i<groups.length ) {
567
        hasPermission = hasPermission(permission,groups[i++],docid);
568 688 bojilova
      }
569
    }
570 802 bojilova
    // Check for @permission for "public" user
571
    if ( !hasPermission ) {
572
      hasPermission = hasPermission(permission,"public",docid);
573
    }
574
575
    return hasPermission;
576
  }
577 688 bojilova
578
  /**
579 802 bojilova
    * Check from db connection if @principal has @permission on @docid.
580 688 bojilova
    * @param permission permission type to check for
581
    * @param principal name of principal to check for @permission
582 802 bojilova
    * @param docid document identifier to check on
583 688 bojilova
    */
584 802 bojilova
  private boolean hasPermission(String permission,
585
                                String principal, String docid)
586 570 bojilova
                 throws SQLException
587
  {
588
    PreparedStatement pstmt;
589 802 bojilova
    // check public access to @docid from xml_documents table
590 570 bojilova
    if ( permission.equals("READ") ) {
591
      try {
592 607 bojilova
        pstmt = conn.prepareStatement(
593 570 bojilova
                "SELECT 'x' FROM xml_documents " +
594 765 bojilova
                "WHERE docid = ? AND public_access = 1");
595 570 bojilova
        // Bind the values to the query
596 802 bojilova
        pstmt.setString(1, docid);
597 570 bojilova
598 555 bojilova
        pstmt.execute();
599 570 bojilova
        ResultSet rs = pstmt.getResultSet();
600
        boolean hasRow = rs.next();
601
        pstmt.close();
602
        if (hasRow) {
603
          return true;
604
        }
605
//System.out.println("Passed the check for public access");
606
607
      } catch (SQLException e) {
608
        throw new
609 688 bojilova
        SQLException("AccessControlList.hasPermission(). " +
610 802 bojilova
                     "Error checking public access for document #"+docid+
611 688 bojilova
                     ". " + e.getMessage());
612 555 bojilova
      }
613 570 bojilova
    }
614
615
    // since owner of resource has all permission on it,
616 802 bojilova
    // check if @principal is owner of @docid in xml_documents table
617 570 bojilova
    if ( principal != null ) {
618
      try {
619 607 bojilova
        pstmt = conn.prepareStatement(
620 570 bojilova
                "SELECT 'x' FROM xml_documents " +
621 765 bojilova
                "WHERE docid = ? AND user_owner = ?");
622 570 bojilova
        // Bind the values to the query
623 802 bojilova
        pstmt.setString(1, docid);
624 570 bojilova
        pstmt.setString(2, principal);
625 555 bojilova
626 570 bojilova
        pstmt.execute();
627
        ResultSet rs = pstmt.getResultSet();
628
        boolean hasRow = rs.next();
629
        pstmt.close();
630
        if (hasRow) {
631
          return true;
632 555 bojilova
        }
633 570 bojilova
//System.out.println("Passed the check for ownership");
634
635
      } catch (SQLException e) {
636
        throw new
637 688 bojilova
        SQLException("AccessControlList.hasPermission(). " +
638
                     "Error checking ownership for " + principal +
639 802 bojilova
                     " on document #" + docid + ". " + e.getMessage());
640 570 bojilova
      }
641
642 802 bojilova
      // check @principal's @permission on @docid from xml_access table
643 570 bojilova
      int accessValue = 0;
644
      int ticketCount = 0;
645
      String permOrder = "";
646
      try {
647 607 bojilova
        pstmt = conn.prepareStatement(
648 570 bojilova
                "SELECT permission, perm_order, ticket_count " +
649
                "FROM xml_access " +
650 765 bojilova
                "WHERE docid = ? " +
651
                "AND principal_name = ? " +
652
                "AND perm_type = ? " +
653 774 bojilova
                "AND " + sysdate +
654
                " BETWEEN " + isnull + "(begin_time," + sysdate + ") " +
655
                     "AND " + isnull + "(end_time," + sysdate + ")");
656 688 bojilova
        // check if it is "deny" with "allowFirst" first
657 570 bojilova
        // Bind the values to the query
658 802 bojilova
        pstmt.setString(1, docid);
659 570 bojilova
        pstmt.setString(2, principal);
660 688 bojilova
        pstmt.setString(3, "deny");
661 570 bojilova
662
        pstmt.execute();
663
        ResultSet rs = pstmt.getResultSet();
664
        boolean hasRows = rs.next();
665
        while ( hasRows ) {
666
          accessValue = rs.getInt(1);
667
          permOrder = rs.getString(2);
668
          ticketCount = rs.getInt(3);
669
          if ( ( accessValue & intValue(permission) ) == intValue(permission) &&
670 638 bojilova
               ( permOrder.equals("allowFirst") ) &&
671 570 bojilova
               ( rs.wasNull() || ticketCount > 0 ) ) {
672
            if ( !rs.wasNull() && ticketCount > 0 ) {
673 802 bojilova
              decreaseNumberOfAccess(accessValue,principal,docid,"deny","allowFirst");
674 570 bojilova
            }
675
            pstmt.close();
676
            return false;
677
          }
678
          hasRows = rs.next();
679 555 bojilova
        }
680 688 bojilova
//System.out.println("Passed the check for \"deny\" access with \"allowFirst\"");
681 555 bojilova
682 688 bojilova
        // it is not denied then check if it is "allow"
683 570 bojilova
        // Bind the values to the query
684 802 bojilova
        pstmt.setString(1, docid);
685 570 bojilova
        pstmt.setString(2, principal);
686 688 bojilova
        pstmt.setString(3, "allow");
687 570 bojilova
688 555 bojilova
        pstmt.execute();
689 570 bojilova
        rs = pstmt.getResultSet();
690
        hasRows = rs.next();
691
        while ( hasRows ) {
692
          accessValue = rs.getInt(1);
693 688 bojilova
          permOrder = rs.getString(2);
694 570 bojilova
          ticketCount = rs.getInt(3);
695
          if ( ( accessValue & intValue(permission) )==intValue(permission) &&
696
               ( rs.wasNull() || ticketCount > 0 ) ) {
697
            if ( !rs.wasNull() && ticketCount > 0 ) {
698 802 bojilova
              decreaseNumberOfAccess(accessValue,principal,docid,"allow",permOrder);
699 570 bojilova
            }
700
            pstmt.close();
701
            return true;
702
          }
703
          hasRows = rs.next();
704
        }
705 688 bojilova
//System.out.println("Passed the check for \"allow\" access");
706 570 bojilova
707 688 bojilova
        // it is not allowed then check if it is "deny" with "denyFirst"
708
        // Bind the values to the query
709 802 bojilova
        pstmt.setString(1, docid);
710 688 bojilova
        pstmt.setString(2, principal);
711
        pstmt.setString(3, "deny");
712
713
        pstmt.execute();
714
        rs = pstmt.getResultSet();
715
        hasRows = rs.next();
716
        while ( hasRows ) {
717
          accessValue = rs.getInt(1);
718
          permOrder = rs.getString(2);
719
          ticketCount = rs.getInt(3);
720
          if ( ( accessValue & intValue(permission) ) == intValue(permission) &&
721
               ( permOrder.equals("denyFirst") ) &&
722
               ( rs.wasNull() || ticketCount > 0 ) ) {
723
            if ( !rs.wasNull() && ticketCount > 0 ) {
724 802 bojilova
              decreaseNumberOfAccess(accessValue,principal,docid,"deny","denyFirst");
725 688 bojilova
            }
726
            pstmt.close();
727
            return false;
728
          }
729
          hasRows = rs.next();
730
        }
731
//System.out.println("Passed the check for \"deny\" access wirh \"denyFirst\"");
732 570 bojilova
733
        pstmt.close();
734
        return false;
735
736
      } catch (SQLException e) {
737
        throw new
738 688 bojilova
        SQLException("AccessControlList.hasPermission(). " +
739
                     "Error checking " + permission + " permission for " +
740 802 bojilova
                     principal + " on document #" + docid + ". " +
741 688 bojilova
                     e.getMessage());
742 555 bojilova
      }
743 570 bojilova
    }
744
745
    return false;
746
  }
747 555 bojilova
748 802 bojilova
  /* Decrease the number of access to @docid for @principal in db. */
749 570 bojilova
  private void decreaseNumberOfAccess(int permission, String principal,
750 802 bojilova
                                      String docid, String permType,
751 688 bojilova
                                      String permOrder)
752 570 bojilova
               throws SQLException
753
  {
754
    PreparedStatement pstmt;
755
    pstmt = conn.prepareStatement(
756
            "UPDATE xml_access SET ticket_count = ticket_count - 1 " +
757 765 bojilova
            "WHERE docid = ? " +
758
            "AND principal_name = ? " +
759
            "AND permission = ? " +
760
            "AND perm_type = ? " +
761
            "AND perm_order = ? " +
762 774 bojilova
            "AND " + sysdate +
763
            " BETWEEN " + isnull + "(begin_time," + sysdate + ") " +
764
                 "AND " + isnull + "(end_time," + sysdate + ")");
765 570 bojilova
    // Bind the values to the query
766 802 bojilova
    pstmt.setString(1, docid);
767 570 bojilova
    pstmt.setString(2, principal);
768
    pstmt.setInt(3, permission);
769
    pstmt.setString(4, permType);
770 688 bojilova
    pstmt.setString(5, permOrder);
771 570 bojilova
772
    pstmt.execute();
773
    pstmt.close();
774
  }
775
776 688 bojilova
777
  /**
778
    * Get Access Control List information for document from db connetion.
779
    * User or Group should have permissions for reading
780
    * access control information for a document specified by @docid.
781
    * @param docid document identifier which acl info to get
782
    * @param user name of user connected to Metacat system
783 802 bojilova
    * @param groups names of user's groups to which user belongs
784 688 bojilova
    */
785 802 bojilova
  public String getACL(String docid, String user, String[] groups)
786 688 bojilova
          throws SQLException
787 570 bojilova
  {
788 688 bojilova
    StringBuffer output = new StringBuffer();
789
    StringBuffer outTemp = new StringBuffer();
790
    MetaCatUtil util = new MetaCatUtil();
791
    String accDoctype = util.getOption("accessdoctype");
792
    String server = util.getOption("server");
793
    String docurl = "metacat://" + server + "/?docid=" + docid;
794
    String systemID;
795
    boolean isOwned = false;
796
    boolean hasPermission = false;
797
    String publicAcc;
798 570 bojilova
799 688 bojilova
    String acfid = "";
800
    String acfid_prev = "";
801
    String principal;
802
    Vector principalArr = new Vector();
803
    int permission;
804
    int perm_prev = -1;
805
    String permType;
806
    String permOrder = "";
807
    String permOrder_prev = "";
808
    String beginTime = "";
809
    String begin_prev = "";
810
    String endTime = "";
811
    String end_prev = "";
812
    int ticketCount = -1;
813
    int ticket_prev = -1;
814
815 638 bojilova
    try {
816 688 bojilova
817
      isOwned = isOwned(docid, user);
818
      systemID = getSystemID(accDoctype);
819
      publicAcc = getPublicAccess(docid);
820
821
      output.append("<?xml version=\"1.0\"?>\n");
822
      output.append("<!DOCTYPE acl PUBLIC \"" + accDoctype + "\" \"" +
823
                    systemID + "\">\n");
824
      output.append("<acl authSystem=\"\">\n");
825
826
      PreparedStatement pstmt;
827
      pstmt = conn.prepareStatement(
828
              "SELECT distinct accessfileid, principal_name, permission, " +
829
              "perm_type, perm_order, to_char(begin_time,'mm/dd/yyyy'), " +
830
              "to_char(end_time,'mm/dd/yyyy'), ticket_count " +
831 765 bojilova
              "FROM xml_access WHERE docid = ? " +
832 688 bojilova
              "ORDER BY accessfileid, perm_order, perm_type, permission");
833
      // Bind the values to the query
834
      pstmt.setString(1, docid);
835
      pstmt.execute();
836
      ResultSet rs = pstmt.getResultSet();
837
      boolean hasRows = rs.next();
838
      while (hasRows) {
839
840
        acfid = rs.getString(1);
841
        principal = rs.getString(2);
842
        permission = rs.getInt(3);
843
        permType = rs.getString(4);
844
        permOrder = rs.getString(5);
845
        beginTime = rs.getString(6);
846
        endTime = rs.getString(7);
847
        ticketCount = rs.getInt(8);
848
849
        // if @docid is not owned by @user, only ACL info from that
850 802 bojilova
        // access files to which @user/@groups has "read" permission
851 688 bojilova
        // is extracted
852
        if ( !isOwned ) {
853
          if ( !acfid.equals(acfid_prev) ) {
854
            acfid_prev = acfid;
855 802 bojilova
            hasPermission = this.hasPermission("READ",user,groups,acfid);
856 688 bojilova
          }
857
          if ( !hasPermission ) {
858
            rs.next();
859
            continue;
860
          }
861
        }
862
863
        // open <resource> tag
864
        if ( !permOrder.equals(permOrder_prev) ) {
865
          // close </resource> tag if any was opened
866
          output.append(outTemp.toString());
867
          outTemp = new StringBuffer();
868
          if ( !permOrder_prev.equals("") ) {
869
            output.append("  </resource>\n");
870
          }
871
          output.append("  <resource order=\"" + permOrder + "\" public=\"" +
872
                        publicAcc + "\">\n");
873
          output.append("    <resourceIdentifier>" + docurl +
874
                        "</resourceIdentifier>\n");
875
          permOrder_prev = permOrder;
876
        }
877
878
        // close </allow> or </deny> tag then open new one
879
        if ( permission != perm_prev ||
880
             (endTime == null) && (end_prev != null) ||
881
             (beginTime == null) && (begin_prev != null) ||
882
             endTime != null && !endTime.equals(end_prev)  ||
883
             beginTime != null && !beginTime.equals(begin_prev) ||
884
             ticketCount != ticket_prev )  {
885
          output.append(outTemp.toString());
886
          outTemp = new StringBuffer();
887
          principalArr.removeAllElements();
888
          output.append("    <" + permType + ">\n");
889
        }
890
891
        // put all principals here for the same
892
        // permission, duration and ticket_count
893
        if ( !principalArr.contains(principal) ) {
894
          principalArr.addElement(principal);
895
          output.append("      <principal>" + principal + "</principal>\n");
896
        }
897
898
        // prepare <permission> tags, <duration> and <ticketCount>
899
        // if any to put within <allow> (<deny>) by next cicle
900
        if ( permission != perm_prev ||
901
             (endTime == null) && (end_prev != null) ||
902
             (beginTime == null) && (begin_prev != null) ||
903
             endTime != null && !endTime.equals(end_prev)  ||
904
             beginTime != null && !beginTime.equals(begin_prev) ||
905
             ticketCount != ticket_prev )  {
906
          if ( (permission & READ) != 0 ) {
907
            outTemp.append("      <permission>read</permission>\n");
908
          }
909
          if ( (permission & WRITE) != 0 ) {
910
            outTemp.append("      <permission>write</permission>\n");
911
          }
912
          if ( (permission & ALL) != 0 ) {
913
            outTemp.append("      <permission>all</permission>\n");
914
          }
915
          if ( (beginTime != null) || (endTime != null) ) {
916
            outTemp.append("      <duration>" + beginTime + " " + endTime +
917
                          "</duration>\n");
918
          }
919
          if ( ticketCount > 0 ) {
920
            outTemp.append("      <ticketCount>" + ticketCount +
921
                          "</ticketCount>\n");
922
          }
923
          outTemp.append("    </" + permType + ">\n");
924
          perm_prev = permission;
925
          ticket_prev = ticketCount;
926
          begin_prev = beginTime;
927
          end_prev = endTime;
928
        }
929
930
        hasRows = rs.next();
931 652 bojilova
      }
932 688 bojilova
933
      // close <allow> or <deny> if anything left in outTemp var
934
      output.append(outTemp.toString());
935
936
      // If there are no any acl info for @docid accessible by @user/@group,
937
      // extract only the following information
938
      if ( permOrder.equals("") ) {
939
        output.append("  <resource public=\"" + publicAcc + "\">\n");
940
        output.append("    <resourceIdentifier>" + docurl +
941
                      "</resourceIdentifier>\n");
942
      }
943
944
      // always close them
945
      output.append("  </resource>\n");
946
      output.append("</acl>\n");
947
948
      pstmt.close();
949
950
      return output.toString();
951
952
    } catch (SQLException e) {
953
      throw new
954
      SQLException("AccessControlList.getACL(). " + e.getMessage());
955 638 bojilova
    }
956 688 bojilova
  }
957
958
  /* Check if @user is owner of @docid from db conn. */
959
  private boolean isOwned(String docid, String user) throws SQLException {
960
961
    PreparedStatement pstmt;
962
    pstmt = conn.prepareStatement("SELECT 'x' FROM xml_documents " +
963 765 bojilova
                                  "WHERE docid = ? " +
964
                                  "AND user_owner = ?");
965 688 bojilova
    pstmt.setString(1, docid);
966
    pstmt.setString(2, user);
967
    pstmt.execute();
968
    ResultSet rs = pstmt.getResultSet();
969
    boolean hasRow = rs.next();
970
971
    return hasRow;
972
  }
973 652 bojilova
974 688 bojilova
  /* Get the flag for public "read" access for @docid from db conn. */
975
  private String getPublicAccess(String docid) throws SQLException {
976
977
    int publicAcc = 0;
978
    PreparedStatement pstmt;
979
    pstmt = conn.prepareStatement("SELECT public_access FROM xml_documents " +
980 765 bojilova
                                  "WHERE docid = ?");
981 688 bojilova
    pstmt.setString(1, docid);
982
    pstmt.execute();
983
    ResultSet rs = pstmt.getResultSet();
984
    boolean hasRow = rs.next();
985
    if ( hasRow ) {
986
      publicAcc = rs.getInt(1);
987
    }
988
989
    return (publicAcc == 1) ? "yes" : "no";
990
  }
991
992
  /* Get SystemID for @publicID from Metacat DB Catalog. */
993
  private String getSystemID(String publicID) throws SQLException {
994
995
    String systemID = "";
996
    PreparedStatement pstmt;
997
    pstmt = conn.prepareStatement("SELECT system_id FROM xml_catalog " +
998
                                  "WHERE entry_type = 'DTD' " +
999 765 bojilova
                                  "AND public_id = ?");
1000 688 bojilova
    pstmt.setString(1, publicID);
1001
    pstmt.execute();
1002
    ResultSet rs = pstmt.getResultSet();
1003
    boolean hasRow = rs.next();
1004
    if ( hasRow ) {
1005
      systemID = rs.getString(1);
1006
    }
1007
1008
    return systemID;
1009
  }
1010
1011 555 bojilova
}