Skip to content

Commit

Permalink
- test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
afabiani committed Sep 5, 2016
1 parent 1fbcfc2 commit 32cea9d
Show file tree
Hide file tree
Showing 17 changed files with 1,082 additions and 693 deletions.
15 changes: 15 additions & 0 deletions src/community/security/oauth2-google/pom.xml
Expand Up @@ -53,5 +53,20 @@
<artifactId>easymockclassextension</artifactId> <artifactId>easymockclassextension</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>
Expand Up @@ -23,35 +23,29 @@
/** /**
* Google specific REST remplates for OAuth2 protocol. * Google specific REST remplates for OAuth2 protocol.
* <p> * <p>
* First of all the user must create an API key through the Google API * First of all the user must create an API key through the Google API Credentials <br/>
* Credentials <br/> * See: <string>https://console.developers.google.com/apis/credentials/oauthclient </strong>
* See:
* <string>https://console.developers.google.com/apis/credentials/oauthclient
* </strong>
* </p> * </p>
* <p> * <p>
* The procedure will provide a new <b>Client ID</b> and <b>Client Secret</b> * The procedure will provide a new <b>Client ID</b> and <b>Client Secret</b>
* </p> * </p>
* <p> * <p>
* Also the user must specify the <b>Authorized redirect URIs</b> pointing to * Also the user must specify the <b>Authorized redirect URIs</b> pointing to the GeoServer instances <br/>
* the GeoServer instances <br/>
* Example: * Example:
* <ul> * <ul>
* <li>http://localhost:8080/geoserver</li> * <li>http://localhost:8080/geoserver</li>
* <li>http://localhost:8080/geoserver/</li> * <li>http://localhost:8080/geoserver/</li>
* </ul> * </ul>
* </p> * </p>
* <p> * <p>
* The Google OAuth2 Filter Endpoint will automatically redirect the users to an * The Google OAuth2 Filter Endpoint will automatically redirect the users to an URL like the following one at first login <br/>
* URL like the following one at first login <br/>
* <br/> * <br/>
* <code> * <code>
* https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=my_client_id&redirect_uri=http://localhost:8080/geoserver&scope=https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile * https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=my_client_id&redirect_uri=http://localhost:8080/geoserver&scope=https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile
* </code> * </code>
* </p> * </p>
* <p> * <p>
* Tipically a correct configuration for the Google OAuth2 Provider is like the * Tipically a correct configuration for the Google OAuth2 Provider is like the following:
* following:
* </p> * </p>
* <ul> * <ul>
* <li>Cliend Id: <b>my_client_id</b></li> * <li>Cliend Id: <b>my_client_id</b></li>
Expand All @@ -70,26 +64,28 @@
@EnableOAuth2Client @EnableOAuth2Client
class GoogleOAuth2SecurityConfiguration extends GeoServerOAuth2SecurityConfiguration { class GoogleOAuth2SecurityConfiguration extends GeoServerOAuth2SecurityConfiguration {


/** /**
* Must have "session" scope * Must have "session" scope
*/ */
@Bean @Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public OAuth2RestTemplate geoServerOauth2RestTemplate() { public OAuth2RestTemplate geoServerOauth2RestTemplate() {


OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(geoServerOAuth2Resource(), OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(geoServerOAuth2Resource(),
new DefaultOAuth2ClientContext(accessTokenRequest)); new DefaultOAuth2ClientContext(getAccessTokenRequest()));


AuthorizationCodeAccessTokenProvider authorizationCodeAccessTokenProvider = new AuthorizationCodeAccessTokenProvider(); AuthorizationCodeAccessTokenProvider authorizationCodeAccessTokenProvider = new AuthorizationCodeAccessTokenProvider();
authorizationCodeAccessTokenProvider.setStateMandatory(false); authorizationCodeAccessTokenProvider.setStateMandatory(false);


AccessTokenProvider accessTokenProviderChain = new AccessTokenProviderChain(Arrays.<AccessTokenProvider> asList( AccessTokenProvider accessTokenProviderChain = new AccessTokenProviderChain(
authorizationCodeAccessTokenProvider, new ImplicitAccessTokenProvider(), Arrays.<AccessTokenProvider> asList(authorizationCodeAccessTokenProvider,
new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider())); new ImplicitAccessTokenProvider(),
new ResourceOwnerPasswordAccessTokenProvider(),
new ClientCredentialsAccessTokenProvider()));


oAuth2RestTemplate.setAccessTokenProvider(accessTokenProviderChain); oAuth2RestTemplate.setAccessTokenProvider(accessTokenProviderChain);


return oAuth2RestTemplate; return oAuth2RestTemplate;
} }


} }
Expand Up @@ -25,52 +25,51 @@
*/ */
public class GoogleAccessTokenConverter extends DefaultAccessTokenConverter { public class GoogleAccessTokenConverter extends DefaultAccessTokenConverter {


private UserAuthenticationConverter userTokenConverter; private UserAuthenticationConverter userTokenConverter;


public GoogleAccessTokenConverter() { public GoogleAccessTokenConverter() {
final DefaultUserAuthenticationConverter defaultUserAuthConverter = final DefaultUserAuthenticationConverter defaultUserAuthConverter = new GoogleUserAuthenticationConverter(
new GoogleUserAuthenticationConverter("email"); "email");
setUserTokenConverter(defaultUserAuthConverter); setUserTokenConverter(defaultUserAuthConverter);
} }


/** /**
* Converter for the part of the data in the token representing a user. * Converter for the part of the data in the token representing a user.
* *
* @param userTokenConverter * @param userTokenConverter the userTokenConverter to set
* the userTokenConverter to set */
*/ public final void setUserTokenConverter(UserAuthenticationConverter userTokenConverter) {
public final void setUserTokenConverter(UserAuthenticationConverter userTokenConverter) { this.userTokenConverter = userTokenConverter;
this.userTokenConverter = userTokenConverter; super.setUserTokenConverter(userTokenConverter);
super.setUserTokenConverter(userTokenConverter); }
}


@Override @Override
public OAuth2Authentication extractAuthentication(Map<String, ?> map) { public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
Map<String, String> parameters = new HashMap<>(); Map<String, String> parameters = new HashMap<>();
Set<String> scope = parseScopes(map); Set<String> scope = parseScopes(map);
Authentication user = userTokenConverter.extractAuthentication(map); Authentication user = userTokenConverter.extractAuthentication(map);
String clientId = (String) map.get(CLIENT_ID); String clientId = (String) map.get(CLIENT_ID);
parameters.put(CLIENT_ID, clientId); parameters.put(CLIENT_ID, clientId);
Set<String> resourceIds = new LinkedHashSet<>( Set<String> resourceIds = new LinkedHashSet<>(map.containsKey(AUD)
map.containsKey(AUD) ? (Collection<String>) map.get(AUD) : Collections.<String> emptySet()); ? (Collection<String>) map.get(AUD) : Collections.<String> emptySet());
OAuth2Request request = new OAuth2Request(parameters, clientId, null, true, scope, resourceIds, null, null, OAuth2Request request = new OAuth2Request(parameters, clientId, null, true, scope,
null); resourceIds, null, null, null);
return new OAuth2Authentication(request, user); return new OAuth2Authentication(request, user);
} }


private Set<String> parseScopes(Map<String, ?> map) { private Set<String> parseScopes(Map<String, ?> map) {
// Parsing of scopes coming back from Google are slightly different from // Parsing of scopes coming back from Google are slightly different from
// the default implementation. Instead of it being a collection it is a // the default implementation. Instead of it being a collection it is a
// String where multiple scopes are separated by a space. // String where multiple scopes are separated by a space.
Object scopeAsObject = map.containsKey(SCOPE) ? map.get(SCOPE) : ""; Object scopeAsObject = map.containsKey(SCOPE) ? map.get(SCOPE) : "";
Set<String> scope = new LinkedHashSet<>(); Set<String> scope = new LinkedHashSet<>();
if (String.class.isAssignableFrom(scopeAsObject.getClass())) { if (String.class.isAssignableFrom(scopeAsObject.getClass())) {
String scopeAsString = (String) scopeAsObject; String scopeAsString = (String) scopeAsObject;
Collections.addAll(scope, scopeAsString.split(" ")); Collections.addAll(scope, scopeAsString.split(" "));
} else if (Collection.class.isAssignableFrom(scopeAsObject.getClass())) { } else if (Collection.class.isAssignableFrom(scopeAsObject.getClass())) {
Collection<String> scopes = (Collection<String>) scopeAsObject; Collection<String> scopes = (Collection<String>) scopeAsObject;
scope.addAll(scopes); scope.addAll(scopes);
} }
return scope; return scope;
} }
} }
Expand Up @@ -33,7 +33,7 @@
public class GoogleTokenServices extends GeoServerOAuthRemoteTokenServices { public class GoogleTokenServices extends GeoServerOAuthRemoteTokenServices {


public GoogleTokenServices() { public GoogleTokenServices() {
tokenConverter = new GoogleAccessTokenConverter(); tokenConverter = new GoogleAccessTokenConverter();
restTemplate = new RestTemplate(); restTemplate = new RestTemplate();
((RestTemplate) restTemplate).setErrorHandler(new DefaultResponseErrorHandler() { ((RestTemplate) restTemplate).setErrorHandler(new DefaultResponseErrorHandler() {
@Override @Override
Expand All @@ -47,7 +47,8 @@ public void handleError(ClientHttpResponse response) throws IOException {
} }


@Override @Override
public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException { public OAuth2Authentication loadAuthentication(String accessToken)
throws AuthenticationException, InvalidTokenException {
Map<String, Object> checkTokenResponse = checkToken(accessToken); Map<String, Object> checkTokenResponse = checkToken(accessToken);


if (checkTokenResponse.containsKey("error")) { if (checkTokenResponse.containsKey("error")) {
Expand All @@ -57,7 +58,8 @@ public OAuth2Authentication loadAuthentication(String accessToken) throws Authen


transformNonStandardValuesToStandardValues(checkTokenResponse); transformNonStandardValuesToStandardValues(checkTokenResponse);


Assert.state(checkTokenResponse.containsKey("client_id"), "Client id must be present in response from auth server"); Assert.state(checkTokenResponse.containsKey("client_id"),
"Client id must be present in response from auth server");
return tokenConverter.extractAuthentication(checkTokenResponse); return tokenConverter.extractAuthentication(checkTokenResponse);
} }


Expand All @@ -66,7 +68,8 @@ private Map<String, Object> checkToken(String accessToken) {
formData.add("token", accessToken); formData.add("token", accessToken);
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", getAuthorizationHeader(clientId, clientSecret)); headers.set("Authorization", getAuthorizationHeader(clientId, clientSecret));
String accessTokenUrl = new StringBuilder(checkTokenEndpointUrl).append("?access_token=").append(accessToken).toString(); String accessTokenUrl = new StringBuilder(checkTokenEndpointUrl).append("?access_token=")
.append(accessToken).toString();
return postForMap(accessTokenUrl, formData, headers); return postForMap(accessTokenUrl, formData, headers);
} }


Expand All @@ -86,11 +89,15 @@ private String getAuthorizationHeader(String clientId, String clientSecret) {
} }
} }


private Map<String, Object> postForMap(String path, MultiValueMap<String, String> formData, HttpHeaders headers) { private Map<String, Object> postForMap(String path, MultiValueMap<String, String> formData,
HttpHeaders headers) {
if (headers.getContentType() == null) { if (headers.getContentType() == null) {
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
} }
ParameterizedTypeReference<Map<String, Object>> map = new ParameterizedTypeReference<Map<String, Object>>() {}; ParameterizedTypeReference<Map<String, Object>> map = new ParameterizedTypeReference<Map<String, Object>>() {
return restTemplate.exchange(path, HttpMethod.POST, new HttpEntity<>(formData, headers), map).getBody(); };
return restTemplate
.exchange(path, HttpMethod.POST, new HttpEntity<>(formData, headers), map)
.getBody();
} }
} }
Expand Up @@ -17,24 +17,24 @@
*/ */
public class GoogleUserAuthenticationConverter extends DefaultUserAuthenticationConverter { public class GoogleUserAuthenticationConverter extends DefaultUserAuthenticationConverter {


private static Object USERNAME_KEY = USERNAME; private static Object USERNAME_KEY = USERNAME;

/** /**
* Default Constructor. * Default Constructor.
* *
* @param username_key * @param username_key
*/ */
public GoogleUserAuthenticationConverter(String username_key) { public GoogleUserAuthenticationConverter(String username_key) {
super(); super();

USERNAME_KEY = username_key; USERNAME_KEY = username_key;
} }

@Override @Override
public Authentication extractAuthentication(Map<String, ?> map) { public Authentication extractAuthentication(Map<String, ?> map) {
if (map.containsKey(USERNAME_KEY)) { if (map.containsKey(USERNAME_KEY)) {
return new UsernamePasswordAuthenticationToken(map.get(USERNAME_KEY), "N/A", null); return new UsernamePasswordAuthenticationToken(map.get(USERNAME_KEY), "N/A", null);
} }
return null; return null;
} }
} }

0 comments on commit 32cea9d

Please sign in to comment.