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.excalibur.datasource.DataSourceComponent;
020: import org.apache.avalon.framework.configuration.Configuration;
021: import org.apache.avalon.framework.parameters.Parameters;
022: import org.apache.avalon.framework.thread.ThreadSafe;
023: import org.apache.cocoon.Constants;
024: import org.apache.cocoon.environment.Cookie;
025: import org.apache.cocoon.environment.ObjectModelHelper;
026: import org.apache.cocoon.environment.Redirector;
027: import org.apache.cocoon.environment.Request;
028: import org.apache.cocoon.environment.Session;
029: import org.apache.cocoon.environment.SourceResolver;
030: import org.apache.commons.lang.BooleanUtils;
031: import org.apache.commons.lang.StringUtils;
032:
033: import java.sql.Connection;
034: import java.sql.ResultSet;
035: import java.sql.Statement;
036: import java.util.Collections;
037: import java.util.HashMap;
038: import java.util.Map;
039:
040: /**
041: * This action is used to authenticate user by comparing several cookie values
042: * (username, password) with the values in database. The description of the
043: * process is given via external xml description file simiar to the one used
044: * for all actions derived from AbstractDatabaseAction. <pre>
045: * <root>
046: * <connection>personnel</connection>
047: * <table name="users_table>
048: * <select dbcol="username" cookie-name="username"
049: * to-session="username"/>
050: * <select dbcol="password" cookie-name="password"
051: * nullable="yes"/>
052: * <select dbcol="role" to-session="role" type="string"/>
053: * <select dbcol="skin" to-session="skin" type="string"/>
054: * </table>
055: * </root>
056: * </pre> The values specified via "cookie-name" describe the name of the
057: * cookie, "dbcol" indicates matching database column, "nullable" means that
058: * cookie-name which is null or empty will not be included in the WHERE clause.
059: * This way you can enable accounts with empty passwords, etc. "to-session"
060: * attribute indicates under which name the value obtained from database should
061: * be stored in the session. Of course new session is created when
062: * authorization is successfull. The "type" attribute can be either string,
063: * long or double and alters the type of object stored in session. Additionally
064: * all parameters that are propagated to the session are made available to the
065: * sitemap via {name} expression. If there is no need to touch the session
066: * object, providing just one-time verification, you can specify action
067: * parameter "create-session" to "no" or "false". No values are then propagated
068: * to the sesion and session object is not verified. If you want to append
069: * attributes to the session without creating a new one, specify action
070: * parameter "append-session" to "yes" or "true".
071: *
072: * @author <a href="mailto:paolo@arsenio.net">Paolo Scaffardi</a>
073: * @version CVS $Id: DatabaseCookieAuthenticatorAction.java 433543 2006-08-22 06:22:54Z crossley $
074: */
075: public class DatabaseCookieAuthenticatorAction extends
076: AbstractDatabaseAction implements ThreadSafe {
077:
078: /**
079: * Main invocation routine.
080: *
081: * @param redirector Description of Parameter
082: * @param resolver Description of Parameter
083: * @param objectModel Description of Parameter
084: * @param src Description of Parameter
085: * @param parameters Description of Parameter
086: * @return Description of the Returned Value
087: * @exception Exception Description of Exception
088: */
089: public Map act(Redirector redirector, SourceResolver resolver,
090: Map objectModel, String src, Parameters parameters)
091: throws Exception {
092: DataSourceComponent datasource = null;
093: Connection conn = null;
094: Statement st = null;
095: ResultSet rs = null;
096:
097: // read global parameter settings
098: boolean reloadable = Constants.DESCRIPTOR_RELOADABLE_DEFAULT;
099:
100: if (this .settings.containsKey("reloadable")) {
101: reloadable = Boolean.valueOf(
102: (String) this .settings.get("reloadable"))
103: .booleanValue();
104: }
105:
106: // read local settings
107: try {
108: Configuration conf = this .getConfiguration(parameters
109: .getParameter("descriptor", (String) this .settings
110: .get("descriptor")), resolver, parameters
111: .getParameterAsBoolean("reloadable", reloadable));
112: String create_session = parameters.getParameter(
113: "create-session", (String) this .settings
114: .get("create-session"));
115: String append_session = parameters.getParameter(
116: "append-session", (String) this .settings
117: .get("append-session"));
118: boolean cs = true;
119: if (create_session != null) {
120: cs = BooleanUtils.toBoolean(create_session.trim());
121: }
122: boolean as = BooleanUtils.toBoolean(append_session.trim());
123:
124: datasource = this .getDataSource(conf);
125: conn = datasource.getConnection();
126: Request req = ObjectModelHelper.getRequest(objectModel);
127:
128: /*
129: * check request validity
130: */
131: if (req == null) {
132: if (getLogger().isDebugEnabled()) {
133: getLogger()
134: .debug("DBCOOKIEAUTH: no request object");
135: }
136: return null;
137: }
138:
139: String query = this .getAuthQuery(objectModel, conf, req);
140: if (query == null) {
141: if (getLogger().isDebugEnabled()) {
142: getLogger().debug(
143: "DBCOOKIEAUTH: have not got query");
144: }
145: req.setAttribute("message",
146: "The authenticator is misconfigured");
147: return null;
148: }
149:
150: if (getLogger().isDebugEnabled()) {
151: getLogger().debug("DBCOOKIEAUTH: query is: " + query);
152: }
153: st = conn.createStatement();
154: rs = st.executeQuery(query);
155:
156: if (rs.next()) {
157: if (getLogger().isDebugEnabled()) {
158: getLogger().debug(
159: "DBCOOKIEAUTH: authorized successfully");
160: }
161: Session session = null;
162:
163: if (cs) {
164: session = req.getSession(false);
165: if (session != null) {
166: if (as == false) {
167: session.invalidate();
168: session = req.getSession(true);
169: if (getLogger().isDebugEnabled()) {
170: getLogger()
171: .debug(
172: "DBCOOKIEAUTH: session invalidated");
173: }
174: }
175: } else {
176: session = req.getSession(true);
177: }
178:
179: if (session == null) {
180: return null;
181: }
182:
183: if (getLogger().isDebugEnabled()) {
184: if (as) {
185: getLogger()
186: .debug(
187: "DBCOOKIEAUTH: appending to session");
188: } else {
189: getLogger().debug(
190: "DBCOOKIEAUTH: session created");
191: }
192: }
193: } else {
194: if (getLogger().isDebugEnabled()) {
195: getLogger()
196: .debug(
197: "DBCOOKIEAUTH: leaving session untouched");
198: }
199: }
200:
201: HashMap actionMap = this .propagateParameters(conf, rs,
202: session);
203: if (!conn.getAutoCommit()) {
204: conn.commit();
205: }
206: return Collections.unmodifiableMap(actionMap);
207: }
208: if (!conn.getAutoCommit()) {
209: conn.rollback();
210: }
211:
212: req
213: .setAttribute(
214: "message",
215: "The username or password were incorrect, please check your CAPS LOCK key and try again.");
216: if (getLogger().isDebugEnabled()) {
217: getLogger().debug("DBCOOKIEAUTH: no results for query");
218: }
219: } catch (Exception e) {
220: if (conn != null) {
221: try {
222: if (!conn.getAutoCommit()) {
223: conn.rollback();
224: }
225: } catch (Exception se) {
226: // ignore
227: }
228: }
229: getLogger().error("Exception: ", e);
230: return null;
231: } finally {
232: if (rs != null) {
233: rs.close();
234: }
235: if (st != null) {
236: st.close();
237: }
238: if (conn != null) {
239: try {
240: conn.close();
241: } catch (Exception e) {
242: // ignore
243: }
244: }
245: }
246: return null;
247: }
248:
249: /**
250: * Gets the authQuery attribute of the DatabaseCookieAuthenticatorAction
251: * object
252: *
253: * @param objectModel Description of Parameter
254: * @param conf Description of Parameter
255: * @param req Description of Parameter
256: * @return The authQuery value
257: */
258: private String getAuthQuery(Map objectModel, Configuration conf,
259: Request req) {
260: boolean first_constraint = true;
261: StringBuffer queryBuffer = new StringBuffer("SELECT ");
262: StringBuffer queryBufferEnd = new StringBuffer("");
263: String dbcol;
264: String cookie_name;
265: String cookie_value;
266: String nullstr;
267: boolean nullable = false;
268: Configuration table = conf.getChild("table");
269: Configuration[] select = table.getChildren("select");
270: try {
271: for (int i = 0; i < select.length; i++) {
272: if (i != 0) {
273: queryBuffer.append(", ");
274: }
275: dbcol = select[i].getAttribute("dbcol");
276: queryBuffer.append(dbcol);
277: cookie_name = select[i].getAttribute("cookie-name", "");
278: if (StringUtils.isEmpty(cookie_name.trim())) {
279: continue;
280: }
281: nullstr = select[i].getAttribute("nullable", "");
282: if (BooleanUtils.toBoolean(nullstr.trim())) {
283: nullable = true;
284: }
285: /*
286: * if there is a cookie name,
287: * but not the value, we exit immediately do
288: * that authorization fails authomatically
289: */
290: cookie_value = getCookie(objectModel, cookie_name)
291: .getValue();
292:
293: if (cookie_value == null
294: || cookie_value.trim().length() == 0) {
295: // value is null
296: if (!nullable) {
297: if (getLogger().isDebugEnabled()) {
298: getLogger().debug(
299: "DBCOOKIEAUTH: cookie-name "
300: + cookie_name
301: + " does not exist");
302: }
303: return null;
304: }
305: } else {
306: if (!first_constraint) {
307: queryBufferEnd.append(" AND ");
308: }
309: queryBufferEnd.append(dbcol + "='" + cookie_value
310: + "'");
311: first_constraint = false;
312: }
313: }
314: queryBuffer.append(" FROM ");
315: queryBuffer.append(table.getAttribute("name"));
316: if (!queryBufferEnd.toString().trim().equals("")) {
317: queryBuffer.append(" WHERE ").append(queryBufferEnd);
318: }
319: return queryBuffer.toString();
320: } catch (Exception e) {
321: getLogger().error("Exception: ", e);
322: return null;
323: }
324: }
325:
326: public static Cookie getCookie(Map objectModel, String cookieName) {
327:
328: Request request = ObjectModelHelper.getRequest(objectModel);
329:
330: Cookie[] cookies = request.getCookies();
331: if (cookies != null) {
332: for (int count = 0; count < cookies.length; count++) {
333: Cookie currentCookie = cookies[count];
334: if (currentCookie.getName().equals(cookieName)) {
335: return currentCookie;
336: }
337: }
338: }
339:
340: return null;
341: }
342:
343: /**
344: * Description of the Method
345: *
346: * @param conf Description of Parameter
347: * @param rs Description of Parameter
348: * @param session Description of Parameter
349: * @return Description of the Returned Value
350: */
351: private HashMap propagateParameters(Configuration conf,
352: ResultSet rs, Session session) {
353: Configuration table = conf.getChild("table");
354: Configuration[] select = table.getChildren("select");
355: String session_param;
356: HashMap map = new HashMap();
357: try {
358: for (int i = 0; i < select.length; i++) {
359: try {
360: session_param = select[i]
361: .getAttribute("to-session");
362: if (session_param != null
363: && !session_param.trim().equals("")) {
364: String s = rs.getString(i + 1);
365: /*
366: * propagate to session
367: */
368: Object o = null;
369: String type = select[i]
370: .getAttribute("type", "");
371: // "string" is the default type
372: if (StringUtils.isEmpty(type.trim())
373: || "string".equals(type)) {
374: o = s;
375: } else if ("long".equals(type)) {
376: Long l = Long.decode(s);
377: o = l;
378: } else if ("double".equals(type)) {
379: Double d = Double.valueOf(s);
380: o = d;
381: }
382: if (session != null) {
383: session.setAttribute(session_param, o);
384: if (getLogger().isDebugEnabled()) {
385: getLogger().debug(
386: "DBCOOKIEAUTH: propagating param "
387: + session_param + "="
388: + s);
389: }
390: }
391: map.put(session_param, o);
392: }
393: } catch (Exception e) {
394: // ignore
395: }
396: }
397: return map;
398: } catch (Exception e) {
399: getLogger().error("Exception: ", e);
400: }
401: return null;
402: }
403: }
|