/AuthenticRoast/AuthenticRoast-Impl/src/main/java/name/aikesommer/authenticator/TomcatAuthenticator.java

http://authenticroast.googlecode.com/ · Java · 287 lines · 220 code · 38 blank · 29 comment · 35 complexity · 0fef8802bfbe3a4b0c39637f32a551c9 MD5 · raw file

  1. /**
  2. * Copyright (C) 2007-2010 Aike J Sommer (http://aikesommer.name/)
  3. *
  4. * This file is part of AuthenticRoast.
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General
  17. * Public License along with this library; if not, write to the
  18. * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  19. * Boston, MA 02110-1301 USA
  20. *
  21. * You can reach the author and get more information about this
  22. * project at: http://aikesommer.name/
  23. */
  24. package name.aikesommer.authenticator;
  25. import java.io.IOException;
  26. import java.lang.reflect.Field;
  27. import java.security.Principal;
  28. import java.util.LinkedList;
  29. import javax.servlet.ServletContext;
  30. import javax.servlet.ServletException;
  31. import org.apache.catalina.Authenticator;
  32. import org.apache.catalina.Container;
  33. import org.apache.catalina.Context;
  34. import org.apache.catalina.Realm;
  35. import org.apache.catalina.Session;
  36. import org.apache.catalina.authenticator.Constants;
  37. import org.apache.catalina.connector.Request;
  38. import org.apache.catalina.connector.Response;
  39. import org.apache.catalina.core.ApplicationContext;
  40. import org.apache.catalina.core.StandardContext;
  41. import org.apache.catalina.deploy.SecurityConstraint;
  42. import org.apache.catalina.realm.GenericPrincipal;
  43. import org.apache.catalina.valves.ValveBase;
  44. /**
  45. * This is the main class called by the container. You probably dont wanna
  46. * call this class directly.
  47. *
  48. * @author Aike J Sommer
  49. */
  50. public class TomcatAuthenticator extends ValveBase implements Authenticator {
  51. static {
  52. RegistryImpl.setResolver(new ClassLoaderResolver() {
  53. public ClassLoader resolve(ServletContext context) {
  54. try {
  55. Field appContextField =
  56. context.getClass().getDeclaredField("context");
  57. appContextField.setAccessible(true);
  58. ApplicationContext appContext =
  59. (ApplicationContext) appContextField.get(context);
  60. appContextField.setAccessible(false);
  61. Field stdContextField = appContext.getClass().
  62. getDeclaredField("context");
  63. stdContextField.setAccessible(true);
  64. StandardContext stdContext =
  65. (StandardContext) stdContextField.get(appContext);
  66. appContextField.setAccessible(false);
  67. return stdContext.getLoader().getClassLoader();
  68. } catch (Exception ex) {
  69. throw new RuntimeException(ex);
  70. }
  71. }
  72. });
  73. }
  74. private Context context;
  75. private AuthenticationManagerBase manager = new AuthenticationManagerBase() {
  76. @Override
  77. public void register(AuthenticationRequest request, SimplePrincipal simplePrincipal) {
  78. TomcatAuthenticator.this.register(request, simplePrincipal);
  79. super.register(request, simplePrincipal);
  80. }
  81. };
  82. protected void register(AuthenticationRequest request,
  83. SimplePrincipal simplePrincipal) {
  84. try {
  85. Tomcat6Request req =
  86. (Tomcat6Request) request;
  87. GenericPrincipal gp = new GenericPrincipal(context.getRealm(),
  88. simplePrincipal.getName(), null,
  89. new LinkedList<String>(simplePrincipal.getGroups()),
  90. simplePrincipal);
  91. req.getCatalinaRequest().setAuthType("ROAST");
  92. req.getCatalinaRequest().setUserPrincipal(gp);
  93. Session session = req.getCatalinaRequest().getSessionInternal(true);
  94. session.setAuthType("ROAST");
  95. session.setPrincipal(gp);
  96. session.setNote(Constants.SESS_USERNAME_NOTE, simplePrincipal.getName());
  97. } catch (Exception ex) {
  98. throw new RuntimeException(ex);
  99. }
  100. }
  101. @Override
  102. public void setContainer(Container container) {
  103. this.context = (Context) container;
  104. super.setContainer(container);
  105. }
  106. private boolean hasAuthConstraint(SecurityConstraint[] constraints) {
  107. if (constraints == null) {
  108. return false;
  109. }
  110. for (int i = 0; i < constraints.length; i++) {
  111. SecurityConstraint constraint = constraints[i];
  112. if (! constraint.getAuthConstraint()) {
  113. continue;
  114. }
  115. return true;
  116. }
  117. return false;
  118. }
  119. private boolean checkRoles(Request request, Response response,
  120. SecurityConstraint[] constraints, Principal principal) {
  121. if (constraints == null) {
  122. return true;
  123. }
  124. for (int i = 0; i < constraints.length; i++) {
  125. SecurityConstraint constraint = constraints[i];
  126. if (! constraint.getAuthConstraint()) {
  127. continue;
  128. }
  129. String[] roles = constraint.getAllRoles() ? context.findSecurityRoles()
  130. : constraint.findAuthRoles();
  131. if (roles == null) {
  132. roles = new String[0];
  133. }
  134. boolean match = false;
  135. for (int j = 0; j < roles.length; j++) {
  136. String role = roles[j];
  137. if (checkRole(principal, role)) {
  138. match = true;
  139. break;
  140. }
  141. }
  142. if (! match) {
  143. response.setStatus(Response.SC_FORBIDDEN);
  144. return false;
  145. }
  146. }
  147. return true;
  148. }
  149. private boolean checkRole(Principal principal, String role) {
  150. if (principal instanceof SimplePrincipal) {
  151. return ((SimplePrincipal) principal).getGroups().contains(role);
  152. } else if (principal instanceof GenericPrincipal) {
  153. for (int i = 0; i < ((GenericPrincipal) principal).getRoles().length; i++) {
  154. String hasRole = ((GenericPrincipal) principal).getRoles()[i];
  155. if (hasRole.equals(role)) {
  156. return true;
  157. }
  158. }
  159. }
  160. return false;
  161. }
  162. @Override
  163. public void invoke(Request request, Response response) throws IOException, ServletException {
  164. if (null == request.getCharacterEncoding()) {
  165. request.setCharacterEncoding("UTF-8");
  166. }
  167. Realm realm = context.getRealm();
  168. SecurityConstraint[] constraints =
  169. realm.findSecurityConstraints(request, this.context);
  170. RegistryImpl registry = RegistryImpl.forContext(request.getContext().
  171. getServletContext());
  172. if (!realm.hasUserDataPermission(request, response,
  173. constraints)) {
  174. return;
  175. }
  176. boolean hasAuthConstraint = hasAuthConstraint(constraints);
  177. Tomcat6Request authReq =
  178. new AuthenticationRequestImpl.Tomcat6(request, response,
  179. hasAuthConstraint, registry.isCrossContext());
  180. registry.createPrincipalStore(authReq);
  181. PluggableAuthenticator authenticator = registry.authenticator();
  182. if (authenticator == null) {
  183. if (! hasAuthConstraint) {
  184. getNext().invoke(request, response);
  185. return;
  186. } else {
  187. response.setStatus(Response.SC_FORBIDDEN);
  188. return;
  189. }
  190. }
  191. authenticator.begin(manager, authReq);
  192. boolean finished = false;
  193. try {
  194. SimplePrincipal simplePrincipal = registry.principalStore().fetch();
  195. if (simplePrincipal != null) {
  196. AuthenticationRequest.ManageAction action = authenticator.manage(manager, authReq);
  197. switch (action) {
  198. case None:
  199. register(authReq, simplePrincipal);
  200. if (!checkRoles(request, response, constraints,
  201. simplePrincipal)) {
  202. return;
  203. }
  204. authenticator.finish(manager, authReq);
  205. finished = true;
  206. getNext().invoke(request, response);
  207. return;
  208. case Clear:
  209. registry.principalStore().invalidate();
  210. if (authReq.isForwarded()) {
  211. return;
  212. }
  213. }
  214. }
  215. switch (authenticator.tryAuthenticate(manager, authReq)) {
  216. case Continue:
  217. return;
  218. case Failure:
  219. response.setStatus(Response.SC_FORBIDDEN);
  220. return;
  221. case None:
  222. if (hasAuthConstraint) {
  223. switch (authenticator.authenticate(manager, authReq)) {
  224. case Continue:
  225. return;
  226. case Success:
  227. break;
  228. default:
  229. response.setStatus(Response.SC_FORBIDDEN);
  230. return;
  231. }
  232. }
  233. }
  234. simplePrincipal = registry.principalStore().fetch();
  235. if (!checkRoles(request, response, constraints, simplePrincipal)) {
  236. return;
  237. }
  238. } catch (Throwable t) {
  239. finished = true;
  240. authenticator.abort(manager, authReq, t);
  241. throw new RuntimeException(t);
  242. } finally {
  243. if (!finished) {
  244. authenticator.finish(manager, authReq);
  245. }
  246. }
  247. getNext().invoke(request, response);
  248. }
  249. }