001: package dinamica.security;
002:
003: import java.io.*;
004: import javax.servlet.*;
005: import javax.servlet.http.*;
006:
007: import dinamica.*;
008:
009: import javax.sql.DataSource;
010: import java.sql.*;
011: import java.util.HashMap;
012:
013: /**
014: * Servlet Filter to enforce Dinamica
015: * server-side security. This module
016: * checks/enforce authentication and autorization
017: * over protected resources, requires special configuration
018: * via filter parameters and the existence of a database schema
019: * containing the tables defined by Dinamica framework for
020: * the security system. It also assumes the existence of
021: * a set of generic Actions used for security tasks (login, loginerror, etc).
022: * This filter mantains API level compatibity with Servlet J2EE
023: * security methods, like getUserName, isUserInRole, etc.
024: *
025: * <br>
026: * Creation date: 10/march/2004<br>
027: * Last Update: 10/march/2004<br>
028: * (c) 2003 Martin Cordova<br>
029: * This code is released under the LGPL license<br>
030: * @author Martin Cordova - dinamica@martincordova.com
031: *
032: */
033:
034: public class SecurityFilter implements Filter {
035:
036: //filter configuration object
037: private FilterConfig _config = null;
038:
039: //filter parameters
040: String _dataSource = null;
041: String _ssl = null;
042: String _loginForm = null;
043: DataSource _ds = null;
044: String _appAlias = null;
045: String _passwordPolicy = null;
046: boolean _debug = false;
047:
048: //cache to store protected services and its authorized roles
049: HashMap<String, String[]> protectedRes = new HashMap<String, String[]>();
050:
051: /**
052: * Init filter
053: **/
054: public void init(FilterConfig config) throws ServletException {
055:
056: _config = config;
057:
058: try {
059:
060: //get filter config parameters
061: _dataSource = _config.getInitParameter("datasource");
062: _loginForm = _config.getInitParameter("loginform");
063: _ssl = _config.getInitParameter("ssl");
064: _appAlias = _config.getInitParameter("app-alias");
065: String debug = _config.getInitParameter("debug");
066:
067: if (debug != null && debug.equals("true"))
068: _debug = true;
069:
070: // get prefix for jndi lookups
071: String _jndiPrefix = config.getServletContext()
072: .getInitParameter("jndi-prefix");
073: if (_jndiPrefix == null)
074: _jndiPrefix = "";
075:
076: String jndiName = _jndiPrefix + _dataSource;
077:
078: //get filter datasource
079: _ds = Jndi.getDataSource(jndiName);
080:
081: //init security cache (protected resources)
082: loadProtectedResources();
083:
084: //save datasource JNDI name as a context attribute
085: //to be used by other modules
086: _config.getServletContext().setAttribute(
087: "dinamica.security.datasource", jndiName);
088:
089: String context = _config.getServletContext()
090: .getServletContextName();
091: System.err.println("FILTER STARTED [" + context
092: + "@SecurityFilter@" + new java.util.Date() + "]");
093:
094: } catch (Throwable e) {
095: System.err.println("[SecurityFilterException@"
096: + new java.util.Date() + "] " + e.getMessage());
097: throw new ServletException(e);
098: }
099:
100: }
101:
102: /**
103: * Intercept request
104: */
105: public void doFilter(ServletRequest request,
106: ServletResponse response, FilterChain next)
107: throws IOException, ServletException
108:
109: {
110:
111: //flag used to indicate if the request can proceed
112: boolean isOK = false;
113:
114: //get http request/response
115: HttpServletRequest req = (HttpServletRequest) request;
116: HttpServletResponse res = (HttpServletResponse) response;
117:
118: //get path
119: String uri = req.getRequestURI();
120: String context = req.getContextPath(); //patch 2006-01-12
121: uri = uri.substring(context.length());
122:
123: if (_debug) {
124: debug(uri, "Intercepting request...");
125: debug(uri, "Session Cookie: " + getSessionCookie(req));
126: }
127:
128: //get authenticated principal
129: HttpSession s = req.getSession(true);
130: DinamicaUser user = (DinamicaUser) s
131: .getAttribute("dinamica.security.login");
132:
133: //create request wrapper
134: RequestWrapper rw = new RequestWrapper(req);
135: rw.setUserPrincipal(user);
136:
137: //set default attributes related to security settings
138: rw.setAttribute("dinamica.security.application", _appAlias);
139: rw
140: .setAttribute("dinamica.security.passpolicy",
141: _passwordPolicy);
142:
143: //requires SSL?
144: if (_ssl.equals("true") && !req.isSecure()) {
145: //PATCH 2006-11-08 - log not authenticated access
146: if (_debug)
147: debug(uri, "Request rejected! - SSL required.");
148:
149: //not an ssl request - reject it
150: res.sendError(401, "Requires secure (HTTPS) access.");
151: return;
152: }
153:
154: try {
155:
156: //is protected?
157: if (protectedRes.containsKey(uri)) {
158:
159: //get authorized roles
160: String roles[] = (String[]) protectedRes.get(uri);
161:
162: //authenticated? can't access protected
163: //resource without proper authentication
164: if (user == null) {
165:
166: //PATCH 2006-11-08 - log not authenticated access
167: if (_debug)
168: debug(uri,
169: "Request not authenticated! - redirecting to login page.");
170:
171: //redirect to login page
172: String loginPage = context + _loginForm;
173: res.sendRedirect(loginPage);
174: return; //patch 2006-02-17 - failed with Tomcat
175: } else {
176:
177: //2005-06-09 - debug to stderr
178: if (_debug) {
179: debug(uri, roles, user);
180: }
181:
182: //authorized?
183: for (int i = 0; i < roles.length; i++) {
184: //check user roles
185: if (rw.isUserInRole(roles[i])) {
186: //OK - authorized
187: isOK = true;
188: break;
189: }
190: }
191: }
192:
193: } else {
194:
195: //2006-02-16 - debug to stderr
196: if (_debug) {
197: debug(uri, user);
198: }
199:
200: //not a protected resource - let it pass
201: isOK = true;
202: }
203:
204: if (isOK)
205: next.doFilter(rw, response);
206: else
207: res.sendError(403, "NOT AUTHORIZED");
208:
209: }
210:
211: catch (Throwable e) {
212: throw new ServletException(e);
213: }
214:
215: }
216:
217: /**
218: * Load into cache the list of protected
219: * resources and authorized roles for each resource
220: * @throws Throwable
221: */
222: void loadProtectedResources() throws Throwable {
223:
224: TemplateEngine t = null;
225: Connection con = null;
226: try {
227:
228: con = _ds.getConnection();
229: Db db = new Db(con);
230:
231: //get default password expiration policy for this webapp
232: String sqlpass = "select pwd_policy from ${schema}s_application where app_alias = '"
233: + _appAlias + "'";
234: t = new TemplateEngine(_config.getServletContext(), null,
235: sqlpass);
236: sqlpass = t.getSql(null);
237:
238: Recordset rspass = db.get(sqlpass);
239:
240: if (rspass.getRecordCount() == 0)
241: throw new Throwable(
242: "Security configuration not found for this application alias: "
243: + _appAlias
244: + " - please check your web.xml configuration regarding the security filter.");
245:
246: rspass.next();
247: _passwordPolicy = rspass.getString("pwd_policy");
248:
249: //get list of protected resource for this app-alias
250: String sql = "select service_id, path"
251: + " from ${schema}s_service s, ${schema}s_application a"
252: + " where s.app_id = a.app_id"
253: + " and a.app_alias = '" + _appAlias + "'";
254:
255: t = new TemplateEngine(_config.getServletContext(), null,
256: sql);
257: sql = t.getSql(null);
258:
259: Recordset rs = db.get(sql);
260:
261: //get authorized roles for each resource
262: String query = "select r.rolename from"
263: + " ${schema}s_service_role sr, ${schema}s_role r"
264: + " where sr.role_id = r.role_id"
265: + " and sr.service_id = ";
266:
267: t = new TemplateEngine(_config.getServletContext(), null,
268: query);
269: query = t.getSql(null);
270:
271: while (rs.next()) {
272:
273: sql = query + rs.getString("service_id");
274:
275: Recordset rs2 = db.get(sql);
276:
277: //store rolenames into array
278: String roles[] = new String[rs2.getRecordCount()];
279: int i = 0;
280: while (rs2.next()) {
281: roles[i] = rs2.getString("rolename");
282: i++;
283: }
284:
285: //save resource + roles array into hashmap
286: protectedRes.put(rs.getString("path"), roles);
287:
288: }
289: } catch (Throwable error) {
290: throw error;
291: } finally {
292: if (con != null)
293: con.close();
294: }
295:
296: }
297:
298: /* (non-Javadoc)
299: * @see javax.servlet.Filter#destroy()
300: */
301: public void destroy() {
302:
303: _config = null;
304:
305: }
306:
307: /**
308: * Print debug information to stderr about resource access authorization
309: * @param uri Action being checked for authorization
310: * @param uriRoles Autorized roles
311: * @param user Current authenticated user
312: */
313: void debug(String uri, String[] uriRoles, DinamicaUser user) {
314: try {
315: String date = StringUtil.formatDate(new java.util.Date(),
316: "yyyy-MM-dd HH:mm:ss");
317: StringBuffer buf = new StringBuffer();
318: buf.append("[DEBUG@SecurityFilter@" + date + "] ");
319: buf.append("URI (" + uri + ") ");
320: buf.append("Authorized Roles (");
321: for (int i = 0; i < uriRoles.length; i++) {
322: buf.append(uriRoles[i] + ";");
323: }
324: buf.append(") ");
325: buf.append("USER (" + user.getName() + ") ");
326: buf.append("User Roles (");
327: String userRoles[] = user.getRoles();
328: for (int i = 0; i < userRoles.length; i++) {
329: buf.append(userRoles[i] + ";");
330: }
331: buf.append(") ");
332:
333: System.err.println(buf.toString());
334:
335: } catch (Throwable e) {
336: e.printStackTrace();
337: }
338: }
339:
340: /**
341: * Print debug information to stderr about access to public (non-protected) resources
342: * @param uri Action being checked for authorization
343: * @param user Current authenticated user
344: */
345: void debug(String uri, DinamicaUser user) {
346: try {
347: String date = StringUtil.formatDate(new java.util.Date(),
348: "yyyy-MM-dd HH:mm:ss");
349: StringBuffer buf = new StringBuffer();
350: buf.append("[DEBUG@SecurityFilter@" + date + "] ");
351: buf.append("PUBLIC URI (" + uri + ") ");
352:
353: if (user != null) {
354: buf.append("USER (" + user.getName() + ") ");
355: buf.append("User Roles (");
356: String userRoles[] = user.getRoles();
357: for (int i = 0; i < userRoles.length; i++) {
358: buf.append(userRoles[i] + ";");
359: }
360: buf.append(") ");
361: }
362: System.err.println(buf.toString());
363: } catch (Throwable e) {
364: e.printStackTrace();
365: }
366: }
367:
368: /**
369: * Print debug information to stderr about resource access
370: * @param uri Action being checked for authorization
371: * @param msg Custom debug message
372: */
373: void debug(String uri, String msg) {
374: try {
375: String date = StringUtil.formatDate(new java.util.Date(),
376: "yyyy-MM-dd HH:mm:ss");
377: StringBuffer buf = new StringBuffer();
378: buf.append("[DEBUG@SecurityFilter@" + date + "] ");
379: buf.append("URI (" + uri + ") ");
380: buf.append(msg);
381: System.err.println(buf.toString());
382: } catch (Throwable e) {
383: e.printStackTrace();
384: }
385: }
386:
387: String getSessionCookie(HttpServletRequest req) {
388: String value = null;
389: Cookie c[] = req.getCookies();
390: if (c != null) {
391: for (int i = 0; i < c.length; i++) {
392: if (c[i].getName().equals("JSESSIONID")) {
393: value = c[i].getValue();
394: break;
395: }
396: }
397: }
398: return value;
399: }
400:
401: }
|