001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.acting;
018:
019: import org.apache.avalon.framework.configuration.Configuration;
020: import org.apache.avalon.framework.parameters.Parameters;
021: import org.apache.avalon.framework.thread.ThreadSafe;
022:
023: import org.apache.cocoon.Constants;
024: import org.apache.cocoon.environment.ObjectModelHelper;
025: import org.apache.cocoon.environment.Redirector;
026: import org.apache.cocoon.environment.Request;
027: import org.apache.cocoon.environment.Session;
028: import org.apache.cocoon.environment.SourceResolver;
029: import org.apache.commons.lang.BooleanUtils;
030:
031: import org.apache.xpath.XPathAPI;
032: import org.apache.xpath.objects.XObject;
033: import org.w3c.dom.Node;
034: import org.xmldb.api.DatabaseManager;
035: import org.xmldb.api.base.Collection;
036: import org.xmldb.api.base.Database;
037: import org.xmldb.api.base.ResourceIterator;
038: import org.xmldb.api.base.ResourceSet;
039: import org.xmldb.api.base.XMLDBException;
040: import org.xmldb.api.modules.XMLResource;
041: import org.xmldb.api.modules.XPathQueryService;
042:
043: import java.util.Collections;
044: import java.util.HashMap;
045: import java.util.Map;
046:
047: /**
048: * This action is used to authenticate user by comparing several request
049: * fields (username, password) with the values in a DBXML compliant database.
050: * The description of the process is given via external xml description file
051: * simiar to the one used for all actions derived from AbstractDatabaseAction.
052: *
053: * <pre>
054: * <root>
055: * <connection>
056: * <driver>org.apache.xindice.client.xmldb.DatabaseImpl</driver>
057: * <base>xmldb:xindice:///db/beta</base>
058: * </connection>
059: *
060: * <root name="users">
061: * <select element="username" request-param="username" to-session="username"/>
062: * <select element="password" request-param="password" nullable="yes"/>
063: *
064: * <select element="role" to-session="role" type="string"/>
065: * <select element="skin" to-session="skin" type="string"/>
066: * </root>
067: * </root>
068: * </pre>
069: *
070: * The values specified via "request-param" describe the name of HTTP request
071: * parameter, "element" indicates matching document node, "nullable" means
072: * that request-param which is null or empty will not be included in the WHERE
073: * clause. This way you can enable accounts with empty passwords, etc.
074: * "to-session" attribute indicates under which name the value obtained from
075: * database should be stored in the session. Of course new session is created
076: * when authorization is successfull. The "type" attribute can be either
077: * string, long or double and alters the type of object stored in session.
078: * Additionally all parameters that are
079: * propagated to the session are made available to the sitemap via {name}
080: * expression.
081: *
082: * If there is no need to touch the session object, providing just one-time
083: * verification, you can specify action parameter "create-session" to "no" or
084: * "false". No values are then propagated to the sesion and session object is
085: * not verified.
086: *
087: * @author <a href="mailto:czoffoli@littlepenguin.org">Christian Zoffoli</a>
088: * @author <a href="mailto:Martin.Man@seznam.cz">Martin Man</a>
089: * @since 2002/02/03
090: * @version $Id: DbXMLAuthenticatorAction.java 433543 2006-08-22 06:22:54Z crossley $
091: *
092: * based on DatabaseAuthenticatorAction created by Martin Man <Martin.Man@seznam.cz>
093: */
094: public class DbXMLAuthenticatorAction extends AbstractDatabaseAction
095: implements ThreadSafe {
096:
097: /**
098: * Main invocation routine.
099: */
100: public Map act(Redirector redirector, SourceResolver resolver,
101: Map objectModel, String src, Parameters parameters)
102: throws Exception {
103:
104: ResourceSet rs = null;
105:
106: // read global parameter settings
107: boolean reloadable = Constants.DESCRIPTOR_RELOADABLE_DEFAULT;
108:
109: if (this .settings.containsKey("reloadable")) {
110: reloadable = Boolean.valueOf(
111: (String) this .settings.get("reloadable"))
112: .booleanValue();
113: }
114:
115: // read local settings
116: try {
117: Configuration conf = this .getConfiguration(parameters
118: .getParameter("descriptor", (String) this .settings
119: .get("descriptor")), resolver, parameters
120: .getParameterAsBoolean("reloadable", reloadable));
121:
122: boolean cs = true;
123: String create_session = parameters.getParameter(
124: "create-session", (String) this .settings
125: .get("create-session"));
126:
127: if (create_session != null
128: && ("no".equals(create_session.trim()) || "false"
129: .equals(create_session.trim()))) {
130: cs = false;
131: }
132:
133: Request req = ObjectModelHelper.getRequest(objectModel);
134:
135: /* check request validity */
136: if (req == null) {
137: getLogger().debug("DBXMLAUTH: no request object");
138: return null;
139: }
140:
141: rs = this .Authenticate(conf, req);
142:
143: if (rs != null) {
144: getLogger().debug("DBXMLAUTH: authorized successfully");
145: Session session = null;
146:
147: if (cs) {
148: session = req.getSession(false);
149: if (session != null)
150: session.invalidate();
151: session = req.getSession(true);
152: if (session == null)
153: return null;
154: getLogger().debug("DBXMLAUTH: session created");
155: } else {
156: getLogger().debug(
157: "DBXMLAUTH: leaving session untouched");
158: }
159:
160: HashMap actionMap = this .propagateParameters(conf, rs,
161: session);
162: return Collections.unmodifiableMap(actionMap);
163: } else {
164: //getLogger ().debug ("DBXMLAUTH: error ResourceSet is null");
165: }
166:
167: req
168: .setAttribute(
169: "message",
170: "The username or password were incorrect, please check your CAPS LOCK key and try again.");
171: getLogger().debug("DBXMLAUTH: no results for query");
172:
173: } catch (Exception e) {
174:
175: getLogger().debug("exception: ", e);
176: return null;
177: }
178:
179: return null;
180: }
181:
182: private String getAuthQuery(Configuration conf, Request req) {
183:
184: StringBuffer queryBuffer = new StringBuffer("//");
185: StringBuffer queryBufferEnd = new StringBuffer("");
186:
187: String dbcol, request_param, request_value, nullstr;
188: boolean nullable = false;
189:
190: Configuration table = conf.getChild("root");
191: Configuration[] select = table.getChildren("select");
192:
193: try {
194:
195: queryBuffer.append(table.getAttribute("name"));
196:
197: for (int i = 0; i < select.length; i++) {
198:
199: dbcol = "[" + select[i].getAttribute("element");
200:
201: try {
202: request_param = select[i]
203: .getAttribute("request-param");
204: if (request_param == null
205: || request_param.trim().equals("")) {
206: continue;
207: }
208: } catch (Exception e) {
209: continue;
210: }
211:
212: try {
213: nullstr = select[i].getAttribute("nullable");
214:
215: if (nullstr != null)
216: nullstr = nullstr.trim();
217:
218: if (BooleanUtils.toBoolean(nullstr)) {
219: nullable = true;
220: }
221:
222: } catch (Exception e1) {
223: }
224:
225: /* if there is a request parameter name,
226: * but not the value, we exit immediately do
227: * that authorization fails authomatically */
228: request_value = req.getParameter(request_param);
229:
230: if (request_value == null
231: || request_value.trim().equals("")) {
232: // value is null
233: if (!nullable) {
234: getLogger().debug(
235: "DBXMLAUTH: request-param "
236: + request_param
237: + " does not exist");
238: return null;
239: }
240: } else {
241: queryBufferEnd.append(dbcol).append("='").append(
242: request_value).append("']");
243: }
244: }
245:
246: if (!queryBufferEnd.toString().trim().equals(""))
247: queryBuffer.append(queryBufferEnd);
248:
249: return queryBuffer.toString();
250: } catch (Exception e) {
251: getLogger().debug("DBXMLAUTH: got exception: " + e);
252: return null;
253: }
254: }
255:
256: private ResourceSet Authenticate(Configuration conf, Request req)
257: throws Exception, XMLDBException {
258:
259: ResourceSet rs = null;
260:
261: String query = this .getAuthQuery(conf, req);
262: if (query == null) {
263: getLogger().debug("DBXMLAUTH: have not got query");
264: req.setAttribute("message",
265: "The authenticator is misconfigured");
266: return null;
267: }
268: getLogger().debug("DBXMLAUTH: query is: " + query);
269:
270: Collection col = CreateConnection(conf);
271:
272: if (col != null) {
273: if (col.isOpen()) {
274: try {
275: XPathQueryService service = (XPathQueryService) col
276: .getService("XPathQueryService", "1.0");
277:
278: rs = service.query(query);
279: ResourceIterator results = rs.getIterator();
280:
281: if (results.hasMoreResources() == false) {
282: getLogger().debug("DBXMLAUTH: auth failed");
283: return null;
284: } else {
285: getLogger().debug("DBXMLAUTH: auth OK");
286: return rs;
287: }
288: } catch (XMLDBException e) {
289: getLogger().debug("DBXMLAUTH: got exception: " + e);
290: return null;
291: } finally {
292: // close col
293: try {
294: col.close();
295: } catch (Exception e) { /* ignore */
296: }
297: getLogger().debug("DBXMLAUTH: collection closed");
298:
299: }
300:
301: } else {
302: getLogger().debug(
303: "DBXMLAUTH: error: collection closed !!");
304: }
305:
306: } else {
307: getLogger().debug(
308: "DBXMLAUTH: couldn't open a connection with DB");
309:
310: }
311:
312: return null;
313: }
314:
315: private Collection CreateConnection(Configuration conf)
316: throws Exception, XMLDBException {
317:
318: Collection col = null;
319:
320: Configuration conn = conf.getChild("connection");
321:
322: try {
323:
324: Class c = Class.forName(conn.getChild("driver").getValue());
325:
326: Database database = (Database) c.newInstance();
327: DatabaseManager.registerDatabase(database);
328:
329: col = DatabaseManager.getCollection(conn.getChild("base")
330: .getValue());
331:
332: } catch (XMLDBException e) {
333: getLogger().debug(
334: "DBXMLAUTH: Exception occured " + e.errorCode);
335: }
336:
337: return col;
338: }
339:
340: private HashMap propagateParameters(Configuration conf,
341: ResourceSet resultSet, Session session) {
342:
343: Configuration table = conf.getChild("root");
344: Configuration[] select = table.getChildren("select");
345: String session_param, type;
346: HashMap map = new HashMap();
347:
348: XObject xo;
349: Node originalnode = null;
350:
351: try {
352:
353: ResourceIterator results = resultSet.getIterator();
354:
355: // Create an XObject to be used in Xpath query
356: xo = new XObject();
357:
358: // Retrieve the next node
359: XMLResource resource = (XMLResource) results.nextResource();
360:
361: originalnode = resource.getContentAsDOM();
362:
363: } catch (Exception e) {
364: getLogger().debug("DBXMLAUTH: error creating XObject ");
365: }
366:
367: try {
368: for (int i = 0; i < select.length; i++) {
369: try {
370: session_param = select[i]
371: .getAttribute("to-session");
372: if (session_param != null
373: && !session_param.trim().equals("")) {
374:
375: String s = "";
376:
377: try {
378: // Use Xalan xpath parser to extract data
379: xo = XPathAPI
380: .eval(
381: originalnode,
382: "/"
383: + table
384: .getAttribute("name")
385: + "/"
386: + select[i]
387: .getAttribute("element"));
388: s = xo.toString();
389: } catch (Exception e) {
390: }
391:
392: /* propagate to session */
393: try {
394: type = select[i].getAttribute("type");
395: } catch (Exception e) {
396: type = null;
397: }
398:
399: if (type == null || "".equals(type.trim())) {
400: type = "string";
401: }
402: Object o = null;
403:
404: if ("string".equals(type)) {
405: o = s;
406: } else if ("long".equals(type)) {
407: Long l = Long.decode(s);
408: o = l;
409: } else if ("double".equals(type)) {
410: Double d = Double.valueOf(s);
411: o = d;
412: }
413:
414: if (session != null) {
415: session.setAttribute(session_param, o);
416: getLogger().debug(
417: "DBXMLAUTH: propagating param "
418: + session_param + "=" + s);
419: }
420: map.put(session_param, o);
421: }
422: } catch (Exception e) {
423: }
424: }
425: return map;
426: } catch (Exception e) {
427: getLogger().debug("exception: ", e);
428: }
429: return null;
430: }
431: }
|