├── .gitignore ├── README.md ├── pom.xml └── src └── main └── java └── org └── omnifaces └── security ├── cdi └── Beans.java ├── constraints ├── EJBSecurityBean.java ├── RolesAllowed.java └── RolesAllowedValidator.java ├── events ├── AuthenticatedEvent.java ├── LoggedOutEvent.java └── SecurityEvent.java └── jaspic ├── Utils.java ├── authmodules ├── BasicAuthModule.java ├── OmniServerAuthModule.java └── TokenAuthModule.java ├── config ├── AuthStacks.java ├── AuthStacksBuilder.java ├── ControlFlag.java └── Module.java ├── core ├── AuthParameters.java ├── AuthResult.java ├── HttpMsgContext.java ├── HttpServerAuthModule.java ├── Jaspic.java ├── SamServices.java ├── ServerAuthModuleWrapper.java └── ServiceType.java ├── exceptions ├── ProfileIncompleteException.java └── RegistrationException.java ├── factory ├── OmniAuthConfigProvider.java ├── OmniServerAuthConfig.java └── OmniServerAuthContext.java ├── filters ├── HttpFilter.java └── OmniServerAuthFilter.java ├── listeners ├── BaseServletContextListener.java └── SamAutoDeregistrationListener.java ├── request ├── BaseCookieDAO.java ├── HttpServletRequestDelegator.java ├── LoginTokenCookieDAO.java ├── RememberMeSettingCookieDAO.java ├── RequestCopier.java ├── RequestData.java ├── RequestDataDAO.java └── StateCookieDAO.java ├── user ├── Authenticator.java ├── TokenAuthenticator.java ├── UsernameOnlyAuthenticator.java └── UsernamePasswordAuthenticator.java └── wrappers ├── AutoRegisterSessionWrapper.java ├── RememberMeWrapper.java └── SaveAndRedirectWrapper.java /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | bin/ 3 | target/ 4 | dist/ 5 | /.settings 6 | /.classpath 7 | /.project 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [OmniSecurity homepage is available at **omnisecurity.omnifaces.org**](http://omnisecurity.omnifaces.org). 2 | 3 | **NOTE** 4 | 5 | Core concepts of OmniSecurity have been standardised as [JSR 375](https://www.jcp.org/en/jsr/detail?id=375) and reimplemented as [Soteria](https://github.com/javaee-security-spec/soteria). 6 | 7 | This means OmniSecurity is now deprecated in favor of JSR 375/Soteria. 8 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 3.0.0 8 | 9 | 10 | org.omnifaces 11 | omnifaces-security 12 | 0.6-SNAPSHOT 13 | jar 14 | 15 | OmniSecurity 16 | Java EE, JSF and JASPIC integrated security 17 | 18 | http://omnisecurity.omnifaces.org 19 | 20 | OmniFaces 21 | http://omnifaces.org 22 | 23 | 2013 24 | 25 | 26 | 27 | The Apache Software License, Version 2.0 28 | http://www.apache.org/licenses/LICENSE-2.0.txt 29 | repo 30 | 31 | 32 | 33 | 34 | 35 | ossrh 36 | https://oss.sonatype.org/content/repositories/snapshots 37 | 38 | 39 | 40 | 41 | https://github.com/omnifaces/omnisecurity 42 | scm:git:git://github.com/omnifaces/omnisecurity.git 43 | scm:git:git@github.com:omnifaces/omnisecurity.git 44 | 45 | 46 | 47 | UTF-8 48 | UTF-8 49 | 1.7 50 | 7.0 51 | 52 | 53 | 54 | 55 | arjan.tijms 56 | Arjan Tijms 57 | arjan.tijms@gmail.com 58 | 59 | 60 | 61 | jan.beernink 62 | Jan Beernink 63 | jan.beernink@gmail.com 64 | 65 | 66 | 67 | 68 | 69 | javax 70 | javaee-api 71 | ${javaee.version} 72 | provided 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | maven-compiler-plugin 82 | 3.2 83 | 84 | ${javase.version} 85 | ${javase.version} 86 | ${javase.version} 87 | 88 | 89 | 90 | 91 | 92 | org.apache.maven.plugins 93 | maven-jar-plugin 94 | 2.5 95 | 96 | 97 | 98 | true 99 | true 100 | 101 | 102 | ${project.url} 103 | ${project.artifactId} 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | org.apache.maven.plugins 112 | maven-source-plugin 113 | 2.4 114 | 115 | 116 | attach-sources 117 | 118 | jar-no-fork 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | org.apache.maven.plugins 127 | maven-javadoc-plugin 128 | 2.10.1 129 | 130 | 1.8 131 | 132 | 133 | 134 | attach-javadocs 135 | 136 | jar 137 | 138 | 139 | 140 | 141 | 142 | 147 | 148 | org.sonatype.plugins 149 | nexus-staging-maven-plugin 150 | 1.6.5 151 | true 152 | 153 | ossrh 154 | https://oss.sonatype.org/ 155 | true 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | release 165 | 166 | 167 | 168 | 169 | org.apache.maven.plugins 170 | maven-gpg-plugin 171 | 1.5 172 | 173 | 174 | sign-artifacts 175 | verify 176 | 177 | sign 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/cdi/Beans.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.cdi; 14 | 15 | import java.lang.annotation.Annotation; 16 | 17 | import javax.enterprise.inject.spi.Bean; 18 | import javax.enterprise.inject.spi.BeanManager; 19 | import javax.enterprise.inject.spi.CDI; 20 | import javax.naming.InitialContext; 21 | import javax.naming.NamingException; 22 | 23 | /** 24 | * The obligatory CDI utility class to obtain a reference to CDI's BeanManager. 25 | * 26 | * @author Arjan Tijms 27 | * 28 | */ 29 | public class Beans { 30 | 31 | public static T getReference(Class beanClass) { 32 | return getReference(beanClass, getBeanManager()); 33 | } 34 | 35 | public static T getReferenceOrNull(Class beanClass) { 36 | return getReferenceOrNull(beanClass, getBeanManager()); 37 | } 38 | 39 | @SuppressWarnings("unchecked") 40 | public static T getReference(Class beanClass, BeanManager beanManager) { 41 | 42 | Bean bean = (Bean) beanManager.resolve(beanManager.getBeans(beanClass)); 43 | 44 | return (T) beanManager.getReference(bean, beanClass, beanManager.createCreationalContext(bean)); 45 | } 46 | 47 | @SuppressWarnings("unchecked") 48 | public static T getReferenceOrNull(Class beanClass, BeanManager beanManager) { 49 | try { 50 | Bean bean = (Bean) beanManager.resolve(beanManager.getBeans(beanClass)); 51 | 52 | return (T) beanManager.getReference(bean, beanClass, beanManager.createCreationalContext(bean)); 53 | } catch (Exception e) { 54 | return null; 55 | } 56 | } 57 | 58 | public static T getInstance(final Class type, final Class scope) { 59 | return getInstance(type, scope, getBeanManager()); 60 | } 61 | 62 | public static T getInstance(final Class type, final Class scope, final BeanManager beanManager) { 63 | 64 | @SuppressWarnings("unchecked") 65 | Bean bean = (Bean) beanManager.resolve(beanManager.getBeans(type)); 66 | 67 | return beanManager.getContext(scope).get(bean, beanManager.createCreationalContext(bean)); 68 | } 69 | 70 | public static BeanManager tryGetBeanManager() { 71 | try { 72 | return getBeanManager(); 73 | } catch (IllegalStateException e) { 74 | return null; 75 | } 76 | } 77 | 78 | public static BeanManager getBeanManager() { 79 | InitialContext context = null; 80 | try { 81 | context = new InitialContext(); 82 | return (BeanManager) context.lookup("java:comp/BeanManager"); 83 | } catch (NamingException e) { 84 | return CDI.current().getBeanManager(); 85 | } finally { 86 | closeContext(context); 87 | } 88 | } 89 | 90 | private static void closeContext(InitialContext context) { 91 | try { 92 | if (context != null) { 93 | context.close(); 94 | } 95 | } catch (NamingException e) { 96 | throw new RuntimeException(e); 97 | } 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/constraints/EJBSecurityBean.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.constraints; 14 | 15 | import java.security.Principal; 16 | 17 | import javax.annotation.Resource; 18 | import javax.ejb.SessionContext; 19 | import javax.ejb.Stateless; 20 | 21 | @Stateless 22 | public class EJBSecurityBean { 23 | 24 | @Resource 25 | private SessionContext context; 26 | 27 | public Principal getUserPrincipal() { 28 | return context.getCallerPrincipal(); 29 | } 30 | 31 | public boolean isUserInRole(String roleName) { 32 | return context.isCallerInRole(roleName); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/constraints/RolesAllowed.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.constraints; 14 | 15 | import static java.lang.annotation.ElementType.METHOD; 16 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 17 | 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.Target; 20 | 21 | import javax.validation.Constraint; 22 | import javax.validation.Payload; 23 | 24 | @Constraint(validatedBy = { RolesAllowedValidator.class }) 25 | @Retention(RUNTIME) 26 | @Target(METHOD) 27 | public @interface RolesAllowed { 28 | String[] value(); 29 | String message() default "{access.denied}"; 30 | Class[] groups() default {}; 31 | Class[] payload() default {}; 32 | } -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/constraints/RolesAllowedValidator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.constraints; 14 | 15 | import static javax.validation.constraintvalidation.ValidationTarget.PARAMETERS; 16 | 17 | import javax.ejb.EJB; 18 | import javax.inject.Inject; 19 | import javax.servlet.http.HttpServletRequest; 20 | import javax.validation.ConstraintValidator; 21 | import javax.validation.ConstraintValidatorContext; 22 | import javax.validation.constraintvalidation.SupportedValidationTarget; 23 | 24 | @SupportedValidationTarget(PARAMETERS) 25 | public class RolesAllowedValidator implements ConstraintValidator { 26 | 27 | private String[] roles; 28 | 29 | @Inject 30 | private HttpServletRequest request; 31 | 32 | @EJB 33 | private EJBSecurityBean securityBean; 34 | 35 | @Override 36 | public void initialize(RolesAllowed constraintAnnotation) { 37 | roles = constraintAnnotation.value(); 38 | } 39 | 40 | @Override 41 | public boolean isValid(final Object[] parameters, final ConstraintValidatorContext context) { 42 | 43 | for (String role : roles) { 44 | if (request != null) { 45 | if (request.isUserInRole(role)) { 46 | return true; 47 | } 48 | } else { 49 | if (securityBean.isUserInRole(role)) { 50 | return true; 51 | } 52 | } 53 | } 54 | 55 | return false; 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/events/AuthenticatedEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.events; 14 | 15 | import java.security.Principal; 16 | 17 | public class AuthenticatedEvent extends SecurityEvent { 18 | 19 | private static final long serialVersionUID = 1L; 20 | 21 | public AuthenticatedEvent(Object source, Principal principal) { 22 | super(source, principal); 23 | } 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/events/LoggedOutEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.events; 14 | 15 | import java.security.Principal; 16 | 17 | public class LoggedOutEvent extends SecurityEvent { 18 | 19 | private static final long serialVersionUID = 1L; 20 | 21 | public LoggedOutEvent(Object source, Principal principal) { 22 | super(source, principal); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/events/SecurityEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.events; 14 | 15 | import java.security.Principal; 16 | import java.util.EventObject; 17 | 18 | public class SecurityEvent extends EventObject { 19 | 20 | private static final long serialVersionUID = 1L; 21 | 22 | private Principal userPrincipal; 23 | 24 | public SecurityEvent(Object source, Principal userPrincipal) { 25 | super(source); 26 | this.userPrincipal = userPrincipal; 27 | } 28 | 29 | public Principal getUserPrincipal() { 30 | return userPrincipal; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic; 14 | 15 | import static java.nio.charset.StandardCharsets.UTF_8; 16 | import static java.util.regex.Pattern.quote; 17 | 18 | import java.io.ByteArrayInputStream; 19 | import java.io.ByteArrayOutputStream; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.io.OutputStream; 23 | import java.io.UnsupportedEncodingException; 24 | import java.net.URLDecoder; 25 | import java.net.URLEncoder; 26 | import java.nio.ByteBuffer; 27 | import java.nio.channels.Channels; 28 | import java.nio.channels.ReadableByteChannel; 29 | import java.nio.channels.WritableByteChannel; 30 | import java.util.ArrayList; 31 | import java.util.Collection; 32 | import java.util.Collections; 33 | import java.util.HashSet; 34 | import java.util.LinkedHashMap; 35 | import java.util.List; 36 | import java.util.Map; 37 | import java.util.Map.Entry; 38 | import java.util.Set; 39 | import java.util.zip.Deflater; 40 | import java.util.zip.DeflaterOutputStream; 41 | import java.util.zip.InflaterInputStream; 42 | 43 | import javax.servlet.http.HttpServletRequest; 44 | import javax.servlet.http.HttpServletResponse; 45 | import javax.xml.bind.DatatypeConverter; 46 | 47 | /** 48 | * An assortment of various utility methods. 49 | * 50 | * @author Arjan Tijms 51 | * 52 | */ 53 | public final class Utils { 54 | 55 | private static final String ERROR_UNSUPPORTED_ENCODING = "UTF-8 is apparently not supported on this platform."; 56 | 57 | private Utils() {} 58 | 59 | public static boolean notNull(Object... objects) { 60 | for (Object object : objects) { 61 | if (object == null) { 62 | return false; 63 | } 64 | } 65 | 66 | return true; 67 | } 68 | 69 | /** 70 | * Returns true if the given string is null or is empty. 71 | * 72 | * @param string The string to be checked on emptiness. 73 | * @return True if the given string is null or is empty. 74 | */ 75 | public static boolean isEmpty(String string) { 76 | return string == null || string.isEmpty(); 77 | } 78 | 79 | /** 80 | * Returns true if the given array is null or is empty. 81 | * 82 | * @param array The array to be checked on emptiness. 83 | * @return true if the given array is null or is empty. 84 | */ 85 | public static boolean isEmpty(Object[] array) { 86 | return array == null || array.length == 0; 87 | } 88 | 89 | /** 90 | * Returns true if the given collection is null or is empty. 91 | * 92 | * @param collection The collection to be checked on emptiness. 93 | * @return true if the given collection is null or is empty. 94 | */ 95 | public static boolean isEmpty(Collection collection) { 96 | return collection == null || collection.isEmpty(); 97 | } 98 | 99 | /** 100 | * Returns true if the given object equals one of the given objects. 101 | * @param The generic object type. 102 | * @param object The object to be checked if it equals one of the given objects. 103 | * @param objects The argument list of objects to be tested for equality. 104 | * @return true if the given object equals one of the given objects. 105 | */ 106 | @SafeVarargs 107 | public static boolean isOneOf(T object, T... objects) { 108 | for (Object other : objects) { 109 | if (object == null ? other == null : object.equals(other)) { 110 | return true; 111 | } 112 | } 113 | 114 | return false; 115 | } 116 | 117 | public static String getBaseURL(HttpServletRequest request) { 118 | String url = request.getRequestURL().toString(); 119 | return url.substring(0, url.length() - request.getRequestURI().length()) + request.getContextPath(); 120 | } 121 | 122 | public static void redirect(HttpServletResponse response, String location) { 123 | try { 124 | response.sendRedirect(location); 125 | } catch (IOException e) { 126 | throw new IllegalStateException(e); 127 | } 128 | } 129 | 130 | public static void redirect(HttpServletRequest request, HttpServletResponse response, String location) { 131 | try { 132 | if (isFacesAjaxRequest(request)) { 133 | response.setHeader("Cache-Control", "no-cache,no-store,must-revalidate"); 134 | response.setDateHeader("Expires", 0); 135 | response.setHeader("Pragma", "no-cache"); // Backwards compatibility for HTTP 1.0. 136 | response.setContentType("text/xml"); 137 | response.setCharacterEncoding(UTF_8.name()); 138 | response.getWriter().printf(FACES_AJAX_REDIRECT_XML, location); 139 | } 140 | else { 141 | response.sendRedirect(location); 142 | } 143 | } catch (IOException e) { 144 | throw new IllegalStateException(e); 145 | } 146 | } 147 | 148 | private static final Set FACES_AJAX_HEADERS = unmodifiableSet("partial/ajax", "partial/process"); 149 | private static final String FACES_AJAX_REDIRECT_XML = "" 150 | + ""; 151 | 152 | public static boolean isFacesAjaxRequest(HttpServletRequest request) { 153 | return FACES_AJAX_HEADERS.contains(request.getHeader("Faces-Request")); 154 | } 155 | 156 | @SuppressWarnings("unchecked") 157 | public static Set unmodifiableSet(Object... values) { 158 | Set set = new HashSet<>(); 159 | 160 | for (Object value : values) { 161 | if (value instanceof Object[]) { 162 | for (Object item : (Object[]) value) { 163 | set.add((E) item); 164 | } 165 | } 166 | else if (value instanceof Collection) { 167 | for (Object item : (Collection) value) { 168 | set.add((E) item); 169 | } 170 | } 171 | else { 172 | set.add((E) value); 173 | } 174 | } 175 | 176 | return Collections.unmodifiableSet(set); 177 | } 178 | 179 | public static String encodeURL(String string) { 180 | if (string == null) { 181 | return null; 182 | } 183 | 184 | try { 185 | return URLEncoder.encode(string, UTF_8.name()); 186 | } 187 | catch (UnsupportedEncodingException e) { 188 | throw new UnsupportedOperationException(ERROR_UNSUPPORTED_ENCODING, e); 189 | } 190 | } 191 | 192 | public static String decodeURL(String string) { 193 | if (string == null) { 194 | return null; 195 | } 196 | 197 | try { 198 | return URLDecoder.decode(string, UTF_8.name()); 199 | } 200 | catch (UnsupportedEncodingException e) { 201 | throw new UnsupportedOperationException(ERROR_UNSUPPORTED_ENCODING, e); 202 | } 203 | } 204 | 205 | public static String getSingleParameterFromState(String state, String paramName) { 206 | Map> requestStateParameters = getParameterMapFromState(state); 207 | 208 | List parameterValues = requestStateParameters.get(paramName); 209 | if (!isEmpty(parameterValues)) { 210 | return parameterValues.get(0); 211 | } 212 | 213 | return null; 214 | } 215 | 216 | public static Map> getParameterMapFromState(String state) { 217 | return toParameterMap(unserializeURLSafe(state)); 218 | } 219 | 220 | /** 221 | * Converts the given request query string to request parameter values map. 222 | * @param queryString The request query string. 223 | * @return The request query string as request parameter values map. 224 | */ 225 | public static Map> toParameterMap(String queryString) { 226 | String[] parameters = queryString.split(quote("&")); 227 | Map> parameterMap = new LinkedHashMap<>(parameters.length); 228 | 229 | for (String parameter : parameters) { 230 | if (parameter.contains("=")) { 231 | String[] pair = parameter.split(quote("=")); 232 | String key = decodeURL(pair[0]); 233 | String value = (pair.length > 1 && !isEmpty(pair[1])) ? decodeURL(pair[1]) : ""; 234 | List values = parameterMap.get(key); 235 | 236 | if (values == null) { 237 | values = new ArrayList<>(1); 238 | parameterMap.put(key, values); 239 | } 240 | 241 | values.add(value); 242 | } 243 | } 244 | 245 | return parameterMap; 246 | } 247 | 248 | /** 249 | * Converts the given request parameter values map to request query string. 250 | * @param parameterMap The request parameter values map. 251 | * @return The request parameter values map as request query string. 252 | */ 253 | public static String toQueryString(Map> parameterMap) { 254 | StringBuilder queryString = new StringBuilder(); 255 | 256 | for (Entry> entry : parameterMap.entrySet()) { 257 | String name = encodeURL(entry.getKey()); 258 | 259 | for (String value : entry.getValue()) { 260 | if (queryString.length() > 0) { 261 | queryString.append("&"); 262 | } 263 | 264 | queryString.append(name).append("=").append(encodeURL(value)); 265 | } 266 | } 267 | 268 | return queryString.toString(); 269 | } 270 | 271 | public static String getSingleParameterFromQueryString(String queryString, String paramName) { 272 | if (!isEmpty(queryString)) { 273 | Map> requestParameters = toParameterMap(queryString); 274 | 275 | if (!isEmpty(requestParameters.get(paramName))) { 276 | return requestParameters.get(paramName).get(0); 277 | } 278 | } 279 | 280 | return null; 281 | } 282 | 283 | /** 284 | * Serialize the given string to the short possible unique URL-safe representation. The current implementation will 285 | * decode the given string with UTF-8 and then compress it with ZLIB using "best compression" algorithm and then 286 | * Base64-encode the resulting bytes without the = padding, whereafter the Base64 characters 287 | * + and / are been replaced by respectively - and _ to make it 288 | * URL-safe (so that no platform-sensitive URL-encoding needs to be done when used in URLs). 289 | * @param string The string to be serialized. 290 | * @return The serialized URL-safe string, or null when the given string is itself null. 291 | */ 292 | public static String serializeURLSafe(String string) { 293 | if (string == null) { 294 | return null; 295 | } 296 | 297 | try { 298 | InputStream raw = new ByteArrayInputStream(string.getBytes(UTF_8)); 299 | ByteArrayOutputStream deflated = new ByteArrayOutputStream(); 300 | stream(raw, new DeflaterOutputStream(deflated, new Deflater(Deflater.BEST_COMPRESSION))); 301 | String base64 = DatatypeConverter.printBase64Binary(deflated.toByteArray()); 302 | return base64.replace('+', '-').replace('/', '_').replace("=", ""); 303 | } 304 | catch (IOException e) { 305 | // This will occur when ZLIB and/or UTF-8 are not supported, but this is not to be expected these days. 306 | throw new RuntimeException(e); 307 | } 308 | } 309 | 310 | /** 311 | * Unserialize the given serialized URL-safe string. This does the inverse of {@link #serializeURLSafe(String)}. 312 | * @param string The serialized URL-safe string to be unserialized. 313 | * @return The unserialized string, or null when the given string is by itself null. 314 | * @throws IllegalArgumentException When the given serialized URL-safe string is not in valid format as returned by 315 | * {@link #serializeURLSafe(String)}. 316 | */ 317 | public static String unserializeURLSafe(String string) { 318 | if (string == null) { 319 | return null; 320 | } 321 | 322 | try { 323 | String base64 = string.replace('-', '+').replace('_', '/') + "===".substring(0, string.length() % 4); 324 | InputStream deflated = new ByteArrayInputStream(DatatypeConverter.parseBase64Binary(base64)); 325 | return new String(toByteArray(new InflaterInputStream(deflated)), UTF_8); 326 | } 327 | catch (UnsupportedEncodingException e) { 328 | // This will occur when UTF-8 is not supported, but this is not to be expected these days. 329 | throw new RuntimeException(e); 330 | } 331 | catch (Exception e) { 332 | // This will occur when the string is not in valid Base64 or ZLIB format. 333 | throw new IllegalArgumentException(e); 334 | } 335 | } 336 | 337 | public static long stream(InputStream input, OutputStream output) throws IOException { 338 | try (ReadableByteChannel inputChannel = Channels.newChannel(input); 339 | WritableByteChannel outputChannel = Channels.newChannel(output)) 340 | { 341 | ByteBuffer buffer = ByteBuffer.allocateDirect(10240); 342 | long size = 0; 343 | 344 | while (inputChannel.read(buffer) != -1) { 345 | buffer.flip(); 346 | size += outputChannel.write(buffer); 347 | buffer.clear(); 348 | } 349 | 350 | return size; 351 | } 352 | } 353 | 354 | /** 355 | * Read the given input stream into a byte array. The given input stream will implicitly be closed after streaming, 356 | * regardless of whether an exception is been thrown or not. 357 | * @param input The input stream. 358 | * @return The input stream as a byte array. 359 | * @throws IOException When an I/O error occurs. 360 | */ 361 | public static byte[] toByteArray(InputStream input) throws IOException { 362 | ByteArrayOutputStream output = new ByteArrayOutputStream(); 363 | stream(input, output); 364 | return output.toByteArray(); 365 | } 366 | 367 | } 368 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/authmodules/BasicAuthModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.authmodules; 14 | 15 | import static javax.xml.bind.DatatypeConverter.parseBase64Binary; 16 | import static org.omnifaces.security.cdi.Beans.getReferenceOrNull; 17 | import static org.omnifaces.security.jaspic.Utils.isEmpty; 18 | 19 | import javax.security.auth.message.AuthException; 20 | import javax.security.auth.message.AuthStatus; 21 | import javax.servlet.http.HttpServletRequest; 22 | import javax.servlet.http.HttpServletResponse; 23 | 24 | import org.omnifaces.security.jaspic.core.HttpMsgContext; 25 | import org.omnifaces.security.jaspic.core.HttpServerAuthModule; 26 | import org.omnifaces.security.jaspic.user.UsernamePasswordAuthenticator; 27 | 28 | /** 29 | * Authentication module that authenticates using basic authentication 30 | * 31 | *

32 | * Token to username/roles mapping is delegated to an implementation of {@link UsernamePasswordAuthenticator}, which 33 | * should be registered as CDI bean. 34 | * 35 | *

36 | * NOTE: This module makes the simplifying assumption that CDI is available in a SAM. Unfortunately 37 | * this is not true for every implementation. See https://java.net/jira/browse/JASPIC_SPEC-14 38 | * 39 | * @author Arjan Tijms 40 | * 41 | */ 42 | public class BasicAuthModule extends HttpServerAuthModule { 43 | 44 | 45 | @Override 46 | public AuthStatus validateHttpRequest(HttpServletRequest request, HttpServletResponse response, HttpMsgContext httpMsgContext) throws AuthException { 47 | 48 | String[] credentials = getCredentials(request); 49 | if (!isEmpty(credentials)) { 50 | 51 | UsernamePasswordAuthenticator identityStore = getReferenceOrNull(UsernamePasswordAuthenticator.class); 52 | if (identityStore != null) { 53 | if (identityStore.authenticate(credentials[0], credentials[1])) { 54 | return httpMsgContext.notifyContainerAboutLogin(identityStore.getUserName(), identityStore.getApplicationRoles()); 55 | } 56 | } 57 | } 58 | 59 | if (httpMsgContext.isProtected()) { 60 | response.setHeader("WWW-Authenticate", "Basic realm=\"test realm:\""); 61 | return httpMsgContext.responseUnAuthorized(); 62 | } 63 | 64 | return httpMsgContext.doNothing(); 65 | } 66 | 67 | private String[] getCredentials(HttpServletRequest request) { 68 | 69 | String authorizationHeader = request.getHeader("Authorization"); 70 | if (!isEmpty(authorizationHeader) && authorizationHeader.startsWith("Basic ") ) { 71 | return new String(parseBase64Binary(authorizationHeader.substring(6))).split(":"); 72 | } 73 | 74 | return null; 75 | } 76 | 77 | } -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/authmodules/OmniServerAuthModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.authmodules; 14 | 15 | import static javax.security.auth.message.AuthStatus.SEND_FAILURE; 16 | import static javax.security.auth.message.AuthStatus.SUCCESS; 17 | import static org.omnifaces.security.cdi.Beans.getReferenceOrNull; 18 | import static org.omnifaces.security.jaspic.Utils.notNull; 19 | import static org.omnifaces.security.jaspic.authmodules.OmniServerAuthModule.LoginResult.LOGIN_FAILURE; 20 | import static org.omnifaces.security.jaspic.authmodules.OmniServerAuthModule.LoginResult.LOGIN_SUCCESS; 21 | import static org.omnifaces.security.jaspic.authmodules.OmniServerAuthModule.LoginResult.NO_LOGIN; 22 | import static org.omnifaces.security.jaspic.core.ServiceType.AUTO_REGISTER_SESSION; 23 | import static org.omnifaces.security.jaspic.core.ServiceType.REMEMBER_ME; 24 | import static org.omnifaces.security.jaspic.core.ServiceType.SAVE_AND_REDIRECT; 25 | 26 | import javax.enterprise.inject.spi.BeanManager; 27 | import javax.security.auth.message.AuthStatus; 28 | import javax.servlet.http.HttpServletRequest; 29 | import javax.servlet.http.HttpServletResponse; 30 | 31 | import org.omnifaces.security.cdi.Beans; 32 | import org.omnifaces.security.jaspic.core.AuthParameters; 33 | import org.omnifaces.security.jaspic.core.HttpMsgContext; 34 | import org.omnifaces.security.jaspic.core.HttpServerAuthModule; 35 | import org.omnifaces.security.jaspic.core.Jaspic; 36 | import org.omnifaces.security.jaspic.core.SamServices; 37 | import org.omnifaces.security.jaspic.user.Authenticator; 38 | import org.omnifaces.security.jaspic.user.UsernameOnlyAuthenticator; 39 | import org.omnifaces.security.jaspic.user.UsernamePasswordAuthenticator; 40 | 41 | 42 | /** 43 | * The actual Server Authentication Module AKA SAM. This SAM is designed to work specifically with a user space 44 | * user name/password {@link Authenticator} that is obtained via a CDI bean manager lookup. 45 | *

46 | * 47 | * Authentication is triggered by the presence of request attributes {@link Jaspic#AUTH_PARAMS} with a non-null username and password 48 | *

49 | * 50 | * The intended usage of this SAM is via a regular JSF user name/password form backed by a bean that sets the above mentioned 51 | * request attributes and then calls {@link HttpServletRequest#authenticate(HttpServletResponse)}. This project provides 52 | * {@link Jaspic#authenticate()} as a convenience shortcut for that. 53 | * 54 | */ 55 | @SamServices({AUTO_REGISTER_SESSION, SAVE_AND_REDIRECT, REMEMBER_ME}) 56 | public class OmniServerAuthModule extends HttpServerAuthModule { 57 | 58 | static enum LoginResult { 59 | LOGIN_SUCCESS, 60 | LOGIN_FAILURE, 61 | NO_LOGIN 62 | } 63 | 64 | @Override 65 | public AuthStatus validateHttpRequest(HttpServletRequest request, HttpServletResponse response, HttpMsgContext httpMsgContext) { 66 | 67 | // Check to see if this is a request from user code to login 68 | // 69 | // In the case of this SAM, it means a managed bean or the Filter method has called request#authenticate (via Jaspic#authenticate) 70 | switch (isLoginRequest(request, response, httpMsgContext)) { 71 | 72 | case LOGIN_SUCCESS: 73 | return SUCCESS; 74 | 75 | case LOGIN_FAILURE: 76 | 77 | // End request processing and don't try to process the handler 78 | // 79 | // Note: Most JASPIC implementations don't distinguish between return codes and only check if return is SUCCESS or not 80 | // Note: In the case of this SAM, login is called following a request#authenticate only, so in that case a non-SUCCESS 81 | // return only means not to process the handler. 82 | return SEND_FAILURE; 83 | 84 | case NO_LOGIN: 85 | // Do nothing (officially we need to execute the unauthenticated user protocol here, but just doing nothing 86 | // typically works as well, additionally for JBoss this is even better as it remembers the unauthenticated 87 | // user :|) 88 | break; 89 | } 90 | 91 | // No login request and no protected resource. Just continue. 92 | return SUCCESS; 93 | } 94 | 95 | private LoginResult isLoginRequest(HttpServletRequest request, HttpServletResponse response, HttpMsgContext httpMsgContext) { 96 | Delegators delegators = tryGetDelegators(); 97 | 98 | // This SAM is supposed to work following a call to HttpServletRequest#authenticate. Such call is in-context of the component executing it, 99 | // which *should* have the correct CDI contexts active to obtain our CDI delegators. 100 | // 101 | // In case this SAM is triggered at the very beginning of a request (which is before even the first Servlet Filter kicks in), those CDI 102 | // contexts are typically not (fully) available. 103 | if (delegators != null) { 104 | 105 | UsernamePasswordAuthenticator usernamePasswordAuthenticator = delegators.getAuthenticator(); 106 | UsernameOnlyAuthenticator usernameOnlyAuthenticator = delegators.getUsernameOnlyAuthenticator(); 107 | 108 | Authenticator authenticator = null; 109 | boolean authenticated = false; 110 | 111 | AuthParameters authParameters = httpMsgContext.getAuthParameters(); 112 | 113 | if (notNull(authParameters.getUsername(), authParameters.getPassword())) { 114 | authenticated = usernamePasswordAuthenticator.authenticate(authParameters.getUsername(), authParameters.getPassword()); 115 | authenticator = usernamePasswordAuthenticator; 116 | } else if (notNull(usernameOnlyAuthenticator, authParameters.getUsername()) && authParameters.getNoPassword()) { 117 | authenticated = usernameOnlyAuthenticator.authenticateWithoutPassword(authParameters.getUsername()); 118 | authenticator = usernameOnlyAuthenticator; 119 | } else { 120 | return NO_LOGIN; 121 | } 122 | 123 | if (authenticated) { 124 | 125 | httpMsgContext.registerWithContainer(authenticator.getUserName(), authenticator.getApplicationRoles()); 126 | 127 | return LOGIN_SUCCESS; 128 | } else { 129 | return LOGIN_FAILURE; 130 | } 131 | 132 | } 133 | 134 | return NO_LOGIN; 135 | } 136 | 137 | private Delegators tryGetDelegators() { 138 | try { 139 | BeanManager beanManager = Beans.getBeanManager(); 140 | 141 | return new Delegators( 142 | getReferenceOrNull(UsernamePasswordAuthenticator.class, beanManager), 143 | getReferenceOrNull(UsernameOnlyAuthenticator.class, beanManager) 144 | ); 145 | } catch (Exception e) { 146 | return null; 147 | } 148 | } 149 | 150 | private static class Delegators { 151 | 152 | private final UsernamePasswordAuthenticator authenticator; 153 | private final UsernameOnlyAuthenticator usernameOnlyAuthenticator; 154 | 155 | public Delegators(UsernamePasswordAuthenticator authenticator, UsernameOnlyAuthenticator usernameOnlyAuthenticator) { 156 | this.authenticator = authenticator; 157 | this.usernameOnlyAuthenticator = usernameOnlyAuthenticator; 158 | } 159 | 160 | public UsernamePasswordAuthenticator getAuthenticator() { 161 | return authenticator; 162 | } 163 | 164 | public UsernameOnlyAuthenticator getUsernameOnlyAuthenticator() { 165 | return usernameOnlyAuthenticator; 166 | } 167 | } 168 | 169 | } -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/authmodules/TokenAuthModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.authmodules; 14 | 15 | import static java.util.regex.Pattern.compile; 16 | import static org.omnifaces.security.cdi.Beans.getReferenceOrNull; 17 | import static org.omnifaces.security.jaspic.Utils.isEmpty; 18 | 19 | import java.util.regex.Matcher; 20 | import java.util.regex.Pattern; 21 | 22 | import javax.security.auth.message.AuthException; 23 | import javax.security.auth.message.AuthStatus; 24 | import javax.servlet.http.HttpServletRequest; 25 | import javax.servlet.http.HttpServletResponse; 26 | 27 | import org.omnifaces.security.jaspic.core.HttpMsgContext; 28 | import org.omnifaces.security.jaspic.core.HttpServerAuthModule; 29 | import org.omnifaces.security.jaspic.user.TokenAuthenticator; 30 | 31 | /** 32 | * Authentication module that authenticates based on a token in the request. 33 | * 34 | *

35 | * Token to username/roles mapping is delegated to an implementation of {@link TokenAuthenticator}, which 36 | * should be registered as CDI bean. 37 | * 38 | *

39 | * NOTE: This module makes the simplifying assumption that CDI is available in a SAM. Unfortunately 40 | * this is not true for every implementation. See https://java.net/jira/browse/JASPIC_SPEC-14 41 | * 42 | * @author Arjan Tijms 43 | * 44 | */ 45 | public class TokenAuthModule extends HttpServerAuthModule { 46 | 47 | private final static Pattern tokenPattern = compile("OmniLogin\\s+auth\\s*=\\s*(.*)"); 48 | 49 | @Override 50 | public AuthStatus validateHttpRequest(HttpServletRequest request, HttpServletResponse response, HttpMsgContext httpMsgContext) throws AuthException { 51 | 52 | String token = getToken(request); 53 | if (!isEmpty(token)) { 54 | 55 | // If a token is present, authenticate with it whether this is strictly required or not. 56 | 57 | TokenAuthenticator tokenAuthenticator = getReferenceOrNull(TokenAuthenticator.class); 58 | if (tokenAuthenticator != null) { 59 | 60 | if (tokenAuthenticator.authenticate(token)) { 61 | return httpMsgContext.notifyContainerAboutLogin(tokenAuthenticator.getUserName(), tokenAuthenticator.getApplicationRoles()); 62 | } 63 | } 64 | } 65 | 66 | if (httpMsgContext.isProtected()) { 67 | return httpMsgContext.responseNotFound(); 68 | } 69 | 70 | return httpMsgContext.doNothing(); 71 | } 72 | 73 | private String getToken(HttpServletRequest request) { 74 | 75 | String authorizationHeader = request.getHeader("Authorization"); 76 | if (!isEmpty(authorizationHeader)) { 77 | 78 | Matcher tokenMatcher = tokenPattern.matcher(authorizationHeader); 79 | 80 | if (tokenMatcher.matches()) { 81 | return tokenMatcher.group(1); 82 | } 83 | } 84 | 85 | return null; 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/config/AuthStacks.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.config; 14 | 15 | import java.util.LinkedHashMap; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | public class AuthStacks { 20 | 21 | private String defaultStackName; 22 | private Map> moduleStacks = new LinkedHashMap<>(); 23 | private boolean logAuthExceptions = true; 24 | 25 | public List getDefaultStack() { 26 | return moduleStacks.get(defaultStackName); 27 | } 28 | 29 | public String getDefaultStackName() { 30 | return defaultStackName; 31 | } 32 | 33 | public void setDefaultStackName(String defaultStackName) { 34 | this.defaultStackName = defaultStackName; 35 | } 36 | 37 | public Map> getModuleStacks() { 38 | return moduleStacks; 39 | } 40 | 41 | public void setModuleStacks(Map> moduleStacks) { 42 | this.moduleStacks = moduleStacks; 43 | } 44 | 45 | public boolean isLogAuthExceptions() { 46 | return logAuthExceptions; 47 | } 48 | 49 | public void setLogAuthExceptions(boolean logAuthExceptions) { 50 | this.logAuthExceptions = logAuthExceptions; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/config/AuthStacksBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.config; 14 | 15 | import static java.util.Arrays.asList; 16 | import static org.omnifaces.security.jaspic.core.ServiceType.AUTO_REGISTER_SESSION; 17 | import static org.omnifaces.security.jaspic.core.ServiceType.REMEMBER_ME; 18 | import static org.omnifaces.security.jaspic.core.ServiceType.SAVE_AND_REDIRECT; 19 | 20 | import java.util.ArrayList; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.UUID; 25 | 26 | import javax.security.auth.message.module.ServerAuthModule; 27 | 28 | import org.omnifaces.security.jaspic.core.SamServices; 29 | import org.omnifaces.security.jaspic.core.ServiceType; 30 | import org.omnifaces.security.jaspic.wrappers.AutoRegisterSessionWrapper; 31 | import org.omnifaces.security.jaspic.wrappers.RememberMeWrapper; 32 | import org.omnifaces.security.jaspic.wrappers.SaveAndRedirectWrapper; 33 | 34 | public class AuthStacksBuilder { 35 | 36 | private boolean logAuthExceptions = true; 37 | private AuthStacks authStacks = new AuthStacks(); 38 | 39 | public StackBuilder stack() { 40 | return new StackBuilder(); 41 | } 42 | 43 | public AuthStacksBuilder logAuthExceptions(boolean logAuthExceptions) { 44 | this.logAuthExceptions = logAuthExceptions; 45 | return this; 46 | } 47 | 48 | public AuthStacks build() { 49 | // If there's no default, take first. 50 | if (authStacks.getDefaultStackName() == null && authStacks.getModuleStacks().size() > 0) { 51 | authStacks.setDefaultStackName(authStacks.getModuleStacks().keySet().iterator().next()); 52 | } 53 | 54 | authStacks.setLogAuthExceptions(logAuthExceptions); 55 | 56 | return authStacks; 57 | } 58 | 59 | public class StackBuilder { 60 | 61 | String name; 62 | boolean isDefault; 63 | List modules = new ArrayList<>(); 64 | 65 | public StackBuilder name(String name) { 66 | this.name = name; 67 | return this; 68 | } 69 | 70 | public StackBuilder setDefault() { 71 | isDefault = true; 72 | return this; 73 | } 74 | 75 | public ModuleBuilder module() { 76 | return new ModuleBuilder(); 77 | } 78 | 79 | public AuthStacksBuilder add() { 80 | if (name == null) { 81 | name = UUID.randomUUID().toString(); 82 | } 83 | if (isDefault) { 84 | authStacks.setDefaultStackName(name); 85 | } 86 | authStacks.getModuleStacks().put(name, modules); 87 | return AuthStacksBuilder.this; 88 | } 89 | 90 | public class ModuleBuilder { 91 | 92 | private Module module = new Module(); 93 | private Map options = new HashMap(); 94 | 95 | public ModuleBuilder serverAuthModule(ServerAuthModule serverAuthModule) { 96 | 97 | ServerAuthModule wrappedServerAuthModule = serverAuthModule; 98 | 99 | if (serverAuthModule.getClass().isAnnotationPresent(SamServices.class)) { 100 | List types = asList(serverAuthModule.getClass().getAnnotation(SamServices.class).value()); 101 | 102 | if (types.contains(SAVE_AND_REDIRECT)) { 103 | wrappedServerAuthModule = new SaveAndRedirectWrapper(wrappedServerAuthModule); 104 | } 105 | 106 | if (types.contains(REMEMBER_ME)) { 107 | wrappedServerAuthModule = new RememberMeWrapper(wrappedServerAuthModule); 108 | } 109 | 110 | if (types.contains(AUTO_REGISTER_SESSION)) { 111 | wrappedServerAuthModule = new AutoRegisterSessionWrapper(wrappedServerAuthModule); 112 | } 113 | } 114 | 115 | module.setServerAuthModule(wrappedServerAuthModule); 116 | return this; 117 | } 118 | 119 | public ModuleBuilder controlFlag(ControlFlag controlFlag) { 120 | module.setControlFlag(controlFlag); 121 | return this; 122 | } 123 | 124 | public ModuleBuilder options(Map options) { 125 | this.options.putAll(options); 126 | return this; 127 | } 128 | 129 | public ModuleBuilder option(String key, String value) { 130 | options.put(key, value); 131 | return this; 132 | } 133 | 134 | public StackBuilder add() { 135 | module.setOptions(options); 136 | modules.add(module); 137 | return StackBuilder.this; 138 | } 139 | } 140 | } 141 | } -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/config/ControlFlag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.config; 14 | 15 | public enum ControlFlag { 16 | 17 | REQUIRED, 18 | REQUISITE, 19 | SUFFICIENT, 20 | OPTIONAL 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/config/Module.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.config; 14 | 15 | import java.util.Map; 16 | 17 | import javax.security.auth.message.module.ServerAuthModule; 18 | 19 | public class Module { 20 | 21 | private ServerAuthModule serverAuthModule; 22 | private ControlFlag controlFlag; 23 | private Map options; 24 | 25 | public Map getOptions() { 26 | return options; 27 | } 28 | 29 | public void setOptions(Map options) { 30 | this.options = options; 31 | } 32 | 33 | public ServerAuthModule getServerAuthModule() { 34 | return serverAuthModule; 35 | } 36 | 37 | public void setServerAuthModule(ServerAuthModule serverAuthModule) { 38 | this.serverAuthModule = serverAuthModule; 39 | } 40 | 41 | public ControlFlag getControlFlag() { 42 | return controlFlag; 43 | } 44 | 45 | public void setControlFlag(ControlFlag controlFlag) { 46 | this.controlFlag = controlFlag; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/core/AuthParameters.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.core; 14 | 15 | /** 16 | * Parameters that are provided along with an authentication request. 17 | * 18 | * @author Arjan Tijms 19 | * 20 | */ 21 | public class AuthParameters { 22 | 23 | private String username; 24 | private String password; 25 | private Boolean rememberMe; 26 | private Boolean noPassword; 27 | private String authMethod; 28 | 29 | private String redirectUrl; 30 | 31 | public AuthParameters username(String username) { 32 | setUsername(username); 33 | return this; 34 | } 35 | 36 | public AuthParameters password(String passWord) { 37 | setPassword(passWord); 38 | return this; 39 | } 40 | 41 | public AuthParameters rememberMe(boolean rememberMe) { 42 | setRememberMe(rememberMe); 43 | return this; 44 | } 45 | 46 | public AuthParameters noPassword(boolean noPassword) { 47 | setNoPassword(noPassword); 48 | return this; 49 | } 50 | 51 | public AuthParameters authMethod(String authMethod) { 52 | setAuthMethod(authMethod); 53 | return this; 54 | } 55 | 56 | public AuthParameters redirectUrl(String redirectUrl) { 57 | setRedirectUrl(redirectUrl); 58 | return this; 59 | } 60 | 61 | // Getters/setters 62 | 63 | public String getUsername() { 64 | return username; 65 | } 66 | 67 | public void setUsername(String username) { 68 | this.username = username; 69 | } 70 | 71 | public String getPassword() { 72 | return password; 73 | } 74 | 75 | public void setPassword(String password) { 76 | this.password = password; 77 | } 78 | 79 | public Boolean getRememberMe() { 80 | return rememberMe; 81 | } 82 | 83 | public void setRememberMe(Boolean rememberMe) { 84 | this.rememberMe = rememberMe; 85 | } 86 | 87 | public String getAuthMethod() { 88 | return authMethod; 89 | } 90 | 91 | public void setAuthMethod(String authMethod) { 92 | this.authMethod = authMethod; 93 | } 94 | 95 | public String getRedirectUrl() { 96 | return redirectUrl; 97 | } 98 | 99 | public void setRedirectUrl(String redirectUrl) { 100 | this.redirectUrl = redirectUrl; 101 | } 102 | 103 | public Boolean getNoPassword() { 104 | return noPassword; 105 | } 106 | 107 | public void setNoPassword(Boolean noPassword) { 108 | this.noPassword = noPassword; 109 | } 110 | 111 | } -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/core/AuthResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.core; 14 | 15 | 16 | import static java.util.logging.Level.SEVERE; 17 | import static javax.security.auth.message.AuthStatus.SEND_FAILURE; 18 | 19 | import java.util.logging.Logger; 20 | 21 | import javax.security.auth.message.AuthException; 22 | import javax.security.auth.message.AuthStatus; 23 | 24 | public class AuthResult { 25 | 26 | private static final Logger logger = Logger.getLogger(AuthResult.class.getName()); 27 | 28 | private AuthStatus authStatus = SEND_FAILURE; 29 | private Exception exception; 30 | private boolean logAuthExceptions = true; 31 | 32 | public AuthResult() {} 33 | 34 | public AuthResult(boolean logAuthExceptions) { 35 | this.logAuthExceptions = logAuthExceptions; 36 | } 37 | 38 | public AuthStatus getAuthStatus() { 39 | return authStatus; 40 | } 41 | 42 | public void setAuthStatus(AuthStatus authStatus) { 43 | this.authStatus = authStatus; 44 | } 45 | 46 | public Exception getException() { 47 | return exception; 48 | } 49 | 50 | public void setException(Exception exception) { 51 | this.exception = exception; 52 | } 53 | 54 | public boolean isFailed() { 55 | return authStatus == SEND_FAILURE; 56 | } 57 | 58 | public void add(AuthResult authResult) { 59 | if (!authResult.isFailed()) { 60 | authStatus = authResult.getAuthStatus(); 61 | } else if (authResult.getException() != null) { 62 | if (exception == null) { 63 | exception = authResult.getException(); 64 | } else { 65 | exception.addSuppressed(authResult.getException()); 66 | } 67 | } 68 | } 69 | 70 | public AuthStatus throwOrReturnStatus() throws AuthException { 71 | maybeThrow(); 72 | return authStatus; 73 | } 74 | 75 | public AuthStatus throwOrFail() throws AuthException { 76 | maybeThrow(); 77 | return SEND_FAILURE; 78 | } 79 | 80 | private void maybeThrow() throws AuthException { 81 | if (exception != null) { 82 | if (logAuthExceptions) { 83 | logger.log(SEVERE, "Exception occured when processing authentication", exception); 84 | } 85 | throw (AuthException) new AuthException().initCause(exception); 86 | } 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/core/HttpMsgContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.core; 14 | 15 | import static java.util.Collections.emptyMap; 16 | import static java.util.Collections.unmodifiableMap; 17 | import static javax.security.auth.message.AuthStatus.SEND_FAILURE; 18 | import static javax.security.auth.message.AuthStatus.SUCCESS; 19 | import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; 20 | import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; 21 | 22 | import java.io.IOException; 23 | import java.util.List; 24 | import java.util.Map; 25 | 26 | import javax.security.auth.Subject; 27 | import javax.security.auth.callback.CallbackHandler; 28 | import javax.security.auth.message.AuthStatus; 29 | import javax.security.auth.message.MessageInfo; 30 | import javax.security.auth.message.config.ServerAuthContext; 31 | import javax.security.auth.message.module.ServerAuthModule; 32 | import javax.servlet.http.HttpServletRequest; 33 | import javax.servlet.http.HttpServletResponse; 34 | 35 | /** 36 | * A convenience context that provides access to JASPIC Servlet Profile specific types 37 | * and functionality. 38 | * 39 | * @author Arjan Tijms 40 | * 41 | */ 42 | public class HttpMsgContext { 43 | 44 | private CallbackHandler handler; 45 | private Map moduleOptions; 46 | private MessageInfo messageInfo; 47 | private Subject clientSubject; 48 | private AuthParameters authParameters; 49 | 50 | public HttpMsgContext(CallbackHandler handler, Map moduleOptions, MessageInfo messageInfo, Subject clientSubject) { 51 | this.handler = handler; 52 | if (moduleOptions != null) { 53 | this.moduleOptions = unmodifiableMap(moduleOptions); 54 | } else { 55 | this.moduleOptions = emptyMap(); 56 | } 57 | this.messageInfo = messageInfo; 58 | this.clientSubject = clientSubject; 59 | if (messageInfo != null) { 60 | this.authParameters = Jaspic.getAuthParameters(getRequest()); 61 | } 62 | } 63 | 64 | /** 65 | * Checks if the current request is to a protected resource or not. A protected resource 66 | * is a resource (e.g. a Servlet, JSF page, JSP page etc) for which a constraint has been defined 67 | * in e.g. web.xml. 68 | * 69 | * @return true if a protected resource was requested, false if a public resource was requested. 70 | */ 71 | public boolean isProtected() { 72 | return Jaspic.isProtectedResource(messageInfo); 73 | } 74 | 75 | public boolean isAnyExplicitAuthCall() { 76 | return Jaspic.isExplicitAuthCall(getRequest()); 77 | } 78 | 79 | public boolean isAuthenticationRequest() { 80 | return Jaspic.isAuthenticationRequest(getRequest()); 81 | } 82 | 83 | /** 84 | * Asks the container to register the given username and roles in order to make 85 | * them available to the application for use with {@link HttpServletRequest#isUserInRole(String)} etc. 86 | *

87 | * This will also ask the runtime to register an authentication session that will live as long as the 88 | * HTTP session is valid. 89 | *

90 | * Note that after this call returned, the authenticated identity will not be immediately active. This 91 | * will only take place (should not errors occur) after the {@link ServerAuthContext} or {@link ServerAuthModule} 92 | * in which this call takes place return control back to the runtime. 93 | * 94 | * @param username the user name that will become the caller principal 95 | * @param roles the roles associated with the caller principal 96 | */ 97 | public void registerWithContainer(String username, List roles) { 98 | registerWithContainer(username, roles, true); 99 | } 100 | 101 | /** 102 | * Asks the container to register the given username and roles in order to make 103 | * them available to the application for use with {@link HttpServletRequest#isUserInRole(String)} etc. 104 | *

105 | * This will optionally (on the basis of the registerSession parameter) ask the runtime to register an 106 | * authentication session that will live as long as the HTTP session is valid. 107 | *

108 | * Note that after this call returned, the authenticated identity will not be immediately active. This 109 | * will only take place (should not errors occur) after the {@link ServerAuthContext} or {@link ServerAuthModule} 110 | * in which this call takes place return control back to the runtime. 111 | * 112 | * @param username the user name that will become the caller principal 113 | * @param roles the roles associated with the caller principal 114 | * @param registerSession if true asks the container to register an authentication setting, if false does not ask this. 115 | */ 116 | public void registerWithContainer(String username, List roles, boolean registerSession) { 117 | 118 | // Basic registration of the username and roles with the container 119 | notifyContainerAboutLogin(username, roles); 120 | 121 | // Explicitly set a flag that we did authentication, so code can check that this happened 122 | Jaspic.setDidAuthentication((HttpServletRequest) messageInfo.getRequestMessage()); 123 | 124 | if (registerSession) { 125 | Jaspic.setRegisterSession(messageInfo, username, roles); 126 | } 127 | } 128 | 129 | /** 130 | * Checks if during the current request code has asked the runtime to register an authentication session. 131 | * 132 | * @return true if code has asked to register an authentication session, false otherwise. 133 | */ 134 | public boolean isRegisterSession() { 135 | return Jaspic.isRegisterSession(messageInfo); 136 | } 137 | 138 | /** 139 | * Asks the runtime to register an authentication session. This will automatically remember the logged-in status 140 | * as long as the current HTTP session remains valid. Without this being asked, a SAM has to manually re-authenticate 141 | * with the runtime at the start of each request. 142 | *

143 | * Note that the user name and roles being asked is an implementation detail; there is no portable way to have 144 | * an auth context read back the user name and roles that were processed by the {@link CallbackHandler}. 145 | * 146 | * @param username the user name for which authentication should be be remembered 147 | * @param roles the roles for which authentication should be remembered. 148 | */ 149 | public void setRegisterSession(String username, List roles) { 150 | Jaspic.setRegisterSession(messageInfo, username, roles); 151 | } 152 | 153 | public void cleanClientSubject() { 154 | Jaspic.cleanSubject(clientSubject); 155 | } 156 | 157 | /** 158 | * Returns the parameters that were provided with the {@link Jaspic#authenticate(AuthParameters)} call. 159 | * 160 | * @return the parameters that were provided with the {@link Jaspic#authenticate(AuthParameters)} call, or a default instance. Never null. 161 | */ 162 | public AuthParameters getAuthParameters() { 163 | return authParameters; 164 | } 165 | 166 | /** 167 | * Returns the handler that the runtime provided to auth context. 168 | * 169 | * @return the handler that the runtime provided to auth context. 170 | */ 171 | public CallbackHandler getHandler() { 172 | return handler; 173 | } 174 | 175 | /** 176 | * Returns the module options that were set on the auth module to which this context belongs. 177 | * 178 | * @return the module options that were set on the auth module to which this context belongs. 179 | */ 180 | public Map getModuleOptions() { 181 | return moduleOptions; 182 | } 183 | 184 | /** 185 | * Returns the named module option that was set on the auth module to which this context belongs. 186 | * 187 | * @return the named module option that was set on the auth module to which this context belongs, or null if no option with that name was set. 188 | */ 189 | public String getModuleOption(String key) { 190 | return moduleOptions.get(key); 191 | } 192 | 193 | /** 194 | * Returns the message info instance for the current request. 195 | * 196 | * @return the message info instance for the current request. 197 | */ 198 | public MessageInfo getMessageInfo() { 199 | return messageInfo; 200 | } 201 | 202 | /** 203 | * Returns the subject for which authentication is to take place. 204 | * 205 | * @return the subject for which authentication is to take place. 206 | */ 207 | public Subject getClientSubject() { 208 | return clientSubject; 209 | } 210 | 211 | /** 212 | * Returns the request object associated with the current request. 213 | * 214 | * @return the request object associated with the current request. 215 | */ 216 | public HttpServletRequest getRequest() { 217 | return (HttpServletRequest) messageInfo.getRequestMessage(); 218 | } 219 | 220 | /** 221 | * Returns the response object associated with the current request. 222 | * 223 | * @return the response object associated with the current request. 224 | */ 225 | public HttpServletResponse getResponse() { 226 | return (HttpServletResponse) messageInfo.getResponseMessage(); 227 | } 228 | 229 | /** 230 | * Sets the response status to 401 (not found). 231 | *

232 | * As a convenience this method returns SEND_FAILURE, so this method can be used in 233 | * one fluent return statement from an auth module. 234 | * 235 | * @return {@link AuthStatus#SEND_FAILURE} 236 | */ 237 | public AuthStatus responseUnAuthorized() { 238 | try { 239 | getResponse().sendError(SC_UNAUTHORIZED); 240 | } catch (IOException e) { 241 | throw new IllegalStateException(e); 242 | } 243 | 244 | return SEND_FAILURE; 245 | } 246 | 247 | /** 248 | * Sets the response status to 404 (not found). 249 | *

250 | * As a convenience this method returns SEND_FAILURE, so this method can be used in 251 | * one fluent return statement from an auth module. 252 | * 253 | * @return {@link AuthStatus#SEND_FAILURE} 254 | */ 255 | public AuthStatus responseNotFound() { 256 | try { 257 | getResponse().sendError(SC_NOT_FOUND); 258 | } catch (IOException e) { 259 | throw new IllegalStateException(e); 260 | } 261 | 262 | return SEND_FAILURE; 263 | } 264 | 265 | /** 266 | * Asks the container to register the given username and roles in order to make 267 | * them available to the application for use with {@link HttpServletRequest#isUserInRole(String)} etc. 268 | * 269 | *

270 | * Note that after this call returned, the authenticated identity will not be immediately active. This 271 | * will only take place (should not errors occur) after the {@link ServerAuthContext} or {@link ServerAuthModule} 272 | * in which this call takes place return control back to the runtime. 273 | * 274 | *

275 | * As a convenience this method returns SUCCESS, so this method can be used in 276 | * one fluent return statement from an auth module. 277 | * 278 | * @param username the user name that will become the caller principal 279 | * @param roles the roles associated with the caller principal 280 | * @return {@link AuthStatus#SUCCESS} 281 | * 282 | */ 283 | public AuthStatus notifyContainerAboutLogin(String username, List roles) { 284 | Jaspic.notifyContainerAboutLogin(clientSubject, handler, username, roles); 285 | 286 | return SUCCESS; 287 | } 288 | 289 | /** 290 | * Instructs the container to "do nothing". 291 | * 292 | *

293 | * This is a somewhat peculiar requirement of JASPIC, which incidentally almost no containers actually require 294 | * or enforce. 295 | * 296 | *

297 | * When intending to do nothing, most JASPIC auth modules simply return "SUCCESS", but according to 298 | * the JASPIC spec the handler MUST have been used when returning that status. Because of this JASPIC 299 | * implicitly defines a "protocol" that must be followed in this case; 300 | * invoking the CallerPrincipalCallback handler with a null as the username. 301 | * 302 | *

303 | * As a convenience this method returns SUCCESS, so this method can be used in 304 | * one fluent return statement from an auth module. 305 | * 306 | * @return {@link AuthStatus#SUCCESS} 307 | */ 308 | public AuthStatus doNothing() { 309 | Jaspic.notifyContainerAboutLogin(clientSubject, handler, null, null); 310 | 311 | return SUCCESS; 312 | } 313 | 314 | } -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/core/HttpServerAuthModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.core; 14 | 15 | import static javax.security.auth.message.AuthStatus.SEND_SUCCESS; 16 | 17 | import java.util.Map; 18 | 19 | import javax.security.auth.Subject; 20 | import javax.security.auth.callback.CallbackHandler; 21 | import javax.security.auth.message.AuthException; 22 | import javax.security.auth.message.AuthStatus; 23 | import javax.security.auth.message.MessageInfo; 24 | import javax.security.auth.message.MessagePolicy; 25 | import javax.security.auth.message.config.ServerAuthContext; 26 | import javax.security.auth.message.module.ServerAuthModule; 27 | import javax.servlet.http.HttpServletRequest; 28 | import javax.servlet.http.HttpServletResponse; 29 | 30 | /** 31 | * A server authentication module (SAM) implementation base class, tailored for the Servlet Container Profile. 32 | * 33 | * @author Arjan Tijms 34 | * 35 | */ 36 | public abstract class HttpServerAuthModule implements ServerAuthModule { 37 | 38 | private CallbackHandler handler; 39 | private Map options; 40 | private final Class[] supportedMessageTypes = new Class[] { HttpServletRequest.class, HttpServletResponse.class }; 41 | 42 | @Override 43 | @SuppressWarnings("unchecked") 44 | public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, @SuppressWarnings("rawtypes") Map options) throws AuthException { 45 | this.handler = handler; 46 | this.options = options; 47 | initializeModule(new HttpMsgContext(handler, options, null, null)); 48 | } 49 | 50 | /** 51 | * A Servlet Container Profile compliant implementation should return HttpServletRequest and HttpServletResponse, so 52 | * the delegation class {@link ServerAuthContext} can choose the right SAM to delegate to. 53 | */ 54 | @Override 55 | public Class[] getSupportedMessageTypes() { 56 | return supportedMessageTypes; 57 | } 58 | 59 | @Override 60 | public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { 61 | HttpMsgContext msgContext = new HttpMsgContext(handler, options, messageInfo, clientSubject); 62 | return validateHttpRequest(msgContext.getRequest(), msgContext.getResponse(), msgContext); 63 | } 64 | 65 | @Override 66 | public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException { 67 | return SEND_SUCCESS; 68 | } 69 | 70 | /** 71 | * Called in response to a {@link HttpServletRequest#logout()} call. 72 | * 73 | */ 74 | @Override 75 | public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { 76 | HttpMsgContext msgContext = new HttpMsgContext(handler, options, messageInfo, subject); 77 | cleanHttpSubject(msgContext.getRequest(), msgContext.getResponse(), msgContext); 78 | } 79 | 80 | public void initializeModule(HttpMsgContext httpMsgContext) { 81 | 82 | } 83 | 84 | public AuthStatus validateHttpRequest(HttpServletRequest request, HttpServletResponse response, HttpMsgContext httpMsgContext) throws AuthException { 85 | throw new IllegalStateException("Not implemented"); 86 | } 87 | 88 | public void cleanHttpSubject(HttpServletRequest request, HttpServletResponse response, HttpMsgContext httpMsgContext) { 89 | httpMsgContext.cleanClientSubject(); 90 | } 91 | 92 | } -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/core/Jaspic.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.core; 14 | 15 | import static java.lang.Boolean.TRUE; 16 | import static javax.security.auth.message.AuthStatus.SUCCESS; 17 | import static org.omnifaces.security.jaspic.Utils.isEmpty; 18 | import static org.omnifaces.security.jaspic.Utils.isOneOf; 19 | import static org.omnifaces.security.jaspic.config.ControlFlag.REQUIRED; 20 | 21 | import java.io.IOException; 22 | import java.util.List; 23 | 24 | import javax.faces.context.FacesContext; 25 | import javax.security.auth.Subject; 26 | import javax.security.auth.callback.Callback; 27 | import javax.security.auth.callback.CallbackHandler; 28 | import javax.security.auth.callback.UnsupportedCallbackException; 29 | import javax.security.auth.message.AuthStatus; 30 | import javax.security.auth.message.MessageInfo; 31 | import javax.security.auth.message.callback.CallerPrincipalCallback; 32 | import javax.security.auth.message.callback.GroupPrincipalCallback; 33 | import javax.security.auth.message.config.AuthConfigFactory; 34 | import javax.security.auth.message.module.ServerAuthModule; 35 | import javax.servlet.ServletContext; 36 | import javax.servlet.ServletException; 37 | import javax.servlet.http.HttpServletRequest; 38 | import javax.servlet.http.HttpServletResponse; 39 | 40 | import org.omnifaces.security.jaspic.config.AuthStacks; 41 | import org.omnifaces.security.jaspic.config.AuthStacksBuilder; 42 | import org.omnifaces.security.jaspic.factory.OmniAuthConfigProvider; 43 | 44 | /** 45 | * A set of utility methods for using the JASPIC API, specially in combination with 46 | * the OmniServerAuthModule. 47 | *

48 | * Note that this contains various methods that assume being called from a JSF context. 49 | * 50 | * @author Arjan Tijms 51 | * 52 | */ 53 | public final class Jaspic { 54 | 55 | public static final String IS_AUTHENTICATION = "org.omnifaces.security.message.request.authentication"; 56 | public static final String IS_AUTHENTICATION_FROM_FILTER = "org.omnifaces.security.message.request.authenticationFromFilter"; 57 | public static final String IS_SECURE_RESPONSE = "org.omnifaces.security.message.request.secureResponse"; 58 | public static final String IS_REFRESH = "org.omnifaces.security.message.request.isRefresh"; 59 | public static final String DID_AUTHENTICATION = "org.omnifaces.security.message.request.didAuthentication"; 60 | 61 | public static final String AUTH_PARAMS = "org.omnifaces.security.message.request.authParams"; 62 | 63 | public static final String LOGGEDIN_USERNAME = "org.omnifaces.security.message.loggedin.username"; 64 | public static final String LOGGEDIN_ROLES = "org.omnifaces.security.message.loggedin.roles"; 65 | public static final String LAST_AUTH_STATUS = "org.omnifaces.security.message.authStatus"; 66 | 67 | public static final String CONTEXT_REGISTRATION_ID = "org.omnifaces.security.message.registrationId"; 68 | 69 | // Key in the MessageInfo Map that when present AND set to true indicated a protected resource is being accessed. 70 | // When the resource is not protected, GlassFish omits the key altogether. WebSphere does insert the key and sets 71 | // it to false. 72 | private static final String IS_MANDATORY = "javax.security.auth.message.MessagePolicy.isMandatory"; 73 | private static final String REGISTER_SESSION = "javax.servlet.http.registerSession"; 74 | 75 | private Jaspic() {} 76 | 77 | public static boolean authenticate(HttpServletRequest request, HttpServletResponse response, AuthParameters authParameters) { 78 | try { 79 | request.setAttribute(IS_AUTHENTICATION, true); 80 | if (authParameters != null) { 81 | request.setAttribute(AUTH_PARAMS, authParameters); 82 | } 83 | return request.authenticate(response); 84 | } catch (ServletException | IOException e) { 85 | throw new IllegalArgumentException(e); 86 | } finally { 87 | request.removeAttribute(IS_AUTHENTICATION); 88 | if (authParameters != null) { 89 | request.removeAttribute(AUTH_PARAMS); 90 | } 91 | } 92 | } 93 | 94 | public static boolean authenticateFromFilter(HttpServletRequest request, HttpServletResponse response) { 95 | try { 96 | request.setAttribute(IS_AUTHENTICATION_FROM_FILTER, true); 97 | return request.authenticate(response); 98 | } catch (ServletException e) { 99 | // Really problematic case, some servers (particularly JBoss Undertow since 1.1.0) throw a 100 | // ServletException when there's isn't actually an error, but just to indicate "nothing" has happened. 101 | return false; 102 | } catch (IOException e) { 103 | throw new IllegalArgumentException(e); 104 | } finally { 105 | request.removeAttribute(IS_AUTHENTICATION_FROM_FILTER); 106 | } 107 | } 108 | 109 | public static boolean refreshAuthentication(HttpServletRequest request, HttpServletResponse response, AuthParameters authParameters) { 110 | try { 111 | request.setAttribute(IS_REFRESH, true); 112 | // Doing an explicit logout is actually not really nice, as it has some side-effects that we need to counter 113 | // (like a SAM supporting remember-me clearing its remember-me cookie, etc). But there doesn't seem to be another 114 | // way in JASPIC 115 | request.logout(); 116 | return authenticate(request, response, authParameters); 117 | } catch (ServletException e) { 118 | throw new IllegalArgumentException(e); 119 | } finally { 120 | request.removeAttribute(IS_REFRESH); 121 | } 122 | } 123 | 124 | public static AuthParameters getAuthParameters(HttpServletRequest request) { 125 | AuthParameters authParameters = (AuthParameters) request.getAttribute(AUTH_PARAMS); 126 | if (authParameters == null) { 127 | authParameters = new AuthParameters(); 128 | } 129 | 130 | return authParameters; 131 | } 132 | 133 | public static void logout(HttpServletRequest request, HttpServletResponse response) { 134 | try { 135 | request.logout(); 136 | // Need to invalidate the session to really logout - request.logout only logs the user out for the *current request* 137 | // This is nearly always unwanted. Although the SAM's cleanSubject method can clear any session data too if needed, 138 | // invalidating the session is pretty much the safest way. 139 | request.getSession().invalidate(); 140 | } catch (ServletException e) { 141 | throw new IllegalArgumentException(e); 142 | } 143 | } 144 | 145 | public static AuthResult validateRequest(ServerAuthModule serverAuthModule, MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) { 146 | 147 | AuthResult authResult = new AuthResult(); 148 | 149 | try { 150 | AuthStatus status = serverAuthModule.validateRequest(messageInfo, clientSubject, serviceSubject); 151 | 152 | // TODO: not 100% sure about this; need mechanism for wrappers to abort the chain and signal to "do nothing" 153 | // TODO: use handler for "do nothing here"? 154 | if (status == null) { 155 | status = SUCCESS; 156 | } 157 | 158 | authResult.setAuthStatus(status); 159 | } catch (Exception exception) { 160 | authResult.setException(exception); 161 | } 162 | 163 | return authResult; 164 | } 165 | 166 | public static void cleanSubject(Subject subject) { 167 | if (subject != null) { 168 | subject.getPrincipals().clear(); 169 | } 170 | } 171 | 172 | public static boolean isRegisterSession(MessageInfo messageInfo) { 173 | return Boolean.valueOf((String)messageInfo.getMap().get(REGISTER_SESSION)); 174 | } 175 | 176 | public static boolean isProtectedResource(MessageInfo messageInfo) { 177 | return Boolean.valueOf((String) messageInfo.getMap().get(IS_MANDATORY)); 178 | } 179 | 180 | @SuppressWarnings("unchecked") 181 | public static void setRegisterSession(MessageInfo messageInfo, String username, List roles) { 182 | messageInfo.getMap().put("javax.servlet.http.registerSession", TRUE.toString()); 183 | 184 | HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage(); 185 | request.setAttribute(LOGGEDIN_USERNAME, username); 186 | // TODO: check for existing roles and add 187 | request.setAttribute(LOGGEDIN_ROLES, roles); 188 | } 189 | 190 | public static boolean isAuthenticationRequest(HttpServletRequest request) { 191 | return TRUE.equals(request.getAttribute(IS_AUTHENTICATION)); 192 | } 193 | 194 | public static boolean isAuthenticationFromFilterRequest(HttpServletRequest request) { 195 | return TRUE.equals(request.getAttribute(IS_AUTHENTICATION_FROM_FILTER)); 196 | } 197 | 198 | public static boolean isSecureResponseRequest(HttpServletRequest request) { 199 | return TRUE.equals(request.getAttribute(IS_SECURE_RESPONSE)); 200 | } 201 | 202 | public static boolean isRefresh(HttpServletRequest request) { 203 | return TRUE.equals(request.getAttribute(IS_REFRESH)); 204 | } 205 | 206 | 207 | /** 208 | * Returns true if authorization was explicitly called for via this class (e.g. by calling {@link Jaspic#authenticate()}, 209 | * false if authorization was called automatically by the runtime at the start of the request or directly via e.g. 210 | * {@link HttpServletRequest#authenticate(HttpServletResponse)} 211 | * 212 | * @param request 213 | * @return true if authorization was initiated via this class, false otherwise 214 | */ 215 | public static boolean isExplicitAuthCall(HttpServletRequest request) { 216 | return isOneOf(TRUE, 217 | request.getAttribute(IS_AUTHENTICATION), 218 | request.getAttribute(IS_AUTHENTICATION_FROM_FILTER), 219 | request.getAttribute(IS_SECURE_RESPONSE) 220 | ); 221 | } 222 | 223 | public static void notifyContainerAboutLogin(Subject clientSubject, CallbackHandler handler, String username, List roles) { 224 | 225 | try { 226 | // 1. Create a handler (kind of directive) to add the caller principal (AKA user principal =basically user name, or user id) that 227 | // the authenticator provides. 228 | // 229 | // This will be the name of the principal returned by e.g. HttpServletRequest#getUserPrincipal 230 | // 231 | // 2 Execute the handler right away 232 | // 233 | // This will typically eventually (NOT right away) add the provided principal in an application server specific way to the JAAS 234 | // Subject. 235 | // (it could become entries in a hash table inside the subject, or individual principles, or nested group principles etc.) 236 | 237 | handler.handle(new Callback[] { new CallerPrincipalCallback(clientSubject, username) }); 238 | 239 | if (!isEmpty(roles)) { 240 | // 1. Create a handler to add the groups (AKA roles) that the authenticator provides. 241 | // 242 | // This is what e.g. HttpServletRequest#isUserInRole and @RolesAllowed for 243 | // 244 | // 2. Execute the handler right away 245 | // 246 | // This will typically eventually (NOT right away) add the provided roles in an application server specific way to the JAAS 247 | // Subject. 248 | // (it could become entries in a hash table inside the subject, or individual principles, or nested group principles etc.) 249 | 250 | handler.handle(new Callback[] { new GroupPrincipalCallback(clientSubject, roles.toArray(new String[roles.size()])) }); 251 | } 252 | 253 | } catch (IOException | UnsupportedCallbackException e) { 254 | // Should not happen 255 | throw new IllegalStateException(e); 256 | } 257 | } 258 | 259 | public static void setLastStatus(HttpServletRequest request, AuthStatus status) { 260 | request.setAttribute(LAST_AUTH_STATUS, status); 261 | } 262 | 263 | public static AuthStatus getLastStatus(HttpServletRequest request) { 264 | return (AuthStatus) request.getAttribute(LAST_AUTH_STATUS); 265 | } 266 | 267 | /** 268 | * Should be called when the callback handler is used with the intention that an actual 269 | * user is going to be authenticated (as opposed to using the handler for the "do nothing" protocol 270 | * which uses the unauthenticated identity). 271 | * 272 | */ 273 | public static void setDidAuthentication(HttpServletRequest request) { 274 | request.setAttribute(DID_AUTHENTICATION, TRUE); 275 | } 276 | 277 | /** 278 | * Returns true if a SAM has indicated that it intended authentication to be happening during 279 | * the current request. 280 | * Does not necessarily mean that authentication has indeed succeeded, for this 281 | * the actual user/caller principal should be checked as well. 282 | * 283 | */ 284 | public static boolean isDidAuthentication(HttpServletRequest request) { 285 | return TRUE.equals(request.getAttribute(DID_AUTHENTICATION)); 286 | } 287 | 288 | public static boolean isDidAuthenticationAndSucceeded(HttpServletRequest request) { 289 | return TRUE.equals(request.getAttribute(DID_AUTHENTICATION)) && request.getUserPrincipal() != null; 290 | } 291 | 292 | /** 293 | * Gets the app context ID from the servlet context. 294 | * 295 | *

296 | * The app context ID is the ID that JASPIC associates with the given application. 297 | * In this case that given application is the web application corresponding to the 298 | * ServletContext. 299 | * 300 | * @param context the servlet context for which to obtain the JASPIC app context ID 301 | * @return the app context ID for the web application corresponding to the given context 302 | */ 303 | public static String getAppContextID(ServletContext context) { 304 | return context.getVirtualServerName() + " " + context.getContextPath(); 305 | } 306 | 307 | /** 308 | * Registers a server auth module as the one and only module for the application corresponding to 309 | * the given servlet context. 310 | * 311 | *

312 | * This will override any other modules that have already been registered, either via proprietary 313 | * means or using the standard API. 314 | * 315 | * @param serverAuthModule the server auth module to be registered 316 | * @param servletContext the context of the app for which the module is registered 317 | * @return A String identifier assigned by an underlying factory corresponding to an underlying factory-factory-factory registration 318 | */ 319 | public static String registerServerAuthModule(ServerAuthModule serverAuthModule, ServletContext servletContext) { 320 | 321 | AuthStacks stacks = new AuthStacksBuilder() 322 | .stack() 323 | .name(serverAuthModule.getClass().getSimpleName()) 324 | .setDefault() 325 | .module() 326 | .serverAuthModule(serverAuthModule) 327 | .controlFlag(REQUIRED) 328 | .add() 329 | .add() 330 | .build(); 331 | 332 | // Register the factory-factory-factory for the SAM 333 | String registrationId = AuthConfigFactory.getFactory().registerConfigProvider( 334 | new OmniAuthConfigProvider(stacks), 335 | "HttpServlet", getAppContextID(servletContext), "OmniSecurity authentication config provider" 336 | ); 337 | 338 | // Remember the registration ID returned by the factory, so we can unregister the JASPIC module when the web module 339 | // is undeployed. JASPIC being the low level API that it is won't do this automatically. 340 | servletContext.setAttribute(CONTEXT_REGISTRATION_ID, registrationId); 341 | 342 | return registrationId; 343 | } 344 | 345 | /** 346 | * Deregisters the server auth module (and encompassing wrappers/factories) that was previously registered via a call 347 | * to registerServerAuthModule. 348 | * 349 | * @param servletContext the context of the app for which the module is deregistered 350 | */ 351 | public static void deregisterServerAuthModule(ServletContext servletContext) { 352 | String registrationId = (String) servletContext.getAttribute(CONTEXT_REGISTRATION_ID); 353 | if (!isEmpty(registrationId)) { 354 | AuthConfigFactory.getFactory().removeRegistration(registrationId); 355 | } 356 | } 357 | 358 | 359 | // Couple of convenience methods for usage in JSF - may remove these as its too tightly coupled 360 | 361 | public static boolean authenticate() { 362 | return authenticate(getRequest(), getResponse(), null); 363 | } 364 | 365 | public static boolean authenticate(AuthParameters authParameters) { 366 | return authenticate(getRequest(), getResponse(), authParameters); 367 | } 368 | 369 | public static boolean refreshAuthentication(AuthParameters authParameters) { 370 | return refreshAuthentication(getRequest(), getResponse(), authParameters); 371 | } 372 | 373 | public static void logout() { 374 | logout(getRequest(), getResponse()); 375 | } 376 | 377 | // End Couple of convenience methods for usage in JSF - may remove these as its too tightly coupled 378 | 379 | public static HttpServletRequest getRequest() { 380 | return (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest(); 381 | } 382 | 383 | public static HttpServletResponse getResponse() { 384 | return (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse(); 385 | } 386 | 387 | 388 | } 389 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/core/SamServices.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.core; 14 | 15 | import static java.lang.annotation.ElementType.TYPE; 16 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 17 | 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.Target; 20 | 21 | @Target(TYPE) 22 | @Retention(RUNTIME) 23 | public @interface SamServices { 24 | 25 | ServiceType[] value() default {}; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/core/ServerAuthModuleWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.core; 14 | 15 | import java.util.Map; 16 | 17 | import javax.security.auth.Subject; 18 | import javax.security.auth.callback.CallbackHandler; 19 | import javax.security.auth.message.AuthException; 20 | import javax.security.auth.message.AuthStatus; 21 | import javax.security.auth.message.MessageInfo; 22 | import javax.security.auth.message.MessagePolicy; 23 | import javax.security.auth.message.module.ServerAuthModule; 24 | 25 | public class ServerAuthModuleWrapper implements ServerAuthModule { 26 | 27 | private ServerAuthModule wrapped; 28 | 29 | public ServerAuthModuleWrapper(ServerAuthModule serverAuthModule) { 30 | this.wrapped = serverAuthModule; 31 | } 32 | 33 | @SuppressWarnings("rawtypes") 34 | @Override 35 | public Class[] getSupportedMessageTypes() { 36 | return wrapped.getSupportedMessageTypes(); 37 | } 38 | 39 | @Override 40 | public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, @SuppressWarnings("rawtypes") Map options) throws AuthException { 41 | wrapped.initialize(requestPolicy, responsePolicy, handler, options); 42 | } 43 | 44 | @Override 45 | public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { 46 | return wrapped.validateRequest(messageInfo, clientSubject, serviceSubject); 47 | } 48 | 49 | @Override 50 | public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException { 51 | return wrapped.secureResponse(messageInfo, serviceSubject); 52 | } 53 | 54 | @Override 55 | public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { 56 | wrapped.cleanSubject(messageInfo, subject); 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/core/ServiceType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.core; 14 | 15 | public enum ServiceType { 16 | AUTO_REGISTER_SESSION, 17 | SAVE_AND_REDIRECT, 18 | REMEMBER_ME 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/exceptions/ProfileIncompleteException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.exceptions; 14 | 15 | public class ProfileIncompleteException extends Exception { 16 | 17 | private static final long serialVersionUID = 1L; 18 | 19 | private String reason; 20 | 21 | public ProfileIncompleteException(String reason) { 22 | this.reason = reason; 23 | } 24 | 25 | public ProfileIncompleteException(String reason, Throwable cause) { 26 | super(cause); 27 | this.reason = reason; 28 | } 29 | 30 | public String getReason() { 31 | return reason; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/exceptions/RegistrationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.exceptions; 14 | 15 | public class RegistrationException extends Exception { 16 | 17 | private static final long serialVersionUID = 5180498365785959486L; 18 | 19 | private String reason; 20 | 21 | public RegistrationException(String reason) { 22 | this.reason = reason; 23 | } 24 | 25 | public RegistrationException(String reason, Throwable cause) { 26 | super(cause); 27 | this.reason = reason; 28 | } 29 | 30 | public String getReason() { 31 | return reason; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/factory/OmniAuthConfigProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.factory; 14 | 15 | import java.util.Map; 16 | 17 | import javax.security.auth.callback.CallbackHandler; 18 | import javax.security.auth.message.AuthException; 19 | import javax.security.auth.message.config.AuthConfigFactory; 20 | import javax.security.auth.message.config.AuthConfigProvider; 21 | import javax.security.auth.message.config.ClientAuthConfig; 22 | import javax.security.auth.message.config.ServerAuthConfig; 23 | 24 | import org.omnifaces.security.jaspic.config.AuthStacks; 25 | 26 | /** 27 | * This class is a kind of meta-factory or factory-factory for delegates to a SAM, from which we can obtain factories for the server 28 | * and the client. 29 | *

30 | * This AuthConfigProvider only supports factories for the server. 31 | * 32 | * @author Arjan Tijms 33 | * 34 | */ 35 | public class OmniAuthConfigProvider implements AuthConfigProvider { 36 | 37 | private static final String CALLBACK_HANDLER_PROPERTY_NAME = "authconfigprovider.client.callbackhandler"; 38 | 39 | private Map providerProperties; 40 | private AuthStacks stacks; 41 | 42 | public OmniAuthConfigProvider(AuthStacks stacks) { 43 | this.stacks = stacks; 44 | } 45 | 46 | /** 47 | * Constructor with signature and implementation that's required by API. 48 | * 49 | * @param properties 50 | * @param factory 51 | */ 52 | public OmniAuthConfigProvider(Map properties, AuthConfigFactory factory) { 53 | this.providerProperties = properties; 54 | 55 | // API requires self registration if factory is provided. Not clear 56 | // where the "layer" (2nd parameter) 57 | // and especially "appContext" (3rd parameter) values have to come from 58 | // at this place. 59 | if (factory != null) { 60 | factory.registerConfigProvider(this, null, null, "Auto registration"); 61 | } 62 | } 63 | 64 | /** 65 | * The actual factory method that creates the factory used to eventually obtain the delegate for a SAM. 66 | */ 67 | @Override 68 | public ServerAuthConfig getServerAuthConfig(String layer, String appContext, CallbackHandler handler) throws AuthException, SecurityException { 69 | return new OmniServerAuthConfig(layer, appContext, handler == null ? createDefaultCallbackHandler() : handler, providerProperties, stacks); 70 | } 71 | 72 | @Override 73 | public ClientAuthConfig getClientAuthConfig(String layer, String appContext, CallbackHandler handler) throws AuthException, SecurityException { 74 | return null; 75 | } 76 | 77 | @Override 78 | public void refresh() { 79 | } 80 | 81 | /** 82 | * Creates a default callback handler via the system property "authconfigprovider.client.callbackhandler", as seemingly required by the API (API 83 | * uses wording "may" create default handler). 84 | * 85 | * @return an instance of the default call back handler 86 | * @throws AuthException 87 | */ 88 | private CallbackHandler createDefaultCallbackHandler() throws AuthException { 89 | String callBackClassName = System.getProperty(CALLBACK_HANDLER_PROPERTY_NAME); 90 | 91 | if (callBackClassName == null) { 92 | throw new AuthException("No default handler set via system property: " + CALLBACK_HANDLER_PROPERTY_NAME); 93 | } 94 | 95 | try { 96 | return (CallbackHandler) Thread.currentThread().getContextClassLoader().loadClass(callBackClassName).newInstance(); 97 | } 98 | catch (Exception e) { 99 | throw new AuthException(e.getMessage()); 100 | } 101 | } 102 | 103 | } -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/factory/OmniServerAuthConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.factory; 14 | 15 | import java.util.Map; 16 | 17 | import javax.security.auth.Subject; 18 | import javax.security.auth.callback.CallbackHandler; 19 | import javax.security.auth.message.AuthException; 20 | import javax.security.auth.message.MessageInfo; 21 | import javax.security.auth.message.config.ServerAuthConfig; 22 | import javax.security.auth.message.config.ServerAuthContext; 23 | import javax.security.auth.message.module.ServerAuthModule; 24 | 25 | import org.omnifaces.security.jaspic.config.AuthStacks; 26 | 27 | /** 28 | * This class functions as a kind of factory for {@link ServerAuthContext} instances, which are delegates for the actual {@link ServerAuthModule} 29 | * (SAM) that we're after. 30 | * 31 | */ 32 | public class OmniServerAuthConfig implements ServerAuthConfig { 33 | 34 | private String layer; 35 | private String appContext; 36 | private CallbackHandler handler; 37 | private AuthStacks stacks; 38 | private Map providerProperties; 39 | 40 | public OmniServerAuthConfig(String layer, String appContext, CallbackHandler handler, Map providerProperties, AuthStacks stacks) { 41 | this.layer = layer; 42 | this.appContext = appContext; 43 | this.handler = handler; 44 | this.providerProperties = providerProperties; 45 | this.stacks = stacks; 46 | } 47 | 48 | @Override 49 | public ServerAuthContext getAuthContext(String authContextID, Subject serviceSubject, @SuppressWarnings("rawtypes") Map properties) throws AuthException { 50 | return new OmniServerAuthContext(handler, stacks); 51 | } 52 | 53 | @Override 54 | public String getMessageLayer() { 55 | return layer; 56 | } 57 | 58 | @Override 59 | public String getAuthContextID(MessageInfo messageInfo) { 60 | return appContext; 61 | } 62 | 63 | @Override 64 | public String getAppContext() { 65 | return appContext; 66 | } 67 | 68 | @Override 69 | public void refresh() { 70 | // doesn't seem to be called by any server, ever. 71 | } 72 | 73 | @Override 74 | public boolean isProtected() { 75 | return false; 76 | } 77 | 78 | public Map getProviderProperties() { 79 | return providerProperties; 80 | } 81 | 82 | } -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/factory/OmniServerAuthContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.factory; 14 | 15 | import static java.util.logging.Level.FINE; 16 | import static javax.security.auth.message.AuthStatus.FAILURE; 17 | import static org.omnifaces.security.jaspic.Utils.getSingleParameterFromQueryString; 18 | import static org.omnifaces.security.jaspic.Utils.isEmpty; 19 | import static org.omnifaces.security.jaspic.Utils.toParameterMap; 20 | import static org.omnifaces.security.jaspic.Utils.unserializeURLSafe; 21 | 22 | import java.util.Collections; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.logging.Logger; 26 | 27 | import javax.security.auth.Subject; 28 | import javax.security.auth.callback.CallbackHandler; 29 | import javax.security.auth.message.AuthException; 30 | import javax.security.auth.message.AuthStatus; 31 | import javax.security.auth.message.MessageInfo; 32 | import javax.security.auth.message.config.ServerAuthContext; 33 | import javax.servlet.http.HttpServletRequest; 34 | 35 | import org.omnifaces.security.jaspic.config.AuthStacks; 36 | import org.omnifaces.security.jaspic.config.Module; 37 | import org.omnifaces.security.jaspic.core.AuthResult; 38 | import org.omnifaces.security.jaspic.core.Jaspic; 39 | 40 | /** 41 | * The Server Authentication Context is an extra (required) indirection between the Application Server and the actual Server Authentication Module 42 | * (SAM). This can be used to encapsulate any number of SAMs and either select one at run-time, invoke them all in order, etc. 43 | *

44 | * This auth context implements the algorithm for a JAAS/PAM like stacking of auth modules and simulates some JASPIC MR2 (Java EE 7) 45 | * features for use in a JASPIC MR1 (Java EE 6) environment such as delivering a logout call to the auth modules and automatically 46 | * creating an authentication session. 47 | *

48 | * This auth context is also instrumental on working around the CDI limitation of JASPIC; a protected request is always redirected 49 | * to a public resource, where a Filter does an explicit call for authentication. Container calls are not delegated to the SAMs, only 50 | * explicit calls are. This ensures at the cost of an extra redirect that SAMs always execute within a context where CDI, EJB etc is available 51 | * and where forwards on the passed-in request instance work. 52 | *

53 | * Note: As explained above, parts of this implementation are redundant with JASPIC 1.0 MR2. Hopefully the Filter workaround will 54 | * be redundant too with some future version of JASPIC. 55 | * 56 | * @author Arjan Tijms 57 | * 58 | */ 59 | public class OmniServerAuthContext implements ServerAuthContext { 60 | 61 | // TODO: Session state needs to be handled much better. This is one of the things that has to be fixed 62 | // before a 1.0 release. The below names are temporary while OmniSecurity 0.x is in alpha. 63 | public static final String AUTH_METHOD_SESSION_NAME = "org.omnifaces.security.jaspic.AuthMethod"; 64 | public static final String REMEMBER_ME_SESSION_NAME = "org.omnifaces.security.jaspic.RememberMe"; 65 | 66 | private static final Logger logger = Logger.getLogger(OmniServerAuthContext.class.getName()); 67 | 68 | private AuthStacks stacks; 69 | private boolean onlyOneModule; 70 | 71 | public OmniServerAuthContext(CallbackHandler handler, AuthStacks stacks) throws AuthException { 72 | 73 | this.stacks = stacks; 74 | 75 | if (stacks.getModuleStacks().size() == 1) { 76 | onlyOneModule = true; 77 | } 78 | 79 | for (List modules : stacks.getModuleStacks().values()) { 80 | for (Module module : modules) { 81 | module.getServerAuthModule().initialize(null, null, handler, module.getOptions()); 82 | } 83 | } 84 | } 85 | 86 | @Override 87 | public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { 88 | 89 | AuthStatus status = doValidateRequest(messageInfo, clientSubject, serviceSubject); 90 | Jaspic.setLastStatus((HttpServletRequest) messageInfo.getRequestMessage(), status); 91 | 92 | return status; 93 | } 94 | 95 | public AuthStatus doValidateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { 96 | 97 | HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage(); 98 | 99 | boolean requiredFailed = false; 100 | AuthResult finalAuthResult = new AuthResult(); 101 | 102 | for (Module module : getModuleStack(request)) { 103 | 104 | AuthResult authResult = Jaspic.validateRequest(module.getServerAuthModule(), messageInfo, clientSubject, serviceSubject); 105 | 106 | if (authResult.getAuthStatus() == FAILURE) { 107 | throw new IllegalStateException("Servlet Container Profile SAM should not return status FAILURE. This is for CLIENT SAMs only"); 108 | } 109 | 110 | finalAuthResult.add(authResult); 111 | 112 | switch (module.getControlFlag()) { 113 | 114 | case REQUIRED: 115 | if (authResult.isFailed()) { 116 | requiredFailed = true; 117 | } 118 | break; 119 | 120 | case REQUISITE: 121 | if (authResult.isFailed()) { 122 | return finalAuthResult.throwOrFail(); 123 | } 124 | break; 125 | 126 | case SUFFICIENT: 127 | if (!authResult.isFailed() && !requiredFailed) { 128 | return authResult.getAuthStatus(); 129 | } 130 | break; 131 | 132 | case OPTIONAL: 133 | // Do nothing 134 | break; 135 | } 136 | } 137 | 138 | return finalAuthResult.throwOrReturnStatus(); 139 | } 140 | 141 | @Override 142 | public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException { 143 | 144 | AuthStatus authStatus = null; 145 | for (Module module : getModuleStack((HttpServletRequest) messageInfo.getRequestMessage())) { 146 | authStatus = module.getServerAuthModule().secureResponse(messageInfo, serviceSubject); 147 | } 148 | 149 | return authStatus; 150 | } 151 | 152 | @Override 153 | public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { 154 | for (Module module : getModuleStack((HttpServletRequest) messageInfo.getRequestMessage())) { // tmp 155 | module.getServerAuthModule().cleanSubject(messageInfo, subject); 156 | } 157 | } 158 | 159 | private List getModuleStack(HttpServletRequest request) { 160 | 161 | String authMethod = Jaspic.getAuthParameters(request).getAuthMethod(); 162 | 163 | if (authMethod == null) { 164 | 165 | // Currently depends on the auth module to set this in a callback URL. 166 | // TODO: this needs much better handling 167 | String state = getSingleParameterFromQueryString(request.getQueryString(), "state"); 168 | 169 | if (!isEmpty(state)) { 170 | try { 171 | Map> requestStateParameters = toParameterMap(unserializeURLSafe(state)); 172 | if (!isEmpty(requestStateParameters.get("authMethod"))) { 173 | authMethod = requestStateParameters.get("authMethod") 174 | .get(0); 175 | } 176 | } 177 | catch (IllegalArgumentException e) { 178 | logger.log(FINE, "Unable to decode state parameter:", e); 179 | } 180 | } 181 | 182 | if (authMethod == null && !onlyOneModule) { 183 | try { 184 | authMethod = (String) request.getSession().getAttribute(AUTH_METHOD_SESSION_NAME); 185 | } catch (IllegalStateException e) { 186 | // Ignore 187 | } 188 | } 189 | 190 | if (authMethod == null) { 191 | authMethod = stacks.getDefaultStackName(); 192 | } 193 | } 194 | 195 | if (!onlyOneModule) { 196 | // If there's more than one module, remember the auth method in the session. 197 | // This is needed so that after repeated interactions with the user we keep 198 | // using the same auth method. 199 | // TODO: Have several options here: 200 | // * Don't save auth method (assumes no module goes into a dialog with the user) 201 | // * Save auth method in session 202 | // * Save auth method in cookie 203 | // * Save auth method custom (use plug-in) 204 | try { 205 | request.getSession().setAttribute(AUTH_METHOD_SESSION_NAME, authMethod); 206 | } catch (IllegalStateException e) { 207 | // Ignore 208 | } 209 | } 210 | 211 | return stacks.getModuleStacks().getOrDefault(authMethod, Collections.emptyList()); 212 | } 213 | 214 | } -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/filters/HttpFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.filters; 14 | 15 | import java.io.IOException; 16 | 17 | import javax.servlet.Filter; 18 | import javax.servlet.FilterChain; 19 | import javax.servlet.FilterConfig; 20 | import javax.servlet.Servlet; 21 | import javax.servlet.ServletContext; 22 | import javax.servlet.ServletException; 23 | import javax.servlet.ServletRequest; 24 | import javax.servlet.ServletResponse; 25 | import javax.servlet.http.HttpServlet; 26 | import javax.servlet.http.HttpServletRequest; 27 | import javax.servlet.http.HttpServletResponse; 28 | import javax.servlet.http.HttpSession; 29 | 30 | /** 31 | *

32 | * The {@link HttpFilter} is abstract filter specifically for HTTP requests. It provides a convenient abstract 33 | * {@link #doFilter(HttpServletRequest, HttpServletResponse, HttpSession, FilterChain)} method directly providing the 34 | * HTTP servlet request, response and session, so that there's no need to cast them everytime in the 35 | * {@link #doFilter(ServletRequest, ServletResponse, FilterChain)} implementation. Also, default implementations of 36 | * {@link #init(FilterConfig)} and {@link #destroy()} are provided, so that there's no need to implement them every time 37 | * even when not really needed. 38 | *

39 | * It's a bit the idea of using the convenient {@link HttpServlet} abstract servlet class instead of the barebones 40 | * {@link Servlet} interface. 41 | * 42 | *

Usage

43 | *

44 | * To use it, just let your custom filter extend from {@link HttpFilter} instead of implement {@link Filter}. 45 | * For example: 46 | *

 47 |  * @WebFilter("/app/*")
 48 |  * public class LoginFilter extends HttpFilter {
 49 |  *
 50 |  *     @Override
 51 |  *     public void doFilter(HttpServletRequest request, HttpServletResponse response, HttpSession session, FilterChain chain)
 52 |  *         throws ServletException, IOException
 53 |  *     {
 54 |  *         if (session != null && session.getAttribute("user") != null) {
 55 |  *             chain.doFilter(request, response);
 56 |  *         }
 57 |  *         else {
 58 |  *             Servlets.facesRedirect(request, response, "login.xhtml");
 59 |  *         }
 60 |  *     }
 61 |  * }
 62 |  * 
63 | * 64 | * Note this class is repackaged from org.omnifaces.filter.HttpFilter 65 | * 66 | * @author Arjan Tijms 67 | * @author Bauke Scholtz 68 | */ 69 | public abstract class HttpFilter implements Filter { 70 | 71 | // Constants ------------------------------------------------------------------------------------------------------ 72 | 73 | private static final String ERROR_NO_FILTERCONFIG = "FilterConfig is not available." 74 | + " It seems that you've overriden HttpFilter#init(FilterConfig)." 75 | + " You should be overriding HttpFilter#init() instead, otherwise you have to call super.init(config)."; 76 | 77 | // Properties ----------------------------------------------------------------------------------------------------- 78 | 79 | private FilterConfig filterConfig; 80 | 81 | // Actions -------------------------------------------------------------------------------------------------------- 82 | 83 | /** 84 | * Called by the servlet container when the filter is about to be placed into service. This implementation stores 85 | * the {@link FilterConfig} object for later use by the getter methods. It's recommended to not 86 | * override this method. Instead, just use {@link #init()} method. When overriding this method anyway, don't forget 87 | * to call super.init(config), otherwise the getter methods will throw an illegal state exception. 88 | */ 89 | @Override 90 | public void init(FilterConfig filterConfig) throws ServletException { 91 | this.filterConfig = filterConfig; 92 | init(); 93 | } 94 | 95 | /** 96 | * Convenience init() method without FilterConfig parameter which will be called by init(FilterConfig). 97 | * @throws ServletException When filter's initialization failed. 98 | */ 99 | public void init() throws ServletException { 100 | // 101 | } 102 | 103 | @Override 104 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 105 | throws ServletException, IOException 106 | { 107 | HttpServletRequest httpRequest = (HttpServletRequest) request; 108 | HttpServletResponse httpResponse = (HttpServletResponse) response; 109 | HttpSession session = httpRequest.getSession(false); 110 | doFilter(httpRequest, httpResponse, session, chain); 111 | } 112 | 113 | /** 114 | * Filter the HTTP request. The session argument is null if there is no session. 115 | * @param request The HTTP request. 116 | * @param response The HTTP response. 117 | * @param session The HTTP session, if any, else null. 118 | * @param chain The filter chain to continue. 119 | * @throws ServletException As wrapper exception when something fails in the request processing. 120 | * @throws IOException Whenever something fails at I/O level. 121 | * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain) 122 | */ 123 | public abstract void doFilter 124 | (HttpServletRequest request, HttpServletResponse response, HttpSession session, FilterChain chain) 125 | throws ServletException, IOException; 126 | 127 | @Override 128 | public void destroy() { 129 | filterConfig = null; 130 | } 131 | 132 | // Getters -------------------------------------------------------------------------------------------------------- 133 | 134 | /** 135 | * Returns the filter config. 136 | * @return The filter config. 137 | */ 138 | protected FilterConfig getFilterConfig() { 139 | checkFilterConfig(); 140 | return filterConfig; 141 | } 142 | 143 | /** 144 | * Returns the value of the filter init parameter associated with the given name. 145 | * @param name The filter init parameter name to return the associated value for. 146 | * @return The value of the filter init parameter associated with the given name. 147 | */ 148 | protected String getInitParameter(String name) { 149 | checkFilterConfig(); 150 | return filterConfig.getInitParameter(name); 151 | } 152 | 153 | /** 154 | * Returns the servlet context. 155 | * @return The servlet context. 156 | */ 157 | protected ServletContext getServletContext() { 158 | checkFilterConfig(); 159 | return filterConfig.getServletContext(); 160 | } 161 | 162 | // Helpers -------------------------------------------------------------------------------------------------------- 163 | 164 | /** 165 | * Check if the filter config is been set and thus the enduser has properly called super.init(config) when 166 | * overriding the init(config). 167 | * @throws IllegalStateException When this is not the case. 168 | */ 169 | private void checkFilterConfig() { 170 | if (filterConfig == null) { 171 | throw new IllegalStateException(ERROR_NO_FILTERCONFIG); 172 | } 173 | } 174 | 175 | } -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/filters/OmniServerAuthFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.filters; 14 | 15 | import static javax.security.auth.message.AuthStatus.SUCCESS; 16 | import static org.omnifaces.security.jaspic.Utils.isOneOf; 17 | import static org.omnifaces.security.jaspic.Utils.redirect; 18 | import static org.omnifaces.security.jaspic.core.Jaspic.authenticateFromFilter; 19 | import static org.omnifaces.security.jaspic.core.Jaspic.getLastStatus; 20 | import static org.omnifaces.security.jaspic.core.Jaspic.isDidAuthenticationAndSucceeded; 21 | 22 | import java.io.IOException; 23 | 24 | import javax.security.auth.message.module.ServerAuthModule; 25 | import javax.servlet.FilterChain; 26 | import javax.servlet.ServletException; 27 | import javax.servlet.http.HttpServletRequest; 28 | import javax.servlet.http.HttpServletResponse; 29 | import javax.servlet.http.HttpSession; 30 | 31 | import org.omnifaces.security.jaspic.request.HttpServletRequestDelegator; 32 | import org.omnifaces.security.jaspic.request.RequestData; 33 | import org.omnifaces.security.jaspic.request.RequestDataDAO; 34 | 35 | /** 36 | * This filter explicitly makes a call to {@link HttpServletRequest#authenticate(HttpServletResponse)} (via a helper method so it can be 37 | * recognized as an explicit call) at the start of each request. 38 | *

39 | * 40 | * The reason for this Filter is that in here CDI, EJB, etc are available, while in a 41 | * {@link ServerAuthModule#validateRequest(javax.security.auth.message.MessageInfo, javax.security.auth.Subject, javax.security.auth.Subject)} 42 | * this is for most servers not the case. 43 | *

44 | * Additionally, in this Filter we can wrap the request if needed. This should be possible in validateHttpRequest as well, but 45 | * historically JASPIC implementations never supported this. As of 2014, WildFly 8 and GlassFish 4 support it. 46 | * 47 | */ 48 | public class OmniServerAuthFilter extends HttpFilter { 49 | 50 | private final RequestDataDAO requestDAO = new RequestDataDAO(); 51 | 52 | @Override 53 | public void doFilter(HttpServletRequest request, HttpServletResponse response, HttpSession session, FilterChain chain) throws ServletException, IOException { 54 | 55 | // Trigger an explicit call to the SAMs, so they will execute within a context where CDI, EJB etc is available. 56 | authenticateFromFilter(request, response); 57 | 58 | if (isOneOf(getLastStatus(request), SUCCESS, null)) { 59 | 60 | HttpServletRequest newRequest = request; 61 | 62 | RequestData requestData = requestDAO.get(request); 63 | if (requestData != null) { 64 | 65 | boolean matchesRequest = requestData.matchesRequest(request); 66 | 67 | // Note that we check for "isDidAuthentication()" below to see if authentication actually happened during the call above 68 | // to "authenticateFromFilter()". The return status "SUCCESS" is too general, and could mean we have a remembered authentication 69 | // or an unauthenticated identity. Here we need to react on the initial authentication only. 70 | if (!matchesRequest && SUCCESS.equals(getLastStatus(request)) && isDidAuthenticationAndSucceeded(request)) { 71 | 72 | // If we just authenticated but are on another URL than where we should take the user, we redirect the user to this original URL. 73 | // It's less ideal for the SAM to do this, since there's no status code for doing authentication AND 74 | // redirecting (after which JASPIC should not invoke the resource). 75 | redirect(response, requestData.getFullRequestURL()); 76 | 77 | // Don't continue the chain (this is what a SAM cannot instruct JASPIC to do). 78 | return; 79 | } 80 | 81 | // If there was a saved request and it matches with the current request, it means the user was originally redirected from 82 | // a protected resource to authenticate and has now been redirected back to it. Since we reach this filter, it means 83 | // the user is now authenticated to access this protected resource. 84 | if (matchesRequest) { 85 | 86 | if (requestData.isRestoreRequest()) { 87 | // We restore the original request here by providing a wrapped request. This will ensure the 88 | // original request parameters (GET + POST) as well as the original cookies etc are available again. 89 | newRequest = new HttpServletRequestDelegator(request, requestData); 90 | } 91 | requestDAO.remove(request); 92 | } 93 | } 94 | 95 | chain.doFilter(newRequest, response); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/listeners/BaseServletContextListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.listeners; 14 | import javax.servlet.ServletContextEvent; 15 | import javax.servlet.ServletContextListener; 16 | 17 | public class BaseServletContextListener implements ServletContextListener { 18 | 19 | @Override 20 | public void contextInitialized(ServletContextEvent arg0) { 21 | // NOOP 22 | } 23 | 24 | @Override 25 | public void contextDestroyed(ServletContextEvent arg0) { 26 | // NOOP 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/listeners/SamAutoDeregistrationListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.listeners; 14 | 15 | import static org.omnifaces.security.jaspic.core.Jaspic.deregisterServerAuthModule; 16 | 17 | import javax.servlet.ServletContextEvent; 18 | import javax.servlet.annotation.WebListener; 19 | 20 | @WebListener 21 | public class SamAutoDeregistrationListener extends BaseServletContextListener { 22 | 23 | @Override 24 | public void contextDestroyed(ServletContextEvent sce) { 25 | deregisterServerAuthModule(sce.getServletContext()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/request/BaseCookieDAO.java: -------------------------------------------------------------------------------- 1 | package org.omnifaces.security.jaspic.request; 2 | 3 | import javax.servlet.http.Cookie; 4 | import javax.servlet.http.HttpServletRequest; 5 | import javax.servlet.http.HttpServletResponse; 6 | 7 | import org.omnifaces.security.jaspic.Utils; 8 | 9 | public class BaseCookieDAO { 10 | 11 | public void save(HttpServletRequest request, HttpServletResponse response, String name, String value, Integer maxAge) { 12 | Cookie cookie = new Cookie(name, value); 13 | if (maxAge != null) { 14 | cookie.setMaxAge(maxAge); 15 | } 16 | cookie.setHttpOnly(true); 17 | cookie.setPath(Utils.isEmpty(request.getContextPath())? "/" : request.getContextPath()); 18 | 19 | response.addCookie(cookie); 20 | } 21 | 22 | public Cookie get(HttpServletRequest request, String name) { 23 | if (request.getCookies() != null) { 24 | for (Cookie cookie : request.getCookies()) { 25 | if (name.equals(cookie.getName()) && !isEmpty(cookie)) { 26 | return cookie; 27 | } 28 | } 29 | } 30 | 31 | return null; 32 | } 33 | 34 | public void remove(HttpServletRequest request, HttpServletResponse response, String name) { 35 | Cookie cookie = new Cookie(name, null); 36 | cookie.setMaxAge(0); 37 | cookie.setPath(Utils.isEmpty(request.getContextPath())? "/" : request.getContextPath()); 38 | 39 | response.addCookie(cookie); 40 | } 41 | 42 | private boolean isEmpty(Cookie cookie) { 43 | return cookie.getValue() == null || cookie.getValue().trim().isEmpty(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/request/HttpServletRequestDelegator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.request; 14 | 15 | import static java.lang.Integer.parseInt; 16 | import static java.util.Collections.enumeration; 17 | import static java.util.Locale.US; 18 | import static java.util.TimeZone.getTimeZone; 19 | 20 | import java.text.DateFormat; 21 | import java.text.ParseException; 22 | import java.text.SimpleDateFormat; 23 | import java.util.ArrayList; 24 | import java.util.Enumeration; 25 | import java.util.List; 26 | import java.util.Locale; 27 | import java.util.Map; 28 | import java.util.TimeZone; 29 | 30 | import javax.servlet.http.Cookie; 31 | import javax.servlet.http.HttpServletRequest; 32 | import javax.servlet.http.HttpServletRequestWrapper; 33 | 34 | import org.omnifaces.security.jaspic.Utils; 35 | 36 | /** 37 | * This class wraps a given HttpServletRequest instance and delegates "most" methods that request 38 | * "base data" to an also given RequestData instance. 39 | * 40 | * @author Arjan Tijms 41 | * 42 | */ 43 | public class HttpServletRequestDelegator extends HttpServletRequestWrapper { 44 | 45 | private static final TimeZone GMT = getTimeZone("GMT"); 46 | private static final String[] datePatterns = { 47 | "EE, dd MMM yyyy HH:mm:ss zz", 48 | "EEEE, dd-MMM-yy HH:mm:ss zz", 49 | "EE MMM d HH:mm:ss yyyy" 50 | }; 51 | 52 | private final RequestData requestData; 53 | private List dateFormats; 54 | 55 | public HttpServletRequestDelegator(HttpServletRequest request, RequestData requestData) { 56 | super(request); 57 | this.requestData = requestData; 58 | 59 | } 60 | 61 | @Override 62 | public Cookie[] getCookies() { 63 | return requestData.getCookies(); 64 | } 65 | 66 | 67 | // ### Headers 68 | 69 | @Override 70 | public Enumeration getHeaderNames() { 71 | return enumeration(requestData.getHeaders().keySet()); 72 | } 73 | 74 | @Override 75 | public String getHeader(String name) { 76 | 77 | // Return the first matching header that has the same (case insensitive) name 78 | for (Map.Entry> header : requestData.getHeaders().entrySet()) { 79 | if (header.getKey().equalsIgnoreCase(name) && !header.getValue().isEmpty()) { 80 | return header.getValue().get(0); 81 | } 82 | } 83 | 84 | return null; 85 | } 86 | 87 | @Override 88 | public Enumeration getHeaders(String name) { 89 | 90 | // Collect headers irrespective of the case that was used for the actual header 91 | // when submitted and the case of the name asked here. 92 | List headers = new ArrayList<>(); 93 | for (Map.Entry> header : requestData.getHeaders().entrySet()) { 94 | if (header.getKey().equalsIgnoreCase(name)) { 95 | headers.addAll(header.getValue()); 96 | } 97 | } 98 | 99 | return enumeration(headers); 100 | } 101 | 102 | @Override 103 | public int getIntHeader(String name) { 104 | String header = getHeader(name); 105 | 106 | if (header == null) { 107 | // Spec defines we should return -1 is header doesn't exist 108 | return -1; 109 | } 110 | 111 | // If header ain't an integer, spec says a NumberFormatException should be thrown, 112 | // which is what Integer.parseInt will do. 113 | return parseInt(header); 114 | } 115 | 116 | @Override 117 | public long getDateHeader(String name) { 118 | String header = getHeader(name); 119 | 120 | if (header == null) { 121 | // Spec defines we should return -1 if header doesn't exist 122 | return -1; 123 | } 124 | 125 | if (dateFormats == null) { 126 | dateFormats = new ArrayList<>(datePatterns.length); 127 | for (String datePattern : datePatterns) { 128 | dateFormats.add(createDateFormat(datePattern)); 129 | } 130 | } 131 | 132 | for (DateFormat dateFormat : dateFormats) { 133 | try { 134 | return dateFormat.parse(header).getTime(); 135 | } catch (ParseException e) { 136 | // noop 137 | } 138 | } 139 | 140 | // If no conversion is possible, spec says an IllegalArgumentException should be thrown 141 | throw new IllegalArgumentException("Can't convert " + header + " to a date"); 142 | } 143 | 144 | 145 | 146 | // ### Parameters 147 | 148 | @Override 149 | public Map getParameterMap() { 150 | return requestData.getParameters(); 151 | } 152 | 153 | @Override 154 | public String getParameter(String name) { 155 | 156 | String[] values = requestData.getParameters().get(name); 157 | if (Utils.isEmpty(values)) { 158 | return null; 159 | } 160 | 161 | return values[0]; 162 | } 163 | 164 | @Override 165 | public Enumeration getParameterNames() { 166 | return enumeration(getParameterMap().keySet()); 167 | } 168 | 169 | @Override 170 | public String[] getParameterValues(String name) { 171 | return getParameterMap().get(name); 172 | } 173 | 174 | 175 | // ### Locales 176 | 177 | @Override 178 | public Enumeration getLocales() { 179 | return enumeration(requestData.getLocales()); 180 | } 181 | 182 | @Override 183 | public Locale getLocale() { 184 | 185 | if (requestData.getLocales().isEmpty()) { 186 | // Is this possible? Doesn't the original HttpServletRequest#getLocales 187 | // already returns the default here? 188 | return Locale.getDefault(); 189 | } 190 | 191 | return requestData.getLocales().get(0); 192 | } 193 | 194 | @Override 195 | public String getMethod() { 196 | return requestData.getMethod(); 197 | } 198 | 199 | 200 | private DateFormat createDateFormat(String pattern) { 201 | DateFormat dateFormat = new SimpleDateFormat(pattern, US); 202 | dateFormat.setTimeZone(GMT); 203 | return dateFormat; 204 | } 205 | 206 | 207 | } 208 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/request/LoginTokenCookieDAO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.request; 14 | 15 | import static java.util.concurrent.TimeUnit.DAYS; 16 | 17 | import javax.servlet.http.Cookie; 18 | import javax.servlet.http.HttpServletRequest; 19 | import javax.servlet.http.HttpServletResponse; 20 | 21 | /** 22 | * This class takes care of saving the login token cookie to the response, retrieving it from 23 | * a request or removing it. 24 | * 25 | * @author Arjan Tijms 26 | * 27 | */ 28 | public class LoginTokenCookieDAO extends BaseCookieDAO { 29 | 30 | private static final String COOKIE_NAME = "omnisecurity_login_token"; 31 | private static final int MAX_AGE = (int) DAYS.toSeconds(14); 32 | 33 | public void save(HttpServletRequest request, HttpServletResponse response, String value) { 34 | save(request, response, COOKIE_NAME, value, MAX_AGE); 35 | } 36 | 37 | public Cookie get(HttpServletRequest request) { 38 | return get(request, COOKIE_NAME); 39 | } 40 | 41 | public void remove(HttpServletRequest request, HttpServletResponse response) { 42 | remove(request, response, COOKIE_NAME); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/request/RememberMeSettingCookieDAO.java: -------------------------------------------------------------------------------- 1 | package org.omnifaces.security.jaspic.request; 2 | 3 | import javax.servlet.http.Cookie; 4 | import javax.servlet.http.HttpServletRequest; 5 | import javax.servlet.http.HttpServletResponse; 6 | 7 | public class RememberMeSettingCookieDAO extends BaseCookieDAO { 8 | 9 | private static final String COOKIE_NAME = "omnisecurity_remember_me"; 10 | 11 | public void save(HttpServletRequest request, HttpServletResponse response, boolean value) { 12 | save(request, response, COOKIE_NAME, "" + value, null); 13 | } 14 | 15 | public Cookie get(HttpServletRequest request) { 16 | return get(request, COOKIE_NAME); 17 | } 18 | 19 | public void remove(HttpServletRequest request, HttpServletResponse response) { 20 | remove(request, response, COOKIE_NAME); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/request/RequestCopier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.request; 14 | 15 | import static java.util.Arrays.copyOf; 16 | import static java.util.Collections.emptyMap; 17 | import static java.util.Collections.list; 18 | import static org.omnifaces.security.jaspic.Utils.isEmpty; 19 | 20 | import java.util.ArrayList; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | import javax.servlet.http.Cookie; 26 | import javax.servlet.http.HttpServletRequest; 27 | 28 | /** 29 | * This class copies all "base data" from a given request. The goal is that this copied data can be used 30 | * later to restore a request, by wrapping a new request and delegating methods that fetch data 31 | * from that request to the copied data. 32 | * 33 | * @author Arjan Tijms 34 | * 35 | */ 36 | public final class RequestCopier { 37 | 38 | private RequestCopier() {} 39 | 40 | public static RequestData copy(HttpServletRequest request) { 41 | 42 | RequestData requestData = new RequestData(); 43 | 44 | requestData.setCookies(copyCookies(request.getCookies())); 45 | requestData.setHeaders(copyHeaders(request)); 46 | requestData.setParameters(copyParameters(request.getParameterMap())); 47 | requestData.setLocales(list(request.getLocales())); 48 | 49 | requestData.setMethod(request.getMethod()); 50 | requestData.setRequestURL(request.getRequestURL().toString()); 51 | requestData.setQueryString(request.getQueryString()); 52 | 53 | return requestData; 54 | } 55 | 56 | 57 | private static Cookie[] copyCookies(Cookie[] cookies) { 58 | 59 | if (isEmpty(cookies)) { 60 | return cookies; 61 | } 62 | 63 | ArrayList copiedCookies = new ArrayList<>(); 64 | for (Cookie cookie : cookies) { 65 | copiedCookies.add((Cookie)cookie.clone()); 66 | } 67 | 68 | return copiedCookies.toArray(new Cookie[copiedCookies.size()]); 69 | } 70 | 71 | private static Map> copyHeaders(HttpServletRequest request) { 72 | 73 | Map> copiedHeaders = new HashMap<>(); 74 | for (String headerName : list(request.getHeaderNames())) { 75 | copiedHeaders.put(headerName, list(request.getHeaders(headerName))); 76 | } 77 | 78 | return copiedHeaders; 79 | } 80 | 81 | private static Map copyParameters(Map parameters) { 82 | 83 | if (isEmptyMap(parameters)) { 84 | return emptyMap(); 85 | } 86 | 87 | Map copiedParameters = new HashMap<>(); 88 | for (Map.Entry parameter : parameters.entrySet()) { 89 | copiedParameters.put(parameter.getKey(), copyOf(parameter.getValue(), parameter.getValue().length)); 90 | } 91 | 92 | return copiedParameters; 93 | } 94 | 95 | private static boolean isEmptyMap(Map map) { 96 | return map == null || map.isEmpty(); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/request/RequestData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.request; 14 | 15 | import java.io.Serializable; 16 | import static org.omnifaces.security.jaspic.Utils.isEmpty; 17 | 18 | import java.util.List; 19 | import java.util.Locale; 20 | import java.util.Map; 21 | 22 | import javax.servlet.http.Cookie; 23 | import javax.servlet.http.HttpServletRequest; 24 | 25 | /** 26 | * This class stores the core data that makes up an HttpServletRequest. 27 | * 28 | * @author Arjan Tijms 29 | * 30 | */ 31 | public class RequestData implements Serializable { 32 | 33 | private Cookie[] cookies; 34 | private Map> headers; 35 | private List locales; 36 | private Map parameters; 37 | 38 | private String method; 39 | private String requestURL; 40 | private String queryString; 41 | 42 | private boolean restoreRequest = true; 43 | 44 | public Cookie[] getCookies() { 45 | return cookies; 46 | } 47 | 48 | public void setCookies(Cookie[] cookies) { 49 | this.cookies = cookies; 50 | } 51 | 52 | public Map> getHeaders() { 53 | return headers; 54 | } 55 | 56 | public void setHeaders(Map> headers) { 57 | this.headers = headers; 58 | } 59 | 60 | public List getLocales() { 61 | return locales; 62 | } 63 | 64 | public void setLocales(List locales) { 65 | this.locales = locales; 66 | } 67 | 68 | public Map getParameters() { 69 | return parameters; 70 | } 71 | 72 | public void setParameters(Map parameters) { 73 | this.parameters = parameters; 74 | } 75 | 76 | public String getMethod() { 77 | return method; 78 | } 79 | 80 | public void setMethod(String method) { 81 | this.method = method; 82 | } 83 | 84 | public String getQueryString() { 85 | return queryString; 86 | } 87 | 88 | public void setQueryString(String queryString) { 89 | this.queryString = queryString; 90 | } 91 | 92 | public String getRequestURL() { 93 | return requestURL; 94 | } 95 | 96 | public void setRequestURL(String requestURL) { 97 | this.requestURL = requestURL; 98 | } 99 | 100 | public boolean isRestoreRequest() { 101 | return restoreRequest; 102 | } 103 | 104 | public void setRestoreRequest(boolean restoreRequest) { 105 | this.restoreRequest = restoreRequest; 106 | } 107 | 108 | public String getFullRequestURL() { 109 | return buildFullRequestURL(requestURL, queryString); 110 | } 111 | 112 | public boolean matchesRequest(HttpServletRequest request) { 113 | // (or use requestURI instead of requestURL?) 114 | return getFullRequestURL().equals(buildFullRequestURL(request.getRequestURL().toString(), request.getQueryString())); 115 | } 116 | 117 | @Override 118 | public String toString() { 119 | return String.format("%s %s", method, getFullRequestURL()); 120 | } 121 | 122 | private String buildFullRequestURL(String requestURL, String queryString) { 123 | return requestURL + (isEmpty(queryString) ? "" : "?" + queryString); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/request/RequestDataDAO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.request; 14 | 15 | import static org.omnifaces.security.jaspic.request.RequestCopier.copy; 16 | 17 | import javax.servlet.http.HttpServletRequest; 18 | import javax.servlet.http.HttpSession; 19 | 20 | /** 21 | * This class saves the data of a HttpServletRequest into the session as a RequestData instance, and has 22 | * methods to retrieve and remove this data again. 23 | * 24 | * @author Arjan Tijms 25 | * 26 | */ 27 | public class RequestDataDAO { 28 | 29 | private static final String ORIGINAL_REQUEST_DATA_SESSION_NAME = "org.omnifaces.security.jaspic.original.request"; 30 | 31 | public void save(HttpServletRequest request) { 32 | request.getSession().setAttribute(ORIGINAL_REQUEST_DATA_SESSION_NAME, copy(request)); 33 | } 34 | 35 | public void saveUrlOnly(HttpServletRequest request, String url) { 36 | RequestData requestData = new RequestData(); 37 | 38 | requestData.setRequestURL(url); 39 | requestData.setRestoreRequest(false); 40 | 41 | request.getSession().setAttribute(ORIGINAL_REQUEST_DATA_SESSION_NAME, requestData); 42 | } 43 | 44 | public RequestData get(HttpServletRequest request) { 45 | HttpSession session = request.getSession(false); 46 | if (session == null) { 47 | return null; 48 | } 49 | 50 | return (RequestData) session.getAttribute(ORIGINAL_REQUEST_DATA_SESSION_NAME); 51 | } 52 | 53 | public void remove(HttpServletRequest request) { 54 | request.getSession().removeAttribute(ORIGINAL_REQUEST_DATA_SESSION_NAME); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/request/StateCookieDAO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.request; 14 | 15 | import javax.servlet.http.Cookie; 16 | import javax.servlet.http.HttpServletRequest; 17 | import javax.servlet.http.HttpServletResponse; 18 | 19 | /** 20 | * This class takes care of saving the login token cookie to the response, retrieving it from 21 | * a request or removing it. 22 | * 23 | * @author Arjan Tijms 24 | * 25 | */ 26 | public class StateCookieDAO extends BaseCookieDAO { 27 | 28 | private static final String COOKIE_NAME = "omnisecurity_state_token"; 29 | 30 | public void save(HttpServletRequest request, HttpServletResponse response, String value) { 31 | save(request, response, COOKIE_NAME, value, null); 32 | } 33 | 34 | public Cookie get(HttpServletRequest request) { 35 | return get(request, COOKIE_NAME); 36 | } 37 | 38 | public void remove(HttpServletRequest request, HttpServletResponse response) { 39 | remove(request, response, COOKIE_NAME); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/user/Authenticator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.user; 14 | 15 | import java.io.Serializable; 16 | import java.util.List; 17 | 18 | /** 19 | * Base interface to be implemented by user code that provides an actual authenticator. 20 | *

21 | * The base interface specifies the data that the authenticator should return (make available). 22 | * 23 | * @author Arjan Tijms 24 | * 25 | */ 26 | public interface Authenticator extends Serializable { 27 | 28 | String getUserName(); 29 | List getApplicationRoles(); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/user/TokenAuthenticator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.user; 14 | 15 | /** 16 | * An authenticator that can do authentication based on a token it previously generated 17 | * for an already authenticated user. 18 | *

19 | * The intend for this authenticator is to generate a (temporary) token that's associated 20 | * with a user for the purpose of a "remember me" facility. 21 | * 22 | * @author Arjan Tijms 23 | * 24 | */ 25 | public interface TokenAuthenticator extends Authenticator { 26 | 27 | boolean authenticate(String token); 28 | String generateLoginToken(); 29 | void removeLoginToken(String token); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/user/UsernameOnlyAuthenticator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.user; 14 | 15 | /** 16 | * Authenticator that "authenticates" based on the username only. This is a security sensitive operation, as no further checks are demanded 17 | * by the API. 18 | *

19 | * This type of authenticator is useful for refreshing the name and or roles of an already logged-in user or for SU logins. 20 | * 21 | * @author Arjan Tijms 22 | * 23 | */ 24 | public interface UsernameOnlyAuthenticator extends Authenticator { 25 | 26 | boolean authenticateWithoutPassword(String username); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/user/UsernamePasswordAuthenticator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.user; 14 | 15 | /** 16 | * An authenticator that accepts a basic user name/password combination for authentication. 17 | * 18 | * @author Arjan Tijms 19 | * 20 | */ 21 | public interface UsernamePasswordAuthenticator extends Authenticator { 22 | 23 | boolean authenticate(String username, String password); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/wrappers/AutoRegisterSessionWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.wrappers; 14 | 15 | import static java.util.Collections.emptyList; 16 | import static java.util.Collections.unmodifiableList; 17 | import static javax.security.auth.message.AuthStatus.SUCCESS; 18 | import static org.omnifaces.security.jaspic.core.Jaspic.LOGGEDIN_ROLES; 19 | import static org.omnifaces.security.jaspic.core.Jaspic.LOGGEDIN_USERNAME; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | import javax.security.auth.Subject; 26 | import javax.security.auth.callback.CallbackHandler; 27 | import javax.security.auth.message.AuthException; 28 | import javax.security.auth.message.AuthStatus; 29 | import javax.security.auth.message.MessageInfo; 30 | import javax.security.auth.message.MessagePolicy; 31 | import javax.security.auth.message.module.ServerAuthModule; 32 | import javax.servlet.http.HttpServletRequest; 33 | import javax.servlet.http.HttpSession; 34 | 35 | import org.omnifaces.security.jaspic.core.HttpMsgContext; 36 | import org.omnifaces.security.jaspic.core.ServerAuthModuleWrapper; 37 | 38 | public class AutoRegisterSessionWrapper extends ServerAuthModuleWrapper { 39 | 40 | private static final String AUTHENTICATOR_SESSION_NAME = "org.omnifaces.security.jaspic.Authenticator"; 41 | 42 | private CallbackHandler handler; 43 | private Map options; 44 | 45 | public AutoRegisterSessionWrapper(ServerAuthModule serverAuthModule) { 46 | super(serverAuthModule); 47 | } 48 | 49 | @Override 50 | @SuppressWarnings({ "rawtypes", "unchecked" }) 51 | public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, Map options) throws AuthException { 52 | super.initialize(requestPolicy, responsePolicy, handler, options); 53 | this.handler = handler; 54 | this.options = options; 55 | } 56 | 57 | @Override 58 | public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { 59 | 60 | HttpMsgContext msgContext = new HttpMsgContext(handler, options, messageInfo, clientSubject); 61 | 62 | // Check to see if we're already authenticated. 63 | // 64 | // With JASPIC 1.0MR1, the container doesn't remember authentication data between requests and we thus have to 65 | // re-authenticate before every request. It's important to skip this step if authentication is explicitly requested, otherwise 66 | // we risk re-authenticating instead of processing a new login request. 67 | // 68 | // With JASPIC 1.1, the container partially takes care of this detail if so requested. 69 | if (!msgContext.isAuthenticationRequest() && canReAuthenticate(msgContext)) { 70 | return null; 71 | } 72 | 73 | AuthStatus authstatus = super.validateRequest(messageInfo, clientSubject, serviceSubject); 74 | if (authstatus == SUCCESS) { 75 | saveAuthentication(msgContext.getRequest()); 76 | } 77 | 78 | return authstatus; 79 | } 80 | 81 | private boolean canReAuthenticate(HttpMsgContext msgContext) { 82 | AuthenticationData authenticationData = getAuthenticationDataFromSession(msgContext); 83 | if (authenticationData != null) { 84 | msgContext.notifyContainerAboutLogin(authenticationData.getUserName(), authenticationData.getApplicationRoles()); 85 | return true; 86 | } 87 | 88 | return false; 89 | } 90 | 91 | @SuppressWarnings("unchecked") 92 | private void saveAuthentication(HttpServletRequest request) { 93 | 94 | if (request.getAttribute(LOGGEDIN_USERNAME) != null) { 95 | request.getSession().setAttribute( 96 | AUTHENTICATOR_SESSION_NAME, 97 | new AuthenticationData( 98 | (String) request.getAttribute(LOGGEDIN_USERNAME), 99 | (List) request.getAttribute(LOGGEDIN_ROLES) 100 | ) 101 | ); 102 | } 103 | } 104 | 105 | private AuthenticationData getAuthenticationDataFromSession(HttpMsgContext msgContext) { 106 | HttpSession session = msgContext.getRequest().getSession(false); 107 | if (session != null) { 108 | return (AuthenticationData) session.getAttribute(AUTHENTICATOR_SESSION_NAME); 109 | } 110 | 111 | return null; 112 | } 113 | 114 | private class AuthenticationData { 115 | 116 | private final String username; 117 | private final List applicationRoles; 118 | 119 | public AuthenticationData(String username, List applicationRoles) { 120 | this.username = username; 121 | if (applicationRoles != null) { 122 | this.applicationRoles = unmodifiableList(new ArrayList<>(applicationRoles)); 123 | } else { 124 | this.applicationRoles = emptyList(); 125 | } 126 | } 127 | 128 | public String getUserName() { 129 | return username; 130 | } 131 | 132 | public List getApplicationRoles() { 133 | return applicationRoles; 134 | } 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/wrappers/RememberMeWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.wrappers; 14 | 15 | import static java.util.logging.Level.FINE; 16 | import static javax.security.auth.message.AuthStatus.SUCCESS; 17 | import static org.omnifaces.security.cdi.Beans.getReferenceOrNull; 18 | import static org.omnifaces.security.jaspic.Utils.getSingleParameterFromState; 19 | import static org.omnifaces.security.jaspic.Utils.notNull; 20 | import static org.omnifaces.security.jaspic.factory.OmniServerAuthContext.REMEMBER_ME_SESSION_NAME; 21 | 22 | import java.util.Map; 23 | import java.util.logging.Logger; 24 | 25 | import javax.security.auth.Subject; 26 | import javax.security.auth.callback.CallbackHandler; 27 | import javax.security.auth.message.AuthException; 28 | import javax.security.auth.message.AuthStatus; 29 | import javax.security.auth.message.MessageInfo; 30 | import javax.security.auth.message.MessagePolicy; 31 | import javax.security.auth.message.module.ServerAuthModule; 32 | import javax.servlet.http.Cookie; 33 | import javax.servlet.http.HttpSession; 34 | 35 | import org.omnifaces.security.jaspic.core.HttpMsgContext; 36 | import org.omnifaces.security.jaspic.core.ServerAuthModuleWrapper; 37 | import org.omnifaces.security.jaspic.request.LoginTokenCookieDAO; 38 | import org.omnifaces.security.jaspic.request.RememberMeSettingCookieDAO; 39 | import org.omnifaces.security.jaspic.user.TokenAuthenticator; 40 | 41 | public class RememberMeWrapper extends ServerAuthModuleWrapper { 42 | 43 | private static final Logger logger = Logger.getLogger(RememberMeWrapper.class.getName()); 44 | 45 | private final LoginTokenCookieDAO cookieDAO = new LoginTokenCookieDAO(); 46 | 47 | private final RememberMeSettingCookieDAO rememberMeSettingCookieDAO = new RememberMeSettingCookieDAO(); 48 | 49 | private CallbackHandler handler; 50 | private Map options; 51 | 52 | public RememberMeWrapper(ServerAuthModule serverAuthModule) { 53 | super(serverAuthModule); 54 | } 55 | 56 | @Override 57 | @SuppressWarnings({ "rawtypes", "unchecked" }) 58 | public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, Map options) throws AuthException { 59 | super.initialize(requestPolicy, responsePolicy, handler, options); 60 | this.handler = handler; 61 | this.options = options; 62 | } 63 | 64 | @Override 65 | public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { 66 | 67 | HttpMsgContext msgContext = new HttpMsgContext(handler, options, messageInfo, clientSubject); 68 | TokenAuthenticator tokenAuthenticator = getReferenceOrNull(TokenAuthenticator.class); 69 | Cookie cookie = cookieDAO.get(msgContext.getRequest()); 70 | 71 | // First try to see if we can authenticate via a cookie 72 | if (notNull(tokenAuthenticator, cookie)) { 73 | if (tokenAuthenticator.authenticate(cookie.getValue())) { 74 | 75 | // We were able to authenticate via the remember-me cookie, register this with the container and return 76 | msgContext.registerWithContainer(tokenAuthenticator.getUserName(), tokenAuthenticator.getApplicationRoles()); 77 | 78 | return SUCCESS; // return SUCCESS just as a SAM would 79 | } else { 80 | // Invalid cookie, remove it 81 | cookieDAO.remove(msgContext.getRequest(), msgContext.getResponse()); 82 | } 83 | } 84 | 85 | // Let the next wrapper of SAM try to authenticate 86 | AuthStatus authstatus = super.validateRequest(messageInfo, clientSubject, serviceSubject); 87 | 88 | if (tokenAuthenticator != null && authstatus == SUCCESS && tokenAuthenticator.getUserName() != null && isRememberMe(msgContext)) { 89 | cookieDAO.save(msgContext.getRequest(), msgContext.getResponse(), tokenAuthenticator.generateLoginToken()); 90 | } 91 | 92 | return authstatus; 93 | } 94 | 95 | @Override 96 | public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { 97 | 98 | HttpMsgContext msgContext = new HttpMsgContext(handler, options, messageInfo, subject); 99 | 100 | // If there's a "remember me" cookie present, remove it. 101 | Cookie cookie = cookieDAO.get(msgContext.getRequest()); 102 | 103 | if (cookie != null) { 104 | cookieDAO.remove(msgContext.getRequest(), msgContext.getResponse()); 105 | 106 | TokenAuthenticator tokenAuthenticator = getReferenceOrNull(TokenAuthenticator.class); 107 | 108 | if (tokenAuthenticator != null) { 109 | tokenAuthenticator.removeLoginToken(cookie.getValue()); 110 | } 111 | } 112 | 113 | super.cleanSubject(messageInfo, subject); 114 | } 115 | 116 | public boolean isRememberMe(HttpMsgContext msgContext) { 117 | 118 | // TODO: handle state better 119 | 120 | if (msgContext.getAuthParameters().getRememberMe() != null) { 121 | return msgContext.getAuthParameters().getRememberMe(); 122 | } 123 | 124 | if (msgContext.getRequest().getParameter("state") != null) { 125 | try { 126 | String rememberMe = getSingleParameterFromState(msgContext.getRequest().getParameter("state"), "rememberMe"); 127 | if (rememberMe != null) { 128 | return Boolean.valueOf(rememberMe); 129 | } 130 | } 131 | catch (IllegalArgumentException e) { 132 | logger.log(FINE, "Unable to decode state parameter", e); 133 | } 134 | } 135 | 136 | Cookie rememberMeSettingCookie = rememberMeSettingCookieDAO.get(msgContext.getRequest()); 137 | if (rememberMeSettingCookie != null) { 138 | try { 139 | return Boolean.valueOf(rememberMeSettingCookie.getValue()); 140 | } 141 | finally { 142 | rememberMeSettingCookieDAO.remove(msgContext.getRequest(), msgContext.getResponse()); 143 | } 144 | } 145 | 146 | HttpSession session = msgContext.getRequest().getSession(false); 147 | if (session != null) { 148 | Boolean rememberMe = (Boolean) session.getAttribute(REMEMBER_ME_SESSION_NAME); 149 | if (rememberMe != null) { 150 | return rememberMe; 151 | } 152 | } 153 | 154 | return false; 155 | } 156 | 157 | } -------------------------------------------------------------------------------- /src/main/java/org/omnifaces/security/jaspic/wrappers/SaveAndRedirectWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 OmniFaces. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package org.omnifaces.security.jaspic.wrappers; 14 | 15 | import static javax.security.auth.message.AuthStatus.SEND_CONTINUE; 16 | import static org.omnifaces.security.jaspic.Utils.getBaseURL; 17 | import static org.omnifaces.security.jaspic.Utils.redirect; 18 | 19 | import java.util.Map; 20 | 21 | import javax.security.auth.Subject; 22 | import javax.security.auth.callback.CallbackHandler; 23 | import javax.security.auth.message.AuthException; 24 | import javax.security.auth.message.AuthStatus; 25 | import javax.security.auth.message.MessageInfo; 26 | import javax.security.auth.message.MessagePolicy; 27 | import javax.security.auth.message.module.ServerAuthModule; 28 | 29 | import org.omnifaces.security.jaspic.core.AuthParameters; 30 | import org.omnifaces.security.jaspic.core.HttpMsgContext; 31 | import org.omnifaces.security.jaspic.core.ServerAuthModuleWrapper; 32 | import org.omnifaces.security.jaspic.request.RequestDataDAO; 33 | 34 | public class SaveAndRedirectWrapper extends ServerAuthModuleWrapper { 35 | 36 | public static final String PUBLIC_REDIRECT_URL = "publicRedirectUrl"; 37 | 38 | private CallbackHandler handler; 39 | private Map options; 40 | 41 | private final RequestDataDAO requestDAO = new RequestDataDAO(); 42 | 43 | public SaveAndRedirectWrapper(ServerAuthModule serverAuthModule) { 44 | super(serverAuthModule); 45 | } 46 | 47 | @Override 48 | @SuppressWarnings({ "rawtypes", "unchecked" }) 49 | public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, Map options) throws AuthException { 50 | super.initialize(requestPolicy, responsePolicy, handler, options); 51 | this.handler = handler; 52 | this.options = options; 53 | } 54 | 55 | @Override 56 | public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { 57 | HttpMsgContext msgContext = new HttpMsgContext(handler, options, messageInfo, clientSubject); 58 | 59 | if (!msgContext.isAnyExplicitAuthCall()) { 60 | 61 | // Check to see if this request is to a protected resource 62 | // 63 | // We'll save the current request here, so we can redirect to the original URL after 64 | // authentication succeeds and when we start processing that URL wrap the request 65 | // with one containing the original headers, cookies, etc. 66 | // 67 | // NOTE: This assumes that automatic session management is used (like e.g. AutoRegisterSessionWrapper does) and that the SAMs 68 | // are NOT invoked when the user is authenticated and has access to the resource. 69 | // 70 | // TODO: Add checks is user is authenticated and deal with case where user is authenticated but doesn't have access 71 | if (msgContext.isProtected()) { 72 | 73 | requestDAO.save(msgContext.getRequest()); 74 | redirect(msgContext.getRequest(), msgContext.getResponse(), getBaseURL(msgContext.getRequest()) + msgContext.getModuleOption(PUBLIC_REDIRECT_URL) + "?new=false"); 75 | 76 | return SEND_CONTINUE; // End request processing for this request and don't try to process the handler 77 | } 78 | 79 | // No explicit login request and no protected resource. Just continue. 80 | return null; 81 | } else { 82 | 83 | // An explicit authentication call was done. Check if this call was accompanied by a 84 | // redirect URL 85 | 86 | String redirectUrl = getRedirectUrl(msgContext); 87 | if (redirectUrl != null) { 88 | requestDAO.saveUrlOnly(msgContext.getRequest(), redirectUrl); 89 | } 90 | 91 | return super.validateRequest(messageInfo, clientSubject, serviceSubject); 92 | } 93 | } 94 | 95 | private String getRedirectUrl(HttpMsgContext msgContext) { 96 | AuthParameters authParameters = msgContext.getAuthParameters(); 97 | return authParameters != null ? authParameters.getRedirectUrl() : null; 98 | } 99 | 100 | } --------------------------------------------------------------------------------