Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that implements replication for metacat
4
 *  Copyright: 2000 Regents of the University of California and the
5
 *             National Center for Ecological Analysis and Synthesis
6
 *    Authors: Chad Berkley
7
 *
8
 *   '$Author: daigle $'
9
 *     '$Date: 2009-03-25 13:41:15 -0800 (Wed, 25 Mar 2009) $'
10
 * '$Revision: 4861 $'
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
 */
26

    
27
package edu.ucsb.nceas.metacat.replication;
28

    
29
import java.io.ByteArrayInputStream;
30
import java.io.FileInputStream;
31
import java.io.FileNotFoundException;
32
import java.io.IOException;
33
import java.io.InputStream;
34
import java.io.OutputStream;
35
import java.io.PrintWriter;
36
import java.security.KeyStore;
37
import java.security.KeyStoreException;
38
import java.security.NoSuchAlgorithmException;
39
import java.security.cert.Certificate;
40
import java.security.cert.CertificateException;
41
import java.security.cert.X509Certificate;
42
import java.util.Enumeration;
43
import java.util.Hashtable;
44

    
45
import javax.servlet.ServletConfig;
46
import javax.servlet.ServletException;
47
import javax.servlet.http.HttpServlet;
48
import javax.servlet.http.HttpServletRequest;
49
import javax.servlet.http.HttpServletResponse;
50
import javax.servlet.http.HttpSession;
51

    
52
import org.apache.commons.io.IOUtils;
53
import org.apache.log4j.Logger;
54
import org.dataone.client.auth.CertificateManager;
55

    
56
import edu.ucsb.nceas.metacat.properties.PropertyService;
57
import edu.ucsb.nceas.metacat.service.ServiceService;
58
import edu.ucsb.nceas.metacat.service.SessionService;
59
import edu.ucsb.nceas.metacat.shared.MetacatUtilException;
60
import edu.ucsb.nceas.metacat.shared.ServiceException;
61
import edu.ucsb.nceas.metacat.util.AuthUtil;
62
import edu.ucsb.nceas.metacat.util.SessionData;
63
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
64

    
65
public class ReplicationServlet extends HttpServlet {
66

    
67
	private static final long serialVersionUID = -2898600143193513155L;
68

    
69
	private static Logger logReplication = Logger.getLogger("ReplicationLogging");
70
	private static Logger logMetacat = Logger.getLogger(ReplicationServlet.class);
71

    
72
	/**
73
	 * Initialize the servlet by creating appropriate database connections
74
	 */
75
	public void init(ServletConfig config) throws ServletException {
76

    
77
		try {
78
			// Register preliminary services
79
			ServiceService.registerService("ReplicationService", ReplicationService
80
					.getInstance());
81
			
82
		} catch (ServiceException se) {
83
			String errorMessage = "ReplicationServlet.init - Service problem while intializing Replication Servlet: "
84
					+ se.getMessage();
85
			logMetacat.error("ReplicationServlet.init - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
86
			logReplication.error(errorMessage);
87
			throw new ServletException(errorMessage);
88
		} 
89
	}
90

    
91
	public void destroy() {
92
		//ServiceService.
93
	}
94

    
95
	public void doGet(HttpServletRequest request, HttpServletResponse response)
96
			throws ServletException, IOException {
97
		// Process the data and send back the response
98
		handleGetOrPost(request, response);
99
	}
100

    
101
	public void doPost(HttpServletRequest request, HttpServletResponse response)
102
			throws ServletException, IOException {
103
		// Process the data and send back the response
104
		handleGetOrPost(request, response);
105
	}
106

    
107
	private void handleGetOrPost(HttpServletRequest request, HttpServletResponse response)
108
			throws ServletException, IOException {
109
		PrintWriter out = null;
110
		Hashtable<String, String[]> params = new Hashtable<String, String[]>();
111
		Enumeration<String> paramlist = request.getParameterNames();
112

    
113
		while (paramlist.hasMoreElements()) {
114
			String name = (String) paramlist.nextElement();
115
			String[] value = request.getParameterValues(name);
116
			params.put(name, value);
117
		}
118

    
119
		String action = "";
120
		if (!params.isEmpty() && params.get("action") != null) {
121
			action = ((String[]) params.get("action"))[0];
122
		}
123
		String server = null;
124

    
125
		try {
126
			// check if the server is included in the list of replicated servers
127
			if (!action.equals("servercontrol") && !action.equals("stop")
128
					&& !action.equals("start") && !action.equals("getall")) {
129

    
130
				// verify the client certificate on the request
131
				boolean isValid = false;
132
				String msg = "Client certificate is invalid";
133
				try {
134
					isValid = hasValidCertificate(request);
135
				} catch (Exception e) {
136
					msg = "Could not verify client certificate: " + e.getMessage();
137
					logMetacat.error(msg, e);
138
					logReplication.error(msg, e);
139
				}
140
				if (!isValid) {
141
					// send message to response
142
					out = response.getWriter();
143
					out.print("<error>");
144
					out.print(msg);
145
					out.print("</error>");
146
					out.close();
147
					return;
148
				}
149
				
150
				server = ((String[]) params.get("server"))[0];
151
				if (ReplicationService.getServerCodeForServerName(server) == 0) {
152
					logReplication.debug("ReplicationServlet.handleGetOrPost - Action \"" + action + "\" rejected for server: "
153
							+ server);
154
					return;
155
				} else {
156
					logReplication.debug("ReplicationServlet.handleGetOrPost - Action \"" + action + "\" accepted for server: "
157
							+ server);
158
				}
159
				
160
			} else {
161
				// start, stop, getall and servercontrol need to check if user is administor
162
				HttpSession sess = request.getSession(true);
163
				SessionData sessionData = null;
164
				String sess_id = "";
165
				String username = "";
166
				String[] groupnames = { "" };
167

    
168
				if (params.containsKey("sessionid")) {
169
					sess_id = ((String[]) params.get("sessionid"))[0];
170
					logReplication.info("ReplicationServlet.handleGetOrPost - in has sessionid " + sess_id);
171
					if (SessionService.getInstance().isSessionRegistered(sess_id)) {
172
						logReplication.info("ReplicationServlet.handleGetOrPost - find the id " + sess_id + " in hash table");
173
						sessionData = SessionService.getInstance().getRegisteredSession(sess_id);
174
					}
175
				}
176
				if (sessionData == null) {
177
					sessionData = new SessionData(sess.getId(), 
178
							(String) sess.getAttribute("username"), 
179
							(String[]) sess.getAttribute("groups"),
180
							(String) sess.getAttribute("password"), 
181
							(String) sess.getAttribute("name"));
182
				}
183

    
184
				username = sessionData.getUserName();
185
				logReplication.warn("ReplicationServlet.handleGetOrPost - The user name from session is: " + username);
186
				groupnames = sessionData.getGroupNames();
187
				if (!AuthUtil.isAdministrator(username, groupnames)) {
188
					out = response.getWriter();
189
					out.print("<error>");
190
					out.print("The user \"" + username
191
							+ "\" is not authorized for this action.");
192
					out.print("</error>");
193
					out.close();
194
					logReplication.warn("ReplicationServlet.handleGetOrPost - The user \"" + username
195
							+ "\" is not authorized for this action: " + action);
196
					return;
197
				}
198

    
199
			}// else
200

    
201
			if (action.equals("readdata")) {
202
				OutputStream outStream = response.getOutputStream();
203
				//to get the data file.
204
				ReplicationService.handleGetDataFileRequest(outStream, params, response);
205
				outStream.close();
206
			} else if (action.equals("forcereplicatedatafile")) {
207
				//read a specific docid from remote host, and store it into local host
208
				ReplicationService.handleForceReplicateDataFileRequest(params, request);
209
			} else if (action.equals("stop")) {
210
				// stop the replication server
211
				ReplicationService.getInstance().stopReplication();
212
				out = response.getWriter();
213
				out.println("Replication Handler Stopped");
214
			} else if (action.equals("start")) {
215
				ReplicationService.getInstance().startReplication(params);
216
				out = response.getWriter();
217
				out.println("Replication Handler Started");
218
			} else if (action.equals("getall")) {
219
				ReplicationService.getInstance().runOnce();
220
				response.setContentType("text/html");
221
				out = response.getWriter();
222
				out.println("<html><body>\"Get All\" Done</body></html>");
223
			} else if (action.equals("forcereplicate")) {
224
				// read a specific docid from remote host, and store it into
225
				// local host
226
				ReplicationService.handleForceReplicateRequest(params, response,
227
						request);
228
			} else if (action.equals("forcereplicatesystemmetadata")) {
229
				ReplicationService.handleForceReplicateSystemMetadataRequest(params, response,
230
						request);
231
			} else if (action.equals("forcereplicatedelete")) {
232
				// read a specific docid from remote host, and store it into
233
				// local host
234
				ReplicationService.handleForceReplicateDeleteRequest(params,
235
						response, request);
236
			} else if (action.equals("update")) {
237
				// request an update list from the server
238
				ReplicationService.handleUpdateRequest(params, response);
239
			} else if (action.equals("read")) {
240
				// request a specific document from the server
241
				// note that this could be replaced by a call to metacatServlet
242
				// handleGetDocumentAction().
243
				ReplicationService.handleGetDocumentRequest(params, response);				
244
			} else if (action.equals("getlock")) {
245
				ReplicationService.handleGetLockRequest(params, response);
246
			} else if (action.equals("getdocumentinfo")) {
247
				ReplicationService.handleGetDocumentInfoRequest(params, response);
248
			} else if (action.equals("getsystemmetadata")) {
249
				ReplicationService.handleGetSystemMetadataRequest(params, response);
250
			} else if (action.equals("gettime")) {
251
				ReplicationService.handleGetTimeRequest(params, response);
252
			} else if (action.equals("getcatalog")) {
253
				ReplicationService.handleGetCatalogRequest(params, response, true);
254
			} else if (action.equals("servercontrol")) {
255
				ReplicationService.handleServerControlRequest(params, response);
256
			} else if (action.equals("test")) {
257
				response.setContentType("text/html");
258
				out = response.getWriter();
259
				out.println("<html><body>Test successfully</body></html>");
260
			}
261

    
262
		} catch (ServiceException e) {
263
			logMetacat.error("ReplicationServlet.handleGetOrPost - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
264
			logReplication.error("ReplicationServlet.handleGetOrPost - Error in ReplicationServlet.handleGetOrPost: " + e.getMessage());
265
		} catch (MetacatUtilException mue) {
266
			logMetacat.error("ReplicationServlet.handleGetOrPost - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
267
			logReplication.error("ReplicationServlet.handleGetOrPost - Metacat utility error in ReplicationServlet.handleGetOrPost: "
268
							+ mue.getMessage());
269
		} finally {
270
			if (out != null) {
271
				out.close();
272
			}
273
		}
274
	}
275

    
276
	private boolean hasValidCertificate(HttpServletRequest request) throws KeyStoreException, PropertyNotFoundException, NoSuchAlgorithmException, CertificateException, IOException {
277
		// get the certificate from the request
278
		X509Certificate certificate = CertificateManager.getInstance().getCertificate(request);
279
		if (certificate != null) {
280
			String givenSubject = CertificateManager.getInstance().getSubjectDN(certificate);
281
			logMetacat.debug("Given certificate subject: " + givenSubject);
282

    
283
			// load the keystore
284
			KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
285
			InputStream is = new FileInputStream(PropertyService.getProperty("replication.keystore.file"));
286
			String password = PropertyService.getProperty("replication.keystore.password");
287
			keyStore.load(is, password.toCharArray());
288
			
289
			// this is expensive
290
			Enumeration<String> aliases = keyStore.aliases();
291
			while (aliases.hasMoreElements()) {
292
				// check that it contains our client's entry
293
				String alias = aliases.nextElement();
294
				logMetacat.debug("checking keyStore alias: " + alias);
295
				Certificate entryCertificate = keyStore.getCertificate(alias);
296
				if (entryCertificate instanceof X509Certificate) {
297
					// check the subject matches
298
					String entrySubject = CertificateManager.getInstance().getSubjectDN((X509Certificate) entryCertificate);
299
					logMetacat.debug("Entry certificate subject: " + entrySubject);
300
					if (entrySubject.equals(givenSubject)) {
301
						try {
302
							certificate.verify(entryCertificate.getPublicKey());
303
						} catch (Exception e) {
304
							logMetacat.warn("Certificate not verifiable: " + e.getMessage(), e);
305
							continue;
306
						}
307
						// if we pass verification, we did it!
308
						return true;
309
					}
310
				}	
311
			}
312
		}
313
 		return false;
314
	}
315
}
(8-8/8)