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
 *
9
 *   '$Author: leinfelder $'
10
 *     '$Date: 2012-07-12 12:21:29 -0700 (Thu, 12 Jul 2012) $'
11
 * '$Revision: 7307 $'
12
 *
13
 * This program is free software; you can redistribute it and/or modify
14
 * it under the terms of the GNU General Public License as published by
15
 * the Free Software Foundation; either version 2 of the License, or
16
 * (at your option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 * GNU General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU General Public License
24
 * along with this program; if not, write to the Free Software
25
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
 */
27

    
28
package edu.ucsb.nceas.metacat.accesscontrol;
29

    
30
import java.io.IOException;
31
import java.io.StringReader;
32
import java.sql.PreparedStatement;
33
import java.sql.ResultSet;
34
import java.sql.SQLException;
35
import java.util.Vector;
36

    
37
import org.apache.log4j.Logger;
38
import org.xml.sax.ContentHandler;
39
import org.xml.sax.ErrorHandler;
40
import org.xml.sax.InputSource;
41
import org.xml.sax.SAXException;
42
import org.xml.sax.XMLReader;
43
import org.xml.sax.helpers.XMLReaderFactory;
44

    
45
import edu.ucsb.nceas.metacat.DBUtil;
46
import edu.ucsb.nceas.metacat.DocInfoHandler;
47
import edu.ucsb.nceas.metacat.IdentifierManager;
48
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
49
import edu.ucsb.nceas.metacat.McdbException;
50
import edu.ucsb.nceas.metacat.PermissionController;
51
import edu.ucsb.nceas.metacat.database.DBConnection;
52
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
53
import edu.ucsb.nceas.metacat.properties.PropertyService;
54
import edu.ucsb.nceas.metacat.shared.AccessException;
55
import edu.ucsb.nceas.metacat.util.DocumentUtil;
56
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
57

    
58

    
59
/** 
60
 * A Class that loads eml-access.xml file containing ACL for a metadata
61
 * document into relational DB. It extends DefaultHandler class to handle
62
 * SAX parsing events when processing the XML stream.
63
 */
64
public class AccessControlForSingleFile implements AccessControlInterface 
65
{
66

    
67
  private String _guid;
68
  
69
  private Logger logMetacat = Logger.getLogger(AccessControlForSingleFile.class);
70
 
71
    /**
72
	 * Construct an instance of the AccessControlForSingleFile class.  This
73
	 * instance will represent one file only.
74
	 * 
75
	 * @param myAccessNumber
76
	 *            the docid or docid with dev will be controlled
77
	 */
78
	public AccessControlForSingleFile(String accessionNumber)
79
			throws AccessControlException {
80

    
81
		// this is a local metacat id
82
		String docid = DocumentUtil.getDocIdFromString(accessionNumber);
83
		// get the revision
84
		String revision = DocumentUtil.getRevisionStringFromString(accessionNumber);
85
		int rev = -1;
86
		if (revision != null) {
87
			// we were given it
88
			rev = Integer.valueOf(revision);
89
		} else {
90
			// look up the latest
91
			try {
92
				rev = DBUtil.getLatestRevisionInDocumentTable(docid);
93
			} catch (SQLException e) {
94
				AccessControlException ace = new AccessControlException(e.getMessage());
95
				ace.initCause(e);
96
				throw ace;
97
			}
98
			if (rev <= 0) {
99
				// look in the revisions table
100
				try {
101
					rev = DBUtil.getMaxRevFromRevisionTable(docid);
102
				} catch (SQLException e) {
103
					AccessControlException ace = new AccessControlException(e.getMessage());
104
					ace.initCause(e);
105
					throw ace;
106
				}
107
			}
108
		}
109
		
110
		// find the guid for this docid.rev
111
		try {
112
			_guid = IdentifierManager.getInstance().getGUID(docid, rev);
113
		} catch (McdbDocNotFoundException e) {
114
			AccessControlException ace = new AccessControlException(e.getMessage());
115
			ace.initCause(e);
116
			throw ace;
117
		}
118
		
119
		// couldn't find it?
120
		if (_guid == null || _guid.equals("")) {
121
			throw new AccessControlException("Guid cannot be null");
122
		}
123

    
124
		logMetacat.debug("AccessControlForSingleFile() - docid: " + _guid);
125
	}
126
  
127
  	/**
128
	 * Insert a single access record into the database based on access DAO
129
	 * object
130
	 * 
131
	 * @param xmlAccessDAO
132
	 *            dao object holding info to insert
133
	 */
134
	public void insertPermissions(XMLAccessDAO xmlAccessDAO) 
135
			throws AccessControlException, PermOrderException{
136
		insertPermissions(xmlAccessDAO.getPrincipalName(), xmlAccessDAO.getPermission(), 
137
				xmlAccessDAO.getPermType(), xmlAccessDAO.getPermOrder(), xmlAccessDAO.getAccessFileId(), xmlAccessDAO.getSubTreeId());
138
	}
139

    
140
	/**
141
	 * Insert a single access record into the database.
142
	 * 
143
	 * @param principalName
144
	 *            the principal credentials
145
	 * @param permission
146
	 *            the permission
147
	 * @param permType
148
	 *            the permission type
149
	 * @param permOrder
150
	 *            the permission order
151
	 */
152
	public void insertPermissions(String principalName, Long permission, String permType, String permOrder, String accessFileId, String subTreeId) 
153
			throws AccessControlException, PermOrderException {
154
		try {
155
			// The addXMLAccess method will create the permission record if it does not exist.  
156
			// It will bitwise OR to permissions if the principal already has a record for this
157
			// doc id.
158
			XMLAccessAccess xmlAccessAccess = new XMLAccessAccess();
159
			//System.out.println("permission in accessControlForSingleFile.insertPermissions: " + permission);
160
			xmlAccessAccess.addXMLAccess(_guid, principalName, new Long(permission), permType, permOrder, accessFileId, subTreeId);
161
		} catch (AccessException ae) {
162
			throw new AccessControlException("AccessControlForSingleFile.insertPermissions - "
163
					+ "DB access error when inserting permissions: " + ae.getMessage());
164
		} 
165
	}
166
  
167
	/**
168
	 * Replace existing permissions with a given block of permissions for this
169
	 * document.
170
	 * 
171
	 * @param accessBlock
172
	 *            the xml access block. This is the same structure as that
173
	 *            returned by the getdocumentinfo action in metacat.
174
	 */
175
	public void insertPermissions(String accessBlock) throws AccessControlException {
176
		try {	
177
			// use DocInfoHandler to parse the access section into DAO objects
178
			XMLReader parser = null;
179
			DocInfoHandler docInfoHandler = new DocInfoHandler(_guid); 
180
			ContentHandler chandler = docInfoHandler;
181

    
182
			// Get an instance of the parser
183
			String parserName = PropertyService.getProperty("xml.saxparser");
184
			parser = XMLReaderFactory.createXMLReader(parserName);
185

    
186
			// Turn off validation
187
			parser.setFeature("http://xml.org/sax/features/validation", false);
188
			parser.setContentHandler((ContentHandler)chandler);
189
			parser.setErrorHandler((ErrorHandler)chandler);
190

    
191
			parser.parse(new InputSource(new StringReader(accessBlock)));
192
			
193
			XMLAccessAccess xmlAccessAccess = new XMLAccessAccess();
194
				
195
			// replace all access on the document
196
	        Vector<XMLAccessDAO> accessControlList = docInfoHandler.getAccessControlList();
197
	        xmlAccessAccess.replaceAccess(_guid, accessControlList);
198

    
199
		} catch (PropertyNotFoundException pnfe) {
200
			throw new AccessControlException("AccessControlForSingleFile.insertPermissions - "
201
					+ "property error when replacing permissions: " + pnfe.getMessage());
202
		} catch (AccessException ae) {
203
			throw new AccessControlException("AccessControlForSingleFile.insertPermissions - "
204
					+ "DB access error when replacing permissions: " + ae.getMessage());
205
		} catch (SAXException se) {
206
			throw new AccessControlException("AccessControlForSingleFile.insertPermissions - "
207
					+ "SAX error when replacing permissions: " + se.getMessage());
208
		} catch(IOException ioe) {
209
			throw new AccessControlException("AccessControlForSingleFile.insertPermissions - "
210
					+ "I/O error when replacing permissions: " + ioe.getMessage());
211
		}
212
	}
213
  
214
	/**
215
	 * Check if access control comination for
216
	 * docid/principal/permission/permorder/permtype already exists.
217
	 * 
218
	 * @param xmlAccessDAO
219
	 *            the dao object holding the access we want to check for.
220
	 * @return true if the Access Control for this file already exists in the DB
221
	 */
222
	public boolean accessControlExists(XMLAccessDAO xmlAccessDAO) throws AccessControlException {
223
		boolean exists = false;
224
		PreparedStatement pstmt = null;
225
		DBConnection conn = null;
226
		int serialNumber = -1;
227
		try {
228
			//check out DBConnection
229
			conn=DBConnectionPool.getDBConnection
230
                               ("AccessControlForSingleFiel.accessControlExists");
231
			serialNumber=conn.getCheckOutSerialNumber();
232
			pstmt = conn.prepareStatement(
233
				"SELECT * FROM xml_access " + 
234
				"WHERE guid = ? " +
235
				"AND principal_name = ? " +
236
				"AND permission = ? " +
237
				"AND perm_type = ? " +
238
				"AND perm_order = ? ");
239
     
240
			// Bind the values to the query
241
			pstmt.setString(1, _guid);
242
			pstmt.setString(2, xmlAccessDAO.getPrincipalName());
243
			pstmt.setLong(3, xmlAccessDAO.getPermission());
244
			pstmt.setString(4, xmlAccessDAO.getPermType());
245
			pstmt.setString(5, xmlAccessDAO.getPermOrder());
246
      
247
			pstmt.execute();
248
			ResultSet rs = pstmt.getResultSet();
249
			exists = rs.next();
250
      
251
		} catch (SQLException sqle){
252
			throw new AccessControlException("AccessControlForSingleFile.accessControlExists - SQL error when " +  
253
					"checking if access control exists: " + sqle.getMessage());
254
		} finally {
255
			try {
256
				if(pstmt != null) {
257
					pstmt.close();
258
				}
259
			} catch (SQLException sqle) {
260
				logMetacat.error("AccessControlForSingleFile.accessControlExists - Could not close " + 
261
						"prepared statement: " +sqle.getMessage());
262
			} finally {
263
				DBConnectionPool.returnDBConnection(conn, serialNumber);
264
			}
265
		}
266
    
267
		return exists;  
268
	}
269
  
270
	/**
271
	 * Get Access Control List information for document from db connetion. User
272
	 * or Group should have permissions for reading access control information
273
	 * for a document specified by
274
	 * 
275
	 * @param user
276
	 *            name of user connected to Metacat system
277
	 * @param groups
278
	 *            names of user's groups to which user belongs
279
	 */
280
	public String getACL(String user, String[] groups)
281
			throws AccessControlException {
282
		StringBuffer output = new StringBuffer();
283
		boolean hasPermission = false;
284

    
285
		try {   
286
			hasPermission = isOwned(user);
287
			if (!hasPermission) {
288
				// get the docid for this guid
289
				String docid = IdentifierManager.getInstance().getLocalId(_guid);
290
				PermissionController controller = new PermissionController(docid);
291
				hasPermission = 
292
					controller.hasPermission(user, groups, READSTRING);
293
			}
294

    
295
			// if the user has permissions, get the access dao list for this doc and return
296
			// it as a string.  Otherwise, get the string for an empty access dao list 
297
			// (which will return the access section with no allow or deny sections)
298
			if (hasPermission) {
299
				// Get a list of all access dao objects for this docid
300
				XMLAccessAccess xmlAccessAccess = new XMLAccessAccess();
301
				Vector<XMLAccessDAO> xmlAccessDAOList = xmlAccessAccess.getXMLAccessForDoc(_guid);
302
				output.append(getAccessString(xmlAccessDAOList));
303
			} else {
304
				output.append(getAccessString(new Vector<XMLAccessDAO>()));
305
			}
306

    
307
			return output.toString();
308

    
309
		} catch (SQLException sqle) {
310
			throw new AccessControlException("AccessControlForSingleFile.getACL() - SQL error when " + 
311
					"getting ACL: " + sqle.getMessage());
312
		} catch (AccessException ae) {
313
			throw new AccessControlException("AccessControlForSingleFile.getACL() - DB access error when " + 
314
					"getting ACL: " + ae.getMessage());
315
		} catch (McdbException mcdb) {
316
			throw new AccessControlException("AccessControlForSingleFile.getACL() - MCDB error when " + 
317
					"getting ACL: " + mcdb.getMessage());
318
		}
319
	}
320
	
321
	/**
322
	 * Get the access xml for all access on this docid
323
	 * 
324
	 * @return string representation of access
325
	 */
326
	public String getAccessString() throws AccessControlException {
327
		Vector<XMLAccessDAO> xmlAccessDAOList = null;
328
		
329
		try {
330
			// Get a list of all access dao objects for this docid
331
			XMLAccessAccess xmlAccessAccess = new XMLAccessAccess();
332
			xmlAccessDAOList = xmlAccessAccess.getXMLAccessForDoc(_guid);
333
		} catch (AccessException ae) {
334
				throw new AccessControlException("AccessControlForSingleFile.getAccessString() - DB access error when " + 
335
						"getting access string: " + ae.getMessage());
336
		} 
337
		
338
		return getAccessString(xmlAccessDAOList);
339
	}
340
	
341
	/**
342
	 * Put together an xml representation of the objects in a given access dao list
343
	 * @param xmlAccessDAOList list of xml access DAO objects
344
	 * @return string representation of access
345
	 */
346
	public String getAccessString(Vector<XMLAccessDAO> xmlAccessDAOList) throws AccessControlException {
347
			
348
		StringBuffer output = new StringBuffer();
349
		StringBuffer tmpOutput = new StringBuffer();
350
		StringBuffer allowOutput = new StringBuffer();
351
		StringBuffer denyOutput = new StringBuffer();
352
		
353
		String principal = null;
354
		int permission = -1;
355
		String permOrder = ALLOWFIRST;
356
		String permType = null;
357
		String accessfileid = null;
358
		String subtreeid = null;
359
		
360
		// We assume that all the records will have the same permission order, so we can just
361
		// grab the perm order from the first one.
362
		if (xmlAccessDAOList.size() > 0) {
363
			permOrder = xmlAccessDAOList.get(0).getPermOrder();
364
			accessfileid = xmlAccessDAOList.get(0).getAccessFileId();
365
			subtreeid = xmlAccessDAOList.get(0).getSubTreeId();
366
		}
367
		
368
		// get the docid for this guid
369
		String docid = _guid;
370
		try {
371
			docid = IdentifierManager.getInstance().getLocalId(_guid);
372
		} catch (McdbDocNotFoundException e) {
373
			logMetacat.warn("Could not lookup docid for guid, defaulting to guid: " + _guid, e);
374
		}
375

    
376
		output.append("<access authSystem=\"knb\" order=\"" + permOrder + "\" id=\"" + docid + "\" scope=\"document\"");
377
		if (accessfileid != null) {
378
			output.append(" accessfileid=\"" + accessfileid + "\"");
379
		}
380
		if (subtreeid != null) {
381
			output.append(" subtreeid=\"" + subtreeid + "\"");
382
		}
383
		
384
		output.append(">\n");
385
		
386
		for (XMLAccessDAO xmlAccessDAO : xmlAccessDAOList) {
387
			principal = xmlAccessDAO.getPrincipalName();
388
			permission = xmlAccessDAO.getPermission().intValue();
389
			//System.out.println("accessControlForSingleFile.getAccessString: permission is set to: " + permission);
390
			permType = xmlAccessDAO.getPermType();
391
			
392
			tmpOutput.append("    <" + permType + ">\n");
393
			tmpOutput.append("      <principal>" + principal + "</principal>\n");
394
	
395
			if ((permission & READ) == READ) {
396
				tmpOutput.append("      <permission>read</permission>\n");
397
			}
398
			if ((permission & WRITE) == WRITE) {
399
				tmpOutput.append("      <permission>write</permission>\n");
400
			}
401
			if ((permission & ALL) == ALL) {
402
				tmpOutput.append("      <permission>all</permission>\n");
403
			}
404
			if ((permission & CHMOD) == CHMOD) {
405
				tmpOutput.append("      <permission>chmod</permission>\n");
406
			}
407

    
408
			tmpOutput.append("    </" + permType + ">\n");
409
			
410
			if (permType.equals(ALLOW)) {
411
				allowOutput.append(tmpOutput);
412
			} else if (permType.equals(DENY)) {
413
				denyOutput.append(tmpOutput);
414
			}
415
			tmpOutput = new StringBuffer();
416
		}
417
		
418
		// This just orders the allow/deny sections based on the permOrder.  Not 
419
		// required, but convenient later when parsing the output.
420
		if (permOrder.equals(ALLOWFIRST)) {
421
			output.append(allowOutput);
422
			output.append(denyOutput);
423
		} else if (permOrder.equals(DENYFIRST)) {
424
			output.append(denyOutput);
425
			output.append(allowOutput);
426
		}
427
		
428
		output.append("</access>");
429
					
430
		return output.toString();
431
	}
432
	
433
	/**
434
	 * check if the docid represented in this class is owned by the user
435
	 * 
436
	 * @param user
437
	 *            the user credentials
438
	 * @return true if doc is owned by user, false otherwise
439
	 */
440
	private boolean isOwned(String user) throws SQLException {
441
		PreparedStatement pstmt = null;
442
		DBConnection conn = null;
443
		int serialNumber = -1;
444
		try {
445
			// check out DBConnection
446
			conn = DBConnectionPool.getDBConnection("AccessControlList.isOwned");
447
			serialNumber = conn.getCheckOutSerialNumber();
448
			String query = 
449
				"SELECT id.guid FROM xml_documents xd, identifier id "
450
				+ "WHERE xd.docid = id.docid " +
451
						"AND xd.rev = id.rev " +
452
						"AND id.guid = ? " + 
453
						"AND user_owner = ? ";
454
			pstmt = conn.prepareStatement(query );
455
			pstmt.setString(1, _guid);
456
			pstmt.setString(2, user);
457
			pstmt.execute();
458
			ResultSet rs = pstmt.getResultSet();
459
			boolean hasRow = rs.next();
460
			return hasRow;
461
		} finally {
462
			try {
463
				if (pstmt != null) {
464
					pstmt.close();
465
				}
466
			} finally {
467
				DBConnectionPool.returnDBConnection(conn, serialNumber);
468
			}
469
		}
470
	}
471
}
(2-2/9)