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 10:23:26 -0800 (Wed, 13 Dec 2000) $'
12
 * '$Revision: 604 $'
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
  public AccessControlList ()
67
  {
68
   // this.conn = conn;
69
  }
70

    
71
  /**
72
   * Construct an instance of the AccessControlList class.
73
   * It is used by the permission check up from DBQuery and DocumentImpl
74
   *
75
   * @param conn the JDBC connection where acl data are loaded
76
   */
77
  public AccessControlList ( Connection conn )
78
  {
79
    this.conn = conn;
80
  }
81

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

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

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

    
110
  }
111

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

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

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

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

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

    
148
    // Set the ErrorHandler to this instance
149
    parser.setErrorHandler((ErrorHandler)this);
150

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

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

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

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

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

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

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

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

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

    
266
  }
267

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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