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: 2011-12-13 15:30:58 -0800 (Tue, 13 Dec 2011) $'
11
 * '$Revision: 6779 $'
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
				PermissionController controller = new PermissionController(_guid);
289
				hasPermission = 
290
					controller.hasPermission(user, groups, READSTRING);
291
			}
292

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

    
305
			return output.toString();
306

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

    
366
		output.append("<access authSystem=\"knb\" order=\"" + permOrder + "\" id=\"" + _guid + "\" scope=\"document\"");
367
		if (accessfileid != null) {
368
			output.append(" accessfileid=\"" + accessfileid + "\"");
369
		}
370
		if (subtreeid != null) {
371
			output.append(" subtreeid=\"" + subtreeid + "\"");
372
		}
373
		
374
		output.append(">\n");
375
		
376
		for (XMLAccessDAO xmlAccessDAO : xmlAccessDAOList) {
377
			principal = xmlAccessDAO.getPrincipalName();
378
			permission = xmlAccessDAO.getPermission().intValue();
379
			//System.out.println("accessControlForSingleFile.getAccessString: permission is set to: " + permission);
380
			permType = xmlAccessDAO.getPermType();
381
			
382
			tmpOutput.append("    <" + permType + ">\n");
383
			tmpOutput.append("      <principal>" + principal + "</principal>\n");
384
	
385
			if ((permission & READ) == READ) {
386
				tmpOutput.append("      <permission>read</permission>\n");
387
			}
388
			if ((permission & WRITE) == WRITE) {
389
				tmpOutput.append("      <permission>write</permission>\n");
390
			}
391
			if ((permission & ALL) == ALL) {
392
				tmpOutput.append("      <permission>all</permission>\n");
393
			}
394
			if ((permission & CHMOD) == CHMOD) {
395
				tmpOutput.append("      <permission>chmod</permission>\n");
396
			}
397

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