> 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 | }
--------------------------------------------------------------------------------