Skip to content
This repository was archived by the owner on Dec 12, 2018. It is now read-only.

Commit ac51144

Browse files
authored
Merge pull request #1172 from stormpath/1158_handlers_fix_using_spring_security_in_front_stormpath
Fix for SS Login and Logout handlers not being invoked
2 parents 3f4c099 + 2433efc commit ac51144

File tree

47 files changed

+1488
-383
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1488
-383
lines changed

examples/spring-security-spring-boot-webmvc-bare-bones/src/main/resources/static/index.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
<!--/*
2+
~ Copyright 2016 Stormpath, Inc.
3+
~
4+
~ Licensed under the Apache License, Version 2.0 (the "License");
5+
~ you may not use this file except in compliance with the License.
6+
~ You may obtain a copy of the License at
7+
~
8+
~ http://www.apache.org/licenses/LICENSE-2.0
9+
~
10+
~ Unless required by applicable law or agreed to in writing, software
11+
~ distributed under the License is distributed on an "AS IS" BASIS,
12+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
~ See the License for the specific language governing permissions and
14+
~ limitations under the License.
15+
*/-->
116
<!DOCTYPE html>
217
<html>
318
<head>

examples/spring-security-webmvc/src/main/java/com/stormpath/spring/examples/WebAppInitializer.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package com.stormpath.spring.examples;
1717

18+
import com.stormpath.sdk.servlet.filter.StormpathFilter;
1819
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
1920
import org.springframework.web.WebApplicationInitializer;
2021
import org.springframework.web.context.ContextLoaderListener;
@@ -46,14 +47,15 @@ public void onStartup(ServletContext sc) throws ServletException {
4647
dispatcher.setLoadOnStartup(1);
4748
dispatcher.addMapping("/");
4849

49-
//Stormpath Filter
50-
FilterRegistration.Dynamic filter = sc.addFilter("stormpathFilter", new DelegatingFilterProxy());
50+
//Spring Security Filter: in front of Stormpath
51+
FilterRegistration.Dynamic securityFilter = sc.addFilter(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME, DelegatingFilterProxy.class);
52+
securityFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/*");
53+
54+
//Stormpath Filter (after Spring Security)
55+
FilterRegistration.Dynamic stormpathFilter = sc.addFilter(StormpathFilter.DEFAULT_FILTER_NAME, DelegatingFilterProxy.class);
5156
EnumSet<DispatcherType> types =
5257
EnumSet.of(DispatcherType.ERROR, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.REQUEST);
53-
filter.addMappingForUrlPatterns(types, false, "/*");
58+
stormpathFilter.addMappingForUrlPatterns(types, false, "/*");
5459

55-
//Spring Security Filter
56-
FilterRegistration.Dynamic securityFilter = sc.addFilter(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME, DelegatingFilterProxy.class);
57-
securityFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/*");
5860
}
5961
}

extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/saml/SamlIdentityProviderIT.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ yl85oFHAdkguTA==
630630

631631
CreateSamlResponseRequest createSamlResponseRequest = client.instantiate(CreateSamlResponseRequest.class)
632632
createSamlResponseRequest.setAccount(account)
633-
createSamlResponseRequest.setAuthnIssueInstant(new Date())
633+
createSamlResponseRequest.setAuthnIssueInstant(new Date())
634634
createSamlResponseRequest.setRequestId(requestId)
635635
.setServiceProvider(registeredSamlServiceProvider)
636636

extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/StormpathFilter.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
*/
3838
public class StormpathFilter extends HttpFilter {
3939

40+
public final static String DEFAULT_FILTER_NAME = "stormpathFilter";
41+
4042
private FilterChainResolver filterChainResolver;
4143
private Set<String> clientRequestAttributeNames;
4244
private Set<String> applicationRequestAttributeNames;

extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/AbstractSocialCallbackController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public boolean isNotAllowedIfAuthenticated() {
5555
return true;
5656
}
5757

58-
protected abstract ProviderAccountRequest getAccountProviderRequest(HttpServletRequest request);
58+
public abstract ProviderAccountRequest getAccountProviderRequest(HttpServletRequest request);
5959

6060
@Override
6161
protected ViewModel doGet(HttpServletRequest request, HttpServletResponse response) throws Exception {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2017 Stormpath, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.stormpath.sdk.servlet.mvc;
17+
18+
import com.stormpath.sdk.lang.Strings;
19+
import com.stormpath.sdk.provider.ProviderAccountRequest;
20+
import com.stormpath.sdk.provider.Providers;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
24+
import javax.servlet.http.HttpServletRequest;
25+
import java.util.Map;
26+
27+
import static com.stormpath.sdk.servlet.mvc.JacksonFieldValueResolver.MARSHALLED_OBJECT;
28+
29+
// Refactor of Provider requests for
30+
// https://github.com/stormpath/stormpath-sdk-java/issues/915
31+
// and to provide uniform responses across all integrations for
32+
// conformance to stormpath-framework-spec as enforced by
33+
// stormpath-framework-tck
34+
/**
35+
* @since 1.3.0
36+
*/
37+
public class DefaultProviderAccountRequestFactory implements ProviderAccountRequestFactory {
38+
39+
private static final Logger log = LoggerFactory.getLogger(DefaultProviderAccountRequestFactory.class);
40+
41+
private final GithubAccessTokenResolver githubAccessTokenResolver = new GithubAccessTokenResolver();
42+
43+
@Override
44+
@SuppressWarnings("unchecked")
45+
public ProviderAccountRequest getProviderAccountRequest(HttpServletRequest request) {
46+
Map<String, Object> map = (Map<String, Object>) request.getAttribute(MARSHALLED_OBJECT);
47+
48+
if (map != null && map.get("providerData") != null) {
49+
Map<String, String> providerData = (Map<String, String>) map.get("providerData");
50+
51+
String providerId = providerData.get("providerId");
52+
if (Strings.hasText(providerId)) {
53+
switch (providerId) {
54+
case "facebook": {
55+
String accessToken = providerData.get("accessToken");
56+
return Providers.FACEBOOK
57+
.account().setAccessToken(accessToken).build();
58+
}
59+
case "github": {
60+
String accessToken = githubAccessTokenResolver.get(request, null);
61+
return Providers.GITHUB
62+
.account().setAccessToken(accessToken).build();
63+
}
64+
case "google": {
65+
String code = providerData.get("code");
66+
return Providers.GOOGLE
67+
.account().setCode(code).build();
68+
}
69+
case "linkedin": {
70+
String code = providerData.get("code");
71+
return Providers.LINKEDIN
72+
.account().setCode(code).build();
73+
}
74+
case "twitter": {
75+
String code = providerData.get("accessToken");
76+
return Providers.TWITTER
77+
.account().setAccessToken(code).build();
78+
}
79+
default: {
80+
log.error("No provider configured for " + providerId);
81+
return null;
82+
}
83+
}
84+
}
85+
}
86+
87+
log.debug("Provider data not found in request.");
88+
return null;
89+
}
90+
}

extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/FormController.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,10 @@ protected ViewModel doGet(HttpServletRequest request, HttpServletResponse respon
132132
@SuppressWarnings("unchecked")
133133
protected Map<String,?> createModel(HttpServletRequest request, HttpServletResponse response) {
134134
List<ErrorModel> errors = null;
135-
if (request.getParameter("error") != null) {
135+
// We need to look for error in queryString now that we moved SpringSecurity to in in front of us: https://github.com/stormpath/stormpath-sdk-java/issues/915
136+
String queryString = request.getQueryString();
137+
boolean requestContainsError = request.getParameter("error") != null || (queryString != null && queryString.contains("error"));
138+
if (requestContainsError) {
136139
errors = new ArrayList<>();
137140
ErrorModel error = null;
138141
HttpSession session = request.getSession(false);
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Copyright 2017 Stormpath, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.stormpath.sdk.servlet.mvc;
17+
18+
import com.fasterxml.jackson.databind.ObjectMapper;
19+
import com.stormpath.sdk.application.Application;
20+
import com.stormpath.sdk.application.ApplicationAccountStoreMapping;
21+
import com.stormpath.sdk.directory.AccountStore;
22+
import com.stormpath.sdk.directory.AccountStoreVisitor;
23+
import com.stormpath.sdk.directory.AccountStoreVisitorAdapter;
24+
import com.stormpath.sdk.directory.Directory;
25+
import com.stormpath.sdk.impl.provider.DefaultGithubProvider;
26+
import com.stormpath.sdk.lang.Assert;
27+
import com.stormpath.sdk.servlet.application.ApplicationResolver;
28+
import com.stormpath.sdk.servlet.http.MediaType;
29+
import com.stormpath.sdk.servlet.http.Resolver;
30+
import org.apache.http.HttpResponse;
31+
import org.apache.http.NameValuePair;
32+
import org.apache.http.client.HttpClient;
33+
import org.apache.http.client.entity.UrlEncodedFormEntity;
34+
import org.apache.http.client.methods.HttpPost;
35+
import org.apache.http.impl.client.HttpClientBuilder;
36+
import org.apache.http.message.BasicNameValuePair;
37+
import org.slf4j.Logger;
38+
import org.slf4j.LoggerFactory;
39+
40+
import javax.servlet.http.HttpServletRequest;
41+
import javax.servlet.http.HttpServletResponse;
42+
import java.nio.charset.StandardCharsets;
43+
import java.util.ArrayList;
44+
import java.util.List;
45+
import java.util.Map;
46+
47+
/**
48+
* @since 1.3.0
49+
*/
50+
public class GithubAccessTokenResolver implements Resolver<String> {
51+
52+
private static final Logger log = LoggerFactory.getLogger(GithubAccessTokenResolver.class);
53+
private static final String GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
54+
private static final String GITHUB_ACCESS_TOKEN_FIELD = "access_token";
55+
56+
/**
57+
* Obtains an access token from GitHub.
58+
* @return a Github access token
59+
*/
60+
@Override
61+
public String get(HttpServletRequest request, HttpServletResponse response) {
62+
final DefaultGithubProvider[] githubProvider = new DefaultGithubProvider[1];
63+
64+
Application application = ApplicationResolver.INSTANCE.getApplication(request);
65+
for (ApplicationAccountStoreMapping mapping : application.getAccountStoreMappings()) {
66+
AccountStore accountStore = mapping.getAccountStore();
67+
68+
AccountStoreVisitor accountStoreVisitor = new AccountStoreVisitorAdapter() {
69+
@Override
70+
public void visit(Directory directory) {
71+
if ("github".equals(directory.getProvider().getProviderId())) {
72+
githubProvider[0] = (DefaultGithubProvider) directory.getProvider();
73+
}
74+
}
75+
};
76+
accountStore.accept(accountStoreVisitor);
77+
}
78+
79+
Assert.notNull(githubProvider[0], "githubProvider cannot be null.");
80+
81+
HttpClient client = HttpClientBuilder.create().build();
82+
83+
try {
84+
HttpPost httpPost = new HttpPost(GITHUB_ACCESS_TOKEN_URL);
85+
List<NameValuePair> nvps = new ArrayList<>();
86+
nvps.add(new BasicNameValuePair("code", getCode(request)));
87+
nvps.add(new BasicNameValuePair("client_id", githubProvider[0].getClientId()));
88+
nvps.add(new BasicNameValuePair("client_secret", githubProvider[0].getClientSecret()));
89+
90+
httpPost.setEntity(new UrlEncodedFormEntity(nvps, StandardCharsets.UTF_8.displayName()));
91+
httpPost.addHeader("Accept", MediaType.APPLICATION_JSON_VALUE);
92+
93+
HttpResponse gitHubResponse = client.execute(httpPost);
94+
ObjectMapper objectMapper = new ObjectMapper();
95+
96+
//noinspection unchecked
97+
Map<String, String> result = objectMapper.readValue(gitHubResponse.getEntity().getContent(), Map.class);
98+
return result.get(GITHUB_ACCESS_TOKEN_FIELD);
99+
} catch (Exception e) {
100+
log.error("Couldn't exchange GitHub oAuth code for an access token", e);
101+
throw new RuntimeException(e);
102+
}
103+
}
104+
105+
@SuppressWarnings("unchecked")
106+
private String getCode(HttpServletRequest request) throws IllegalArgumentException {
107+
return request.getParameter("code");
108+
}
109+
}

extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/IdSiteLogoutController.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.stormpath.sdk.servlet.filter.ServerUriResolver;
2222
import com.stormpath.sdk.servlet.http.Resolver;
2323
import com.stormpath.sdk.servlet.idsite.IdSiteOrganizationContext;
24+
import com.stormpath.sdk.servlet.util.ServletUtils;
2425

2526
import javax.servlet.http.HttpServletRequest;
2627
import javax.servlet.http.HttpServletResponse;
@@ -73,7 +74,12 @@ public ViewModel handleRequest(HttpServletRequest request, HttpServletResponse r
7374
}
7475

7576
//redirect to ID Site to perform the SSO logout to effectively log out the user across all applications:
76-
return idSiteController.handleRequest(request, response);
77+
ViewModel viewModel = idSiteController.handleRequest(request, response);
78+
//Let's commit the response right away as sometimes IDSite's logout fails to be invoked on time before the login screen is re-loaded.
79+
//When that happens, the logout does not take place either in IDSIte nor in our end since IDSIte will reply to the login request with
80+
//an already authenticated response.That causes our integration to create a new cookie.
81+
ServletUtils.issueRedirect(request, response, viewModel.getViewName(), null, true, true);
82+
return null;
7783
}
7884

7985
private static class LogoutIdSiteController extends IdSiteController {

extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/JacksonFieldValueResolver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
*/
1414
public class JacksonFieldValueResolver implements RequestFieldValueResolver {
1515

16-
protected static final String MARSHALLED_OBJECT = JacksonFieldValueResolver.class.getName() + ".MARSHALLED_OBJECT";
16+
public static final String MARSHALLED_OBJECT = JacksonFieldValueResolver.class.getName() + ".MARSHALLED_OBJECT";
1717

1818
ObjectMapper objectMapper = new ObjectMapper();
1919

0 commit comments

Comments
 (0)