Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that loads eml-access.xml file containing ACL 
4
 *             for a metadata document into relational DB
5
 *  Copyright: 2000 Regents of the University of California and the
6
 *             National Center for Ecological Analysis and Synthesis
7
 *    Authors: Jivka Bojilova
8
 *    Release: @release@
9
 *
10
 *   '$Author: bojilova $'
11
 *     '$Date: 2001-01-31 10:05:23 -0800 (Wed, 31 Jan 2001) $'
12
 * '$Revision: 688 $'
13
 *
14
 * This program is free software; you can redistribute it and/or modify
15
 * it under the terms of the GNU General Public License as published by
16
 * the Free Software Foundation; either version 2 of the License, or
17
 * (at your option) any later version.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 * GNU General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU General Public License
25
 * along with this program; if not, write to the Free Software
26
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27
 */
28

    
29
package edu.ucsb.nceas.metacat;
30

    
31
import java.io.*;
32
import java.sql.*;
33
import java.util.Stack;
34
import java.util.Vector;
35
import java.util.Hashtable;
36
import java.net.URL;
37
import java.net.MalformedURLException;
38

    
39
import org.xml.sax.Attributes;
40
import org.xml.sax.InputSource;
41
import org.xml.sax.ContentHandler;
42
import org.xml.sax.EntityResolver;
43
import org.xml.sax.ErrorHandler;
44
import org.xml.sax.SAXException;
45
import org.xml.sax.SAXParseException;
46
import org.xml.sax.XMLReader;
47
import org.xml.sax.helpers.XMLReaderFactory;
48
import org.xml.sax.helpers.DefaultHandler;
49

    
50
/** 
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
  private Connection conn;
62
  private String parserName;
63
  private Stack elementStack;
64
  private String server;
65

    
66
  private boolean	processingDTD;
67
  private String  user;
68
  private String  group;
69
  private String  aclid;
70
  private String 	docname;
71
  private String 	doctype;
72
  private String 	systemid;
73

    
74
  private String docurl;
75
  private Vector resourceURL;
76
  private Vector resourceID;
77
  private Vector principal;
78
  private int    permission;
79
  private String permType;
80
  private String permOrder;
81
  private String publicAcc;
82
  private String beginTime;
83
  private String endTime;
84
  private int    ticketCount;
85
  private int    serverCode = 1;
86
  
87
  /**
88
   * Construct an instance of the AccessControlList class.
89
   * It is used by the permission check up from DBQuery or DocumentImpl
90
   * and from "getaccesscontrol" action
91
   *
92
   * @param conn the JDBC connection where acl info is get
93
   */
94
  public AccessControlList(Connection conn) throws SQLException
95
  {
96
    this.conn = conn;
97
  }
98

    
99
  /**
100
   * Construct an instance of the AccessControlList class.
101
   * It parse acl file and loads acl data into db connection.
102
   *
103
   * @param conn the JDBC connection where acl data are loaded
104
   * @param aclid the Accession# of the document with the acl data
105
   * @param acl the acl file containing acl data
106
   * @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
   */
111
  public AccessControlList(Connection conn, String aclid, Reader acl,
112
                           String user, String group, int serverCode)
113
                  throws SAXException, IOException, ClassNotFoundException 
114
  {
115
    // Get an instance of the parser
116
    MetaCatUtil util = new MetaCatUtil();
117
    String parserName = util.getOption("saxparser");
118
    this.server = util.getOption("server");
119

    
120
    this.conn = conn;
121
    this.parserName = parserName;
122
    this.processingDTD = false;
123
    this.elementStack = new Stack();
124
    
125
    this.user = user;
126
    this.group = group;
127
    this.aclid = aclid;
128
    this.resourceURL = new Vector();
129
    this.resourceID = new Vector();
130
    this.principal = new Vector();
131
    this.permission = 0;
132
    this.ticketCount = 0;
133
    this.publicAcc = null;
134
    this.serverCode = serverCode;
135
    
136
    // Initialize the parser and read the queryspec
137
    XMLReader parser = initializeParser();
138
    parser.parse(new InputSource(acl));
139

    
140
  }
141

    
142
  /**
143
   * Construct an instance of the AccessControlList class.
144
   * It parses eml-access file and loads acl data into db connection.
145
   * It is used from command line execution.
146
   *
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
   * @param user the user connected to MetaCat servlet and owns the document
151
   * @param group the group to which user belongs
152
   */
153
  public AccessControlList( Connection conn, String aclid, String aclfilename,
154
                           String user, String group )
155
                  throws SAXException, IOException, ClassNotFoundException 
156
  {
157
    this(conn, aclid, new FileReader(new File(aclfilename).toString()), 
158
         user, group, 1);
159
  }
160
  
161
  /* Set up the SAX parser for reading the XML serialized ACL */
162
  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
    // Turn off validation
170
    parser.setFeature("http://xml.org/sax/features/validation", true);
171
      
172
    // Set Handlers in the parser
173
    // Set the ContentHandler to this instance
174
    parser.setContentHandler((ContentHandler)this);
175

    
176
    // 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
    // Set the ErrorHandler to this instance
182
    parser.setErrorHandler((ErrorHandler)this);
183

    
184
    return parser;
185
  }
186
  
187
  /**
188
   * Callback method used by the SAX Parser when beginning of the document
189
   */
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
   * Callback method used by the SAX Parser when the start tag of an 
208
   * 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
    if ( currentNode.getTagName().equals("resource") ) {
223
      permOrder = currentNode.getAttribute("order");
224
      publicAcc = currentNode.getAttribute("public");
225
    }
226
    elementStack.push(currentNode); 
227
  }
228

    
229
  /**
230
   * Callback method used by the SAX Parser when the text sequences of an 
231
   * 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
    if (currentTag.equals("resourceIdentifier")) {
242

    
243
      // 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
      
256
      // if it is the local server (originator of the document),
257
      // check for permission for @user on resource is needed
258
      // @user must have permission "all" on it(docid)
259
      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
        }
269
        if ( !hasPermission ) {
270
          throw new SAXException(
271
          "Permission denied for setting access control on " + docid);
272
        }
273
      }
274
      // end of check for "all" perm on docid
275

    
276
    } else if (currentTag.equals("principal")) {
277

    
278
      principal.addElement(inputString);
279

    
280
    } else if (currentTag.equals("permission")) {
281

    
282
      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
      } else {
289
        throw new SAXException("Unknown permission type: " + inputString);
290
      }
291

    
292
    } else if (currentTag.equals("duration") && 
293
               beginTime == null && endTime == null ) {
294
      try {
295
        beginTime = inputString.substring(0, inputString.indexOf(" "));
296
        endTime = inputString.substring(inputString.indexOf(" ")+1);
297
      } catch (StringIndexOutOfBoundsException se) {
298
        beginTime = inputString;
299
      }
300

    
301
    } else if (currentTag.equals("ticketCount") && ticketCount == 0 ) {
302
      try {
303
        ticketCount = (new Integer(inputString.trim())).intValue();
304
      } catch (NumberFormatException nfe) {
305
        throw new SAXException("Wrong integer format for:" + inputString);
306
      }
307
    }
308
  }
309

    
310
  /**
311
   * Callback method used by the SAX Parser when the end tag of an 
312
   * 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
    BasicNode leaving = (BasicNode)elementStack.pop();
319
    String leavingTagName = leaving.getTagName();
320

    
321
    if ( leavingTagName.equals("resourceIdentifier") ) {
322
      
323
      try {
324
        // make a relationship for @aclid on the current resource(docurl)
325
        if ( aclid != null ) {
326
          insertRelation(aclid, docurl);
327
        }
328
      } catch (SQLException sqle) {
329
        throw new SAXException(sqle);
330
      }
331
      
332
    } else if ( leavingTagName.equals("allow") ||
333
                leavingTagName.equals("deny")    ) {
334
      
335
      if ( permission > 0 ) {
336

    
337
        // insert into db calculated permission for the list of principals
338
        try {
339
          insertPermissions(leavingTagName);
340
        } catch (SQLException sqle) {
341
          throw new SAXException(sqle);
342
        }
343
      }
344

    
345
      // reset the allow/deny permission
346
      principal = new Vector();
347
      permission = 0;
348
      beginTime = null;
349
      endTime = null;
350
      ticketCount = 0;
351
    
352
    } else if ( leavingTagName.equals("resource") ) {
353

    
354
      // update public access for the list of resources
355
      if ( publicAcc != null ) {
356
        try {
357
          updatePublicAccess(publicAcc);
358
        } catch (SQLException sqle) {
359
          throw new SAXException(sqle);
360
        }
361
      }
362
      
363
      // reset the resource
364
      resourceID = new Vector();
365
      resourceURL = new Vector();
366
      permOrder = null;
367
      publicAcc = null;
368
    }
369

    
370
  }
371

    
372
  /** 
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
  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
   * SAX Handler that receives notification of the start of entities.
388
   * @param name name of the entity
389
   */
390
  public void startEntity(String name) throws SAXException {
391
    if (name.equals("[dtd]")) {
392
      processingDTD = true;
393
    }
394
  }
395

    
396
  /** 
397
   * SAX Handler that receives notification of the end of entities.
398
   * @param name name of the entity
399
   */
400
  public void endEntity(String name) throws SAXException {
401
    if (name.equals("[dtd]")) {
402
      processingDTD = false;
403
    }
404
  }
405

    
406
  /**
407
   * Get the document name.
408
   */
409
  public String getDocname() {
410
    return docname;
411
  }
412

    
413
  /**
414
   * Get the document processing state.
415
   */
416
  public boolean processingDTD() {
417
    return processingDTD;
418
  }
419
  
420
  /* Delete from db all permission for resources related to @aclid if any.*/
421
  private void deletePermissionsForRelatedResources(String aclid) 
422
          throws SQLException 
423
  {
424
    // delete all acl records for resources related to @aclid if any
425
    Statement stmt = conn.createStatement();
426
    stmt.execute("DELETE FROM xml_access WHERE accessfileid = '" + aclid + "'");
427
    stmt.close();
428
  }
429

    
430
  /* Delete all of the relations with a docid of @docid from db connection.*/
431
  private void deleteRelations(String docid)
432
          throws SQLException 
433
  {
434
    Statement stmt = conn.createStatement();
435
    stmt.execute("DELETE FROM xml_relation WHERE docid='" + docid + "'");
436
    stmt.close();
437
  }
438

    
439
  /* Insert relationship for @aclid on @resourceURL into db connection.*/
440
  private void insertRelation(String aclid, String resourceURL) 
441
          throws SQLException 
442
  {
443
    String aclURL = "metacat://" + server + "/?docid=" + aclid;
444
    
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
  /* Insert into db calculated permission for the list of principals */
460
  private void insertPermissions( String permType ) 
461
          throws SQLException 
462
  {
463
    PreparedStatement pstmt;
464
 
465
    try {
466
      pstmt = conn.prepareStatement(
467
              "INSERT INTO xml_access " + 
468
              "(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
      // Bind the values to the query
472
      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
      pstmt.setString(9, aclid);
478
      if ( ticketCount > 0 ) {
479
        pstmt.setString(8, "" + ticketCount);
480
      } else {
481
        pstmt.setString(8, "");
482
      }
483
      String docid;
484
      String prName;
485
      for ( int i = 0; i < resourceID.size(); i++ ) {
486
        docid = (String)resourceID.elementAt(i);
487
        pstmt.setString(1, docid);
488
        for ( int j = 0; j < principal.size(); j++ ) {
489
          prName = (String)principal.elementAt(j);
490
          pstmt.setString(2, prName);
491
          pstmt.execute();
492
          
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
        }
507
      }
508
      pstmt.close();
509

    
510
    } catch (SQLException e) {
511
      throw new 
512
      SQLException("AccessControlList.insertPermissions(): " + e.getMessage());
513
    }
514
  }
515

    
516
  /* Update into db public "read" access for the list of resources. */
517
  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
      if ( publicAcc == null ) {
527
        pstmt.setString(1, null);
528
      } else if ( publicAcc.toUpperCase().equals("YES") ) {
529
        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
      pstmt.close();
538

    
539
    } catch (SQLException e) {
540
      throw new 
541
      SQLException("AccessControlList.updatePublicAccess(): " + e.getMessage());
542
    }
543
  }
544

    
545
  /* 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
  public boolean hasPermission ( String permission,
635
                                 String principal, String resourceID )
636
                 throws SQLException
637
  {
638
    PreparedStatement pstmt;
639
    // check public access to @resourceID from xml_documents table
640
    if ( permission.equals("READ") ) {
641
      try {
642
        pstmt = conn.prepareStatement(
643
                "SELECT 'x' FROM xml_documents " +
644
                "WHERE docid LIKE ? AND public_access = 1");
645
        // Bind the values to the query
646
        pstmt.setString(1, resourceID);
647

    
648
        pstmt.execute();
649
        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
        SQLException("AccessControlList.hasPermission(). " +
660
                     "Error checking public access for document #"+resourceID+
661
                     ". " + e.getMessage());
662
      }
663
    }
664
    
665
    // since owner of resource has all permission on it,
666
    // check if @principal is owner of @resourceID in xml_documents table
667
    if ( principal != null ) {
668
      try {
669
        pstmt = conn.prepareStatement(
670
                "SELECT 'x' FROM xml_documents " +
671
                "WHERE docid LIKE ? AND user_owner LIKE ?");
672
        // Bind the values to the query
673
        pstmt.setString(1, resourceID);
674
        pstmt.setString(2, principal);
675

    
676
        pstmt.execute();
677
        ResultSet rs = pstmt.getResultSet();
678
        boolean hasRow = rs.next();
679
        pstmt.close();
680
        if (hasRow) {
681
          return true;
682
        }
683
//System.out.println("Passed the check for ownership");      
684
     
685
      } catch (SQLException e) {
686
        throw new 
687
        SQLException("AccessControlList.hasPermission(). " +
688
                     "Error checking ownership for " + principal +
689
                     " on document #" + resourceID + ". " + e.getMessage());
690
      }
691

    
692
      // check @principal's @permission on @resourceID from xml_access table
693
      int accessValue = 0;
694
      int ticketCount = 0;
695
      String permOrder = "";
696
      try {
697
        pstmt = conn.prepareStatement(
698
                "SELECT permission, perm_order, ticket_count " +
699
                "FROM xml_access " +
700
                "WHERE docid LIKE ? " + 
701
                "AND principal_name LIKE ? " +
702
                "AND perm_type LIKE ? " +
703
                "AND sysdate BETWEEN nvl(begin_time,sysdate) " +
704
                                "AND nvl(end_time,sysdate)");
705
        // check if it is "deny" with "allowFirst" first
706
        // Bind the values to the query
707
        pstmt.setString(1, resourceID);
708
        pstmt.setString(2, principal);
709
        pstmt.setString(3, "deny");
710

    
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
               ( permOrder.equals("allowFirst") ) &&
720
               ( rs.wasNull() || ticketCount > 0 ) ) {
721
            if ( !rs.wasNull() && ticketCount > 0 ) {
722
              decreaseNumberOfAccess(accessValue,principal,resourceID,"deny","allowFirst");
723
            }
724
            pstmt.close();
725
            return false;
726
          }
727
          hasRows = rs.next();
728
        }
729
//System.out.println("Passed the check for \"deny\" access with \"allowFirst\"");      
730

    
731
        // it is not denied then check if it is "allow"
732
        // Bind the values to the query
733
        pstmt.setString(1, resourceID);
734
        pstmt.setString(2, principal);
735
        pstmt.setString(3, "allow");
736

    
737
        pstmt.execute();
738
        rs = pstmt.getResultSet();
739
        hasRows = rs.next();
740
        while ( hasRows ) {
741
          accessValue = rs.getInt(1);
742
          permOrder = rs.getString(2);
743
          ticketCount = rs.getInt(3);
744
          if ( ( accessValue & intValue(permission) )==intValue(permission) &&
745
               ( rs.wasNull() || ticketCount > 0 ) ) {
746
            if ( !rs.wasNull() && ticketCount > 0 ) {
747
              decreaseNumberOfAccess(accessValue,principal,resourceID,"allow",permOrder);
748
            }
749
            pstmt.close();
750
            return true;
751
          }
752
          hasRows = rs.next();
753
        }
754
//System.out.println("Passed the check for \"allow\" access");      
755

    
756
        // 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
      
782
        pstmt.close();
783
        return false;
784
  
785
      } catch (SQLException e) {
786
        throw new 
787
        SQLException("AccessControlList.hasPermission(). " +
788
                     "Error checking " + permission + " permission for " +
789
                     principal + " on document #" + resourceID + ". " +
790
                     e.getMessage());
791
      }
792
    }
793
    
794
    return false;
795
  }
796

    
797
  /* Decrease the number of access to @resourceID for @principal in db. */
798
  private void decreaseNumberOfAccess(int permission, String principal,
799
                                      String resourceID, String permType, 
800
                                      String permOrder) 
801
               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
            "AND perm_type LIKE ? " +
810
            "AND perm_order LIKE ? " +
811
            "AND sysdate BETWEEN nvl(begin_time,sysdate) " +
812
                            "AND nvl(end_time,sysdate)");
813
    // Bind the values to the query
814
    pstmt.setString(1, resourceID);
815
    pstmt.setString(2, principal);
816
    pstmt.setInt(3, permission);
817
    pstmt.setString(4, permType);
818
    pstmt.setString(5, permOrder);
819

    
820
    pstmt.execute();
821
    pstmt.close();
822
  }
823
 
824
 
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
  {
836
    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
    
847
    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
    try {
864
      
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
      }
983

    
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
    }
1007
  }
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

    
1025
  /* 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
}
(1-1/43)