/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cocoon.acting; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.parameters.Parameters; import org.apache.avalon.framework.thread.ThreadSafe; import org.apache.cocoon.Constants; import org.apache.cocoon.environment.ObjectModelHelper; import org.apache.cocoon.environment.Redirector; import org.apache.cocoon.environment.Request; import org.apache.cocoon.environment.Session; import org.apache.cocoon.environment.SourceResolver; import org.apache.commons.lang.BooleanUtils; import org.apache.xpath.XPathAPI; import org.apache.xpath.objects.XObject; import org.w3c.dom.Node; import org.xmldb.api.DatabaseManager; import org.xmldb.api.base.Collection; import org.xmldb.api.base.Database; import org.xmldb.api.base.ResourceIterator; import org.xmldb.api.base.ResourceSet; import org.xmldb.api.base.XMLDBException; import org.xmldb.api.modules.XMLResource; import org.xmldb.api.modules.XPathQueryService; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * This action is used to authenticate user by comparing several request * fields (username, password) with the values in a DBXML compliant database. * The description of the process is given via external xml description file * simiar to the one used for all actions derived from AbstractDatabaseAction. * * <pre> * <root> * <connection> * <driver>org.apache.xindice.client.xmldb.DatabaseImpl</driver> * <base>xmldb:xindice:///db/beta</base> * </connection> * * <root name="users"> * <select element="username" request-param="username" to-session="username"/> * <select element="password" request-param="password" nullable="yes"/> * * <select element="role" to-session="role" type="string"/> * <select element="skin" to-session="skin" type="string"/> * </root> * </root> * </pre> * * The values specified via "request-param" describe the name of HTTP request * parameter, "element" indicates matching document node, "nullable" means * that request-param which is null or empty will not be included in the WHERE * clause. This way you can enable accounts with empty passwords, etc. * "to-session" attribute indicates under which name the value obtained from * database should be stored in the session. Of course new session is created * when authorization is successfull. The "type" attribute can be either * string, long or double and alters the type of object stored in session. * Additionally all parameters that are * propagated to the session are made available to the sitemap via {name} * expression. * * If there is no need to touch the session object, providing just one-time * verification, you can specify action parameter "create-session" to "no" or * "false". No values are then propagated to the sesion and session object is * not verified. * * @author <a href="mailto:czoffoli@littlepenguin.org">Christian Zoffoli</a> * @author <a href="mailto:Martin.Man@seznam.cz">Martin Man</a> * @since 2002/02/03 * @version $Id$ * * based on DatabaseAuthenticatorAction created by Martin Man <Martin.Man@seznam.cz> */ public class DbXMLAuthenticatorAction extends AbstractDatabaseAction implements ThreadSafe { /** * Main invocation routine. */ public Map act (Redirector redirector, SourceResolver resolver, Map objectModel, String src, Parameters parameters) throws Exception { ResourceSet rs = null; // read global parameter settings boolean reloadable = Constants.DESCRIPTOR_RELOADABLE_DEFAULT; if (this.settings.containsKey("reloadable")) { reloadable = Boolean.valueOf((String) this.settings.get("reloadable")).booleanValue(); } // read local settings try { Configuration conf = this.getConfiguration ( parameters.getParameter ("descriptor", (String) this.settings.get("descriptor")), resolver, parameters.getParameterAsBoolean("reloadable", reloadable)); boolean cs = true; String create_session = parameters.getParameter ("create-session", (String) this.settings.get("create-session")); if (create_session != null && ("no".equals (create_session.trim ()) || "false".equals (create_session.trim ()))) { cs = false; } Request req = ObjectModelHelper.getRequest(objectModel); /* check request validity */ if (req == null) { getLogger ().debug ("DBXMLAUTH: no request object"); return null; } rs = this.Authenticate( conf, req ); if (rs != null ) { getLogger ().debug ("DBXMLAUTH: authorized successfully"); Session session = null; if (cs) { session = req.getSession (false); if (session != null) session.invalidate (); session = req.getSession (true); if (session == null) return null; getLogger ().debug ("DBXMLAUTH: session created"); } else { getLogger ().debug ("DBXMLAUTH: leaving session untouched"); } HashMap actionMap = this.propagateParameters (conf, rs, session); return Collections.unmodifiableMap (actionMap); } else { //getLogger ().debug ("DBXMLAUTH: error ResourceSet is null"); } req.setAttribute("message", "The username or password were incorrect, please check your CAPS LOCK key and try again."); getLogger ().debug ("DBXMLAUTH: no results for query"); } catch (Exception e) { getLogger().debug ("exception: ", e); return null; } return null; } private String getAuthQuery ( Configuration conf, Request req ) { StringBuffer queryBuffer = new StringBuffer ("//"); StringBuffer queryBufferEnd = new StringBuffer (""); String dbcol, request_param, request_value, nullstr; boolean nullable = false; Configuration table = conf.getChild ("root"); Configuration[] select = table.getChildren ("select"); try { queryBuffer.append (table.getAttribute ("name")); for (int i = 0; i < select.length; i ++) { dbcol = "[" + select[i].getAttribute ("element"); try { request_param = select[i].getAttribute ("request-param"); if (request_param == null || request_param.trim().equals ("")) { continue; } } catch (Exception e) { continue; } try { nullstr = select[i].getAttribute ("nullable"); if (nullstr != null) nullstr = nullstr.trim (); if (BooleanUtils.toBoolean(nullstr)) { nullable = true; } } catch (Exception e1) { } /* if there is a request parameter name, * but not the value, we exit immediately do * that authorization fails authomatically */ request_value = req.getParameter (request_param); if (request_value == null || request_value.trim().equals ("")) { // value is null if (!nullable) { getLogger ().debug ("DBXMLAUTH: request-param " + request_param + " does not exist"); return null; } } else { queryBufferEnd.append (dbcol).append("='").append(request_value).append("']"); } } if (!queryBufferEnd.toString ().trim ().equals ("")) queryBuffer.append (queryBufferEnd); return queryBuffer.toString (); } catch (Exception e) { getLogger ().debug ("DBXMLAUTH: got exception: " + e); return null; } } private ResourceSet Authenticate( Configuration conf, Request req) throws Exception, XMLDBException { ResourceSet rs = null; String query = this.getAuthQuery (conf, req); if (query == null) { getLogger ().debug ("DBXMLAUTH: have not got query"); req.setAttribute("message", "The authenticator is misconfigured"); return null; } getLogger ().debug ("DBXMLAUTH: query is: " + query); Collection col = CreateConnection(conf); if (col != null) { if (col.isOpen()) { try { XPathQueryService service = (XPathQueryService) col.getService("XPathQueryService", "1.0"); rs = service.query(query); ResourceIterator results = rs.getIterator(); if (results.hasMoreResources() == false) { getLogger ().debug ("DBXMLAUTH: auth failed"); return null; } else { getLogger ().debug ("DBXMLAUTH: auth OK"); return rs; } } catch (XMLDBException e) { getLogger ().debug ("DBXMLAUTH: got exception: " + e); return null; } finally { // close col try { col.close(); } catch (Exception e) { /* ignore */ } getLogger ().debug ("DBXMLAUTH: collection closed"); } } else { getLogger ().debug ("DBXMLAUTH: error: collection closed !!"); } } else { getLogger ().debug ("DBXMLAUTH: couldn't open a connection with DB"); } return null; } private Collection CreateConnection( Configuration conf ) throws Exception, XMLDBException { Collection col = null; Configuration conn = conf.getChild ("connection"); try { Class c = Class.forName( conn.getChild("driver").getValue() ); Database database = (Database) c.newInstance(); DatabaseManager.registerDatabase(database); col = DatabaseManager.getCollection( conn.getChild("base").getValue() ); } catch (XMLDBException e) { getLogger ().debug ("DBXMLAUTH: Exception occured " + e.errorCode); } return col; } private HashMap propagateParameters (Configuration conf, ResourceSet resultSet, Session session) { Configuration table = conf.getChild ("root"); Configuration[] select = table.getChildren ("select"); String session_param, type; HashMap map = new HashMap(); XObject xo; Node originalnode = null; try { ResourceIterator results = resultSet.getIterator(); // Create an XObject to be used in Xpath query xo = new XObject(); // Retrieve the next node XMLResource resource = (XMLResource) results.nextResource(); originalnode = resource.getContentAsDOM(); } catch (Exception e) { getLogger ().debug ("DBXMLAUTH: error creating XObject "); } try { for (int i = 0; i < select.length; i ++) { try { session_param = select[i].getAttribute ("to-session"); if (session_param != null && !session_param.trim().equals ("")) { String s = ""; try { // Use Xalan xpath parser to extract data xo = XPathAPI.eval(originalnode, "/" + table.getAttribute ("name") + "/" + select[i].getAttribute ("element") ); s = xo.toString(); } catch (Exception e) { } /* propagate to session */ try { type = select[i].getAttribute ("type"); } catch (Exception e) { type = null; } if (type == null || "".equals (type.trim ())) { type = "string"; } Object o = null; if ("string".equals (type)) { o = s; } else if ("long".equals (type)) { Long l = Long.decode (s); o = l; } else if ("double".equals (type)) { Double d = Double.valueOf (s); o = d; } if (session != null) { session.setAttribute (session_param, o); getLogger ().debug ("DBXMLAUTH: propagating param " + session_param + "=" + s); } map.put (session_param, o); } } catch (Exception e) { } } return map; } catch (Exception e) { getLogger().debug("exception: ", e); } return null; } }