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