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: 2000-12-13 11:37:28 -0800 (Wed, 13 Dec 2000) $'
12
 * '$Revision: 608 $'
13
 */
14

    
15
package edu.ucsb.nceas.metacat;
16

    
17
import java.io.*;
18
import java.sql.*;
19
import java.util.Stack;
20
import java.util.Vector;
21

    
22
import org.xml.sax.Attributes;
23
import org.xml.sax.InputSource;
24
import org.xml.sax.ContentHandler;
25
import org.xml.sax.EntityResolver;
26
import org.xml.sax.ErrorHandler;
27
import org.xml.sax.SAXException;
28
import org.xml.sax.SAXParseException;
29
import org.xml.sax.XMLReader;
30
import org.xml.sax.helpers.XMLReaderFactory;
31
import org.xml.sax.helpers.DefaultHandler;
32

    
33
/** 
34
 * A Class that loads eml-access.xml file containing ACL for a metadata
35
 * document into relational DB. It extends DefaultHandler class to handle
36
 * SAX parsing events when processing the XML stream.
37
 */
38
public class AccessControlList extends DefaultHandler {
39

    
40
  static final int ALL = 1;
41
  static final int WRITE = 2;
42
  static final int READ = 4;
43

    
44
  private Connection conn;
45
  private String parserName;
46
  private Stack elementStack;
47

    
48
  private boolean	processingDTD;
49
  private String 	docname;
50
  private String 	doctype;
51
  private String 	systemid;
52

    
53
  private String resourceId;
54
  private Vector principalName;
55
  private int    permission;
56
  private String permType;
57
  private String permOrder;
58
  private String beginTime;
59
  private String endTime;
60
  private int    ticketCount;
61

    
62
  /**
63
   * Construct an instance of the AccessControlList class.
64
   * It is used by the permission check up from DBQuery and DocumentImpl
65
   *
66
   * @param conn the JDBC connection where acl data are loaded
67
   */
68
  public AccessControlList ( Connection conn ) throws SQLException
69
  {
70
    Connection dbconn = conn;
71
//    if ( dbconn == null || dbconn.isClosed() ) {
72
      System.out.println("AccessControlList: " + 
73
                         "DB connection was closed. Open a new one");
74
      try {
75
        MetaCatUtil util = new MetaCatUtil();
76
        dbconn = util.openDBConnection();
77
      } catch (Exception e) {
78
        throw new SQLException(e.getMessage());
79
      }
80
//    }
81
    this.conn = dbconn;
82
  }
83

    
84
  /**
85
   * Construct an instance of the AccessControlList class.
86
   * It parse acl file and loads acl data into db connection.
87
   *
88
   * @param conn the JDBC connection where acl data are loaded
89
   * @param docid the Accession# of the document with the acl data
90
   * @param acl the acl file containing acl data
91
   */
92
  public AccessControlList ( Connection conn, String docid, Reader acl )
93
                  throws SAXException, IOException, ClassNotFoundException 
94
  {
95
    // Get an instance of the parser
96
    MetaCatUtil util = new MetaCatUtil();
97
    String parserName = util.getOption("saxparser");
98

    
99
    this.conn = conn;
100
    this.parserName = parserName;
101
    this.processingDTD = false;
102
    this.elementStack = new Stack();
103
    
104
    this.principalName = new Vector();
105
    this.permission = 0;
106
    this.ticketCount = 0;
107

    
108
    // Initialize the parser and read the queryspec
109
    XMLReader parser = initializeParser();
110
    parser.parse(new InputSource(acl));
111

    
112
  }
113

    
114
  /**
115
   * Construct an instance of the AccessControlList class.
116
   * It parse acl file and loads acl data into db connection.
117
   *
118
   * @param conn the JDBC connection where acl data are loaded
119
   * @param docid the Accession# of the document with the acl data
120
   * @param aclfilename the name of acl file containing acl data
121
   */
122
  public AccessControlList( Connection conn, String docid, String aclfilename )
123
                  throws SAXException, IOException, ClassNotFoundException 
124
  {
125
    this(conn,docid,new FileReader(new File(aclfilename).toString()));
126
  }
127

    
128
  /**
129
   * Set up the SAX parser for reading the XML serialized ACL
130
   */
131
  private XMLReader initializeParser() throws SAXException 
132
  {
133
    XMLReader parser = null;
134

    
135
    // Get an instance of the parser
136
    parser = XMLReaderFactory.createXMLReader(parserName);
137

    
138
    // Turn off validation
139
    parser.setFeature("http://xml.org/sax/features/validation", false);
140
      
141
    // Set Handlers in the parser
142
    // Set the ContentHandler to this instance
143
    parser.setContentHandler((ContentHandler)this);
144

    
145
    // make a DBEntityResolver instance
146
    // Set the EntityReslover to DBEntityResolver instance
147
    EntityResolver eresolver = new DBEntityResolver(conn,this,null);
148
    parser.setEntityResolver((EntityResolver)eresolver);
149

    
150
    // Set the ErrorHandler to this instance
151
    parser.setErrorHandler((ErrorHandler)this);
152

    
153
    return parser;
154
  }
155
  
156
  /**
157
   * callback method used by the SAX Parser when the start tag of an 
158
   * element is detected. Used in this context to parse and store
159
   * the acl information in class variables.
160
   */
161
  public void startElement (String uri, String localName, 
162
                            String qName, Attributes atts) 
163
         throws SAXException 
164
  {
165
    BasicNode currentNode = new BasicNode(localName);
166
    if (atts != null) {
167
      int len = atts.getLength();
168
      for (int i = 0; i < len; i++) {
169
        currentNode.setAttribute(atts.getLocalName(i), atts.getValue(i));
170
      }
171
    }
172
    if ( currentNode.getTagName().equals("resource") ) {
173
      permOrder = currentNode.getAttribute("order");
174
    }
175
    elementStack.push(currentNode); 
176
  }
177

    
178
  /**
179
   * callback method used by the SAX Parser when the text sequences of an 
180
   * xml stream are detected. Used in this context to parse and store
181
   * the acl information in class variables.
182
   */
183
  public void characters(char ch[], int start, int length)
184
         throws SAXException 
185
  {
186
    String inputString = new String(ch, start, length);
187
    BasicNode currentNode = (BasicNode)elementStack.peek(); 
188
    String currentTag = currentNode.getTagName();
189

    
190
    if (currentTag.equals("resourceIdentifier")) {
191
      resourceId = inputString;
192
    } else if (currentTag.equals("principalName")) {
193
      principalName.addElement(new String(inputString));
194
    } else if (currentTag.equals("permission")) {
195
      if ( inputString.trim().toUpperCase().equals("READ") ) {
196
        permission = permission | READ;
197
      } else if ( inputString.trim().toUpperCase().equals("WRITE") ) {
198
        permission = permission | WRITE;
199
      } else if ( inputString.trim().toUpperCase().equals("ALL") ) {
200
        permission = permission | ALL;
201
      }
202
    } else if (currentTag.equals("duration") && 
203
               beginTime == null && endTime == null ) {
204
      try {
205
        beginTime = inputString.substring(0, inputString.indexOf(" "));
206
        endTime = inputString.substring(inputString.indexOf(" ")+1);
207
      } catch (StringIndexOutOfBoundsException se) {
208
        beginTime = inputString;
209
      }
210
    } else if (currentTag.equals("ticketCount") && ticketCount == 0 ) {
211
      ticketCount = (new Integer(inputString.trim())).intValue();
212
    }
213
  }
214

    
215
  /**
216
   * callback method used by the SAX Parser when the end tag of an 
217
   * element is detected. Used in this context to parse and store
218
   * the acl information in class variables.
219
   */
220
  public void endElement (String uri, String localName, String qName)
221
         throws SAXException 
222
  {
223
    BasicNode leaving = (BasicNode)elementStack.pop(); 
224
    if ( leaving.getTagName().equals("allow") ) {
225
      
226
      if ( permission > 0 ) {
227

    
228
        // insert into db calculated permission for the list of principals
229
        try {
230
          insertPermissions("allowed");
231
        } catch (SQLException sqle) {
232
          throw new SAXException(sqle);
233
        }
234
      }
235

    
236
      // reset the allowed permission
237
      principalName = new Vector();
238
      permission = 0;
239
      beginTime = null;
240
      endTime = null;
241
      ticketCount = 0;
242
    
243
    } else if ( leaving.getTagName().equals("deny") ) {
244
      
245
      if ( permission > 0 ) {
246

    
247
        // insert into db calculated permission for the list of principals
248
        try {
249
          insertPermissions("denied");
250
        } catch (SQLException sqle) {
251
          throw new SAXException(sqle);
252
        }
253
      }
254

    
255
      // reset the denied permission
256
      principalName = new Vector();
257
      permission = 0;
258
      beginTime = null;
259
      endTime = null;
260
      ticketCount = 0;
261

    
262
    } else if ( leaving.getTagName().equals("resource") ) {
263
      // reset the resource identifier
264
      resourceId = null;
265
      permOrder = null;
266
    }
267

    
268
  }
269

    
270
  /** SAX Handler that receives notification of DOCTYPE. Sets the DTD */
271
  public void startDTD(String name, String publicId, String systemId) 
272
              throws SAXException {
273
    docname = name;
274
    doctype = publicId;
275
    systemid = systemId;
276
//    processingDTD = true;
277
  }
278

    
279
  /** 
280
   * SAX Handler that receives notification of the start of entities
281
   */
282
  public void startEntity(String name) throws SAXException {
283
    if (name.equals("[dtd]")) {
284
      processingDTD = true;
285
    }
286
  }
287

    
288
  /** 
289
   * SAX Handler that receives notification of the end of entities
290
   */
291
  public void endEntity(String name) throws SAXException {
292
    if (name.equals("[dtd]")) {
293
      processingDTD = false;
294
    }
295
  }
296

    
297
  /**
298
   * get the document name
299
   */
300
  public String getDocname() {
301
    return docname;
302
  }
303

    
304
  /**
305
   * get the document processing state
306
   */
307
  public boolean processingDTD() {
308
    return processingDTD;
309
  }
310
  
311
  /** Insert into db calculated permission for the list of principals */
312
  private void insertPermissions( String permType ) 
313
          throws SQLException 
314
  {
315
    PreparedStatement pstmt;
316
    // 
317
    try {
318
      pstmt = conn.prepareStatement(
319
              "INSERT INTO xml_access " + 
320
              "(docid,principal_name,permission,perm_type,perm_order," +
321
              "begin_time,end_time,ticket_count) VALUES " +
322
              "(?,?,?,?,?,to_date(?,'mm/dd/yy'),to_date(?,'mm/dd/yy'),?)");
323
      // Bind the values to the query
324
      pstmt.setString(1, resourceId);
325
      pstmt.setInt(3, permission);
326
      pstmt.setString(4, permType);
327
      pstmt.setString(5, permOrder);
328
      pstmt.setString(6, beginTime);
329
      pstmt.setString(7, endTime);
330
      if ( ticketCount > 0 ) {
331
        pstmt.setString(8, "" + ticketCount);
332
      } else {
333
        pstmt.setString(8, "");
334
      }
335
      for ( int i = 0; i < principalName.size(); i++ ) {
336
        pstmt.setString(2, (String)principalName.elementAt(i));
337
        pstmt.execute();
338
      }
339

    
340
    } catch (SQLException e) {
341
      throw new 
342
      SQLException("AccessControlList.insertPermissions(): " + e.getMessage());
343
    }
344
  }
345

    
346
  /** Check for @permission for @principal on @resourceId from db connection */
347
  public boolean hasPermission ( String permission,
348
                                 String principal, String resourceId )
349
                 throws SQLException
350
  {
351
    PreparedStatement pstmt;
352
    // check public access to @resourceId from xml_documents table
353
    if ( permission.equals("READ") ) {
354
      try {
355
        pstmt = conn.prepareStatement(
356
                "SELECT 'x' FROM xml_documents " +
357
                "WHERE docid LIKE ? AND public_access = 1");
358
        // Bind the values to the query
359
        pstmt.setString(1, principal);
360

    
361
        pstmt.execute();
362
        ResultSet rs = pstmt.getResultSet();
363
        boolean hasRow = rs.next();
364
        pstmt.close();
365
        if (hasRow) {
366
          return true;
367
        }
368
//System.out.println("Passed the check for public access");      
369

    
370
      } catch (SQLException e) {
371
        throw new 
372
        SQLException("Error checking document's public access: "
373
                      + e.getMessage());
374
      }
375
    }
376
    
377
    // since owner of resource has all permission on it,
378
    // check if @principal is owner of @resourceId in xml_documents table
379
    if ( principal != null ) {
380
      try {
381
        pstmt = conn.prepareStatement(
382
                "SELECT 'x' FROM xml_documents " +
383
                "WHERE docid LIKE ? AND user_owner LIKE ?");
384
        // Bind the values to the query
385
        pstmt.setString(1, resourceId);
386
        pstmt.setString(2, principal);
387

    
388
        pstmt.execute();
389
        ResultSet rs = pstmt.getResultSet();
390
        boolean hasRow = rs.next();
391
        pstmt.close();
392
        if (hasRow) {
393
          return true;
394
        }
395
//System.out.println("Passed the check for ownership");      
396
     
397
      } catch (SQLException e) {
398
        throw new 
399
        SQLException("AccessControlList.hasPermission(): " +
400
                     "Error checking document's ownership. " + e.getMessage());
401
      }
402

    
403
      // check @principal's @permission on @resourceId from xml_access table
404
      int accessValue = 0;
405
      int ticketCount = 0;
406
      String permOrder = "";
407
      try {
408
        pstmt = conn.prepareStatement(
409
                "SELECT permission, perm_order, ticket_count " +
410
                "FROM xml_access " +
411
                "WHERE docid LIKE ? " + 
412
                "AND principal_name LIKE ? " +
413
                "AND sysdate BETWEEN nvl(begin_time,sysdate) " +
414
                                "AND nvl(end_time,sysdate) " +
415
                "AND perm_type LIKE ?");
416
        // check if it is "denied" first
417
        // Bind the values to the query
418
        pstmt.setString(1, resourceId);
419
        pstmt.setString(2, principal);
420
        pstmt.setString(3, "denied");
421

    
422
        pstmt.execute();
423
        ResultSet rs = pstmt.getResultSet();
424
        boolean hasRows = rs.next();
425
        while ( hasRows ) {
426
          accessValue = rs.getInt(1);
427
          permOrder = rs.getString(2);
428
          ticketCount = rs.getInt(3);
429
          if ( ( accessValue & intValue(permission) ) == intValue(permission) &&
430
               ( permOrder.equals("allow first") ) &&
431
               ( rs.wasNull() || ticketCount > 0 ) ) {
432
            if ( !rs.wasNull() && ticketCount > 0 ) {
433
              decreaseNumberOfAccess(accessValue,principal,resourceId,"denied");
434
            }
435
            pstmt.close();
436
            return false;
437
          }
438
          hasRows = rs.next();
439
        }
440
//System.out.println("Passed the check for denied access");      
441

    
442
        // it is not denied then check if it is "allowed"
443
        // Bind the values to the query
444
        pstmt.setString(1, resourceId);
445
        pstmt.setString(2, principal);
446
        pstmt.setString(3, "allowed");
447

    
448
        pstmt.execute();
449
        rs = pstmt.getResultSet();
450
        hasRows = rs.next();
451
        while ( hasRows ) {
452
          accessValue = rs.getInt(1);
453
          ticketCount = rs.getInt(3);
454
          if ( ( accessValue & intValue(permission) )==intValue(permission) &&
455
               ( rs.wasNull() || ticketCount > 0 ) ) {
456
            if ( !rs.wasNull() && ticketCount > 0 ) {
457
              decreaseNumberOfAccess(accessValue,principal,resourceId,"allowed");
458
            }
459
            pstmt.close();
460
            return true;
461
          }
462
          hasRows = rs.next();
463
        }
464
//System.out.println("Passed the check for allowed access");      
465

    
466
        // ???
467
        // here there could be a check for the group's permission like 
468
        // selfrecursive call to hasPermission(conn,permission,group,recourceId)
469
        //
470
      
471
        pstmt.close();
472
        return false;
473
  
474
      } catch (SQLException e) {
475
        throw new 
476
        SQLException("AccessControlList.hasPermission(): " +
477
                     "Error checking document's permission. " + e.getMessage());
478
      }
479
    }
480
    
481
    return false;
482
  }
483

    
484
  /** decrease the number of access to @resourceId for @principal */
485
  private void decreaseNumberOfAccess(int permission, String principal,
486
                                      String resourceId, String permType)
487
               throws SQLException
488
  {
489
    PreparedStatement pstmt;
490
    pstmt = conn.prepareStatement(
491
            "UPDATE xml_access SET ticket_count = ticket_count - 1 " +
492
            "WHERE docid LIKE ? " +
493
            "AND principal_name LIKE ? " +
494
            "AND permission LIKE ? " +
495
            "AND sysdate BETWEEN nvl(begin_time,sysdate) " +
496
                            "AND nvl(end_time,sysdate) " +
497
            "AND perm_type LIKE ?");
498
    // Bind the values to the query
499
    pstmt.setString(1, resourceId);
500
    pstmt.setString(2, principal);
501
    pstmt.setInt(3, permission);
502
    pstmt.setString(4, permType);
503

    
504
    pstmt.execute();
505
    pstmt.close();
506
  }
507
 
508
  // get the int value of READ, WRITE or ALL
509
  private int intValue ( String permission )
510
  {
511
    if ( permission.equals("READ") ) {
512
      return READ;
513
    } else if ( permission.equals("WRITE") ) {
514
      return WRITE;
515
    } else if ( permission.equals("ALL") ) {
516
      return ALL;
517
    }
518
    
519
    return -1;
520
  }
521
}
(1-1/38)