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