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-12-12 14:38:02 -0800 (Wed, 12 Dec 2012) $'
11
 * '$Revision: 7475 $'
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.IdentifierManager;
47
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
48
import edu.ucsb.nceas.metacat.McdbException;
49
import edu.ucsb.nceas.metacat.PermissionController;
50
import edu.ucsb.nceas.metacat.database.DBConnection;
51
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
52
import edu.ucsb.nceas.metacat.properties.PropertyService;
53
import edu.ucsb.nceas.metacat.shared.AccessException;
54
import edu.ucsb.nceas.metacat.util.DocumentUtil;
55
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
56
import edu.ucsb.nceas.utilities.access.AccessControlInterface;
57
import edu.ucsb.nceas.utilities.access.DocInfoHandler;
58
import edu.ucsb.nceas.utilities.access.XMLAccessDAO;
59

    
60

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

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

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

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

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

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

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

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

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

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

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

    
309
			return output.toString();
310

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

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

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