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