Commit af09579e authored by Daniel Gerhardt's avatar Daniel Gerhardt

Migrate to Pac4j for OAuth handling

This improves/fixes compatibility with third-party OAuth APIs.
parent 150cab8c
......@@ -215,11 +215,14 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>
com.github.leleuj.springframework.security
</groupId>
<artifactId>spring-security-oauth-client</artifactId>
<version>1.0.0</version>
<groupId>org.pac4j</groupId>
<artifactId>spring-security-pac4j</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>org.pac4j</groupId>
<artifactId>pac4j-oauth</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>com.corundumstudio.socketio</groupId>
......
......@@ -17,9 +17,6 @@
*/
package de.thm.arsnova.config;
import com.github.leleuj.ss.oauth.client.authentication.OAuthAuthenticationProvider;
import com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationEntryPoint;
import com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationFilter;
import de.thm.arsnova.CASLogoutSuccessHandler;
import de.thm.arsnova.CasUserDetailsService;
import de.thm.arsnova.LoginAuthenticationFailureHandler;
......@@ -28,10 +25,12 @@ import de.thm.arsnova.security.ApplicationPermissionEvaluator;
import de.thm.arsnova.security.CustomLdapUserDetailsMapper;
import de.thm.arsnova.security.DbUserDetailsService;
import org.jasig.cas.client.validation.Cas20ProxyTicketValidator;
import org.scribe.up.provider.impl.FacebookProvider;
import org.scribe.up.provider.impl.Google2Provider;
import org.scribe.up.provider.impl.Google2Provider.Google2Scope;
import org.scribe.up.provider.impl.TwitterProvider;
import org.pac4j.core.client.Client;
import org.pac4j.core.config.Config;
import org.pac4j.oauth.client.FacebookClient;
import org.pac4j.oauth.client.Google2Client;
import org.pac4j.oauth.client.TwitterClient;
import org.pac4j.springframework.security.web.CallbackFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -88,6 +87,7 @@ import java.util.List;
@EnableWebSecurity
@Profile("!test")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final String OAUTH_CALLBACK_PATH_SUFFIX = "/auth/oauth_callback";
private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
@Autowired
......@@ -140,14 +140,11 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
http.addFilter(casAuthenticationFilter());
http.addFilter(casLogoutFilter());
}
if (googleEnabled) {
http.addFilterAfter(googleFilter(), CasAuthenticationFilter.class);
}
if (facebookEnabled) {
http.addFilterAfter(facebookFilter(), CasAuthenticationFilter.class);
}
if (twitterEnabled) {
http.addFilterAfter(twitterFilter(), CasAuthenticationFilter.class);
if (facebookEnabled || googleEnabled || twitterEnabled) {
CallbackFilter callbackFilter = new CallbackFilter(oauthConfig());
callbackFilter.setSuffix(OAUTH_CALLBACK_PATH_SUFFIX);
http.addFilterAfter(callbackFilter, CasAuthenticationFilter.class);
}
}
......@@ -168,15 +165,12 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
if (googleEnabled) {
providers.add("google");
auth.authenticationProvider(googleAuthProvider());
}
if (facebookEnabled) {
providers.add("facebook");
auth.authenticationProvider(facebookAuthProvider());
}
if (twitterEnabled) {
providers.add("twitter");
auth.authenticationProvider(twitterAuthProvider());
}
logger.info("Enabled authentication providers: {}", providers);
}
......@@ -328,7 +322,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public ServiceProperties casServiceProperties() {
ServiceProperties properties = new ServiceProperties();
properties.setService(rootUrl + servletContext.getContextPath() + "/login/cas");
properties.setService(rootUrl + apiPath + "/login/cas");
properties.setSendRenew(false);
return properties;
......@@ -375,104 +369,45 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
return handler;
}
// Facebook Authentication Configuration
@Bean
public OAuthAuthenticationEntryPoint facebookEntryPoint() {
final OAuthAuthenticationEntryPoint entryPoint = new OAuthAuthenticationEntryPoint();
entryPoint.setProvider(facebookProvider());
return entryPoint;
}
@Bean
public FacebookProvider facebookProvider() {
final FacebookProvider provider = new FacebookProvider();
provider.setKey(facebookKey);
provider.setSecret(facebookSecret);
provider.setCallbackUrl(rootUrl + servletContext.getContextPath() + "/j_spring_facebook_security_check");
return provider;
}
@Bean
public OAuthAuthenticationFilter facebookFilter() throws Exception {
final OAuthAuthenticationFilter filter = new OAuthAuthenticationFilter("/j_spring_facebook_security_check");
filter.setProvider(facebookProvider());
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationFailureHandler(failureHandler());
filter.setAuthenticationSuccessHandler(successHandler());
return filter;
}
@Bean
public OAuthAuthenticationProvider facebookAuthProvider() {
final OAuthAuthenticationProvider authProvider = new OAuthAuthenticationProvider();
authProvider.setProvider(facebookProvider());
return authProvider;
}
// Twitter Authentication Configuration
// OAuth Authentication Configuration
@Bean
public TwitterProvider twitterProvider() {
final TwitterProvider provider = new TwitterProvider();
provider.setKey(twitterKey);
provider.setSecret(twitterSecret);
provider.setCallbackUrl(rootUrl + servletContext.getContextPath() + "/j_spring_twitter_security_check");
return provider;
}
public Config oauthConfig() {
List<Client> clients = new ArrayList<>();
if (facebookEnabled) {
clients.add(facebookClient());
}
if (googleEnabled) {
clients.add(googleClient());
}
if (twitterEnabled) {
clients.add(twitterClient());
}
@Bean
public OAuthAuthenticationFilter twitterFilter() throws Exception {
final OAuthAuthenticationFilter filter = new OAuthAuthenticationFilter("/j_spring_twitter_security_check");
filter.setProvider(twitterProvider());
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationFailureHandler(failureHandler());
filter.setAuthenticationSuccessHandler(successHandler());
return filter;
return new Config(rootUrl + apiPath + OAUTH_CALLBACK_PATH_SUFFIX, clients);
}
@Bean
public OAuthAuthenticationProvider twitterAuthProvider() {
final OAuthAuthenticationProvider authProvider = new OAuthAuthenticationProvider();
authProvider.setProvider(twitterProvider());
public FacebookClient facebookClient() {
final FacebookClient client = new FacebookClient(facebookKey, facebookSecret);
client.setCallbackUrl(rootUrl + apiPath + OAUTH_CALLBACK_PATH_SUFFIX + "?client_name=FacebookClient");
return authProvider;
return client;
}
// Google Authentication Configuration
@Bean
public Google2Provider googleProvider() {
final Google2Provider provider = new Google2Provider();
provider.setKey(googleKey);
provider.setSecret(googleSecret);
provider.setCallbackUrl(rootUrl + servletContext.getContextPath() + "/j_spring_google_security_check");
provider.setScope(Google2Scope.EMAIL);
return provider;
}
public TwitterClient twitterClient() {
final TwitterClient client = new TwitterClient(twitterKey, twitterSecret);
client.setCallbackUrl(rootUrl + apiPath + OAUTH_CALLBACK_PATH_SUFFIX + "?client_name=TwitterClient");
@Bean
public OAuthAuthenticationFilter googleFilter() throws Exception {
final OAuthAuthenticationFilter filter = new OAuthAuthenticationFilter("/j_spring_google_security_check");
filter.setProvider(googleProvider());
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationFailureHandler(failureHandler());
filter.setAuthenticationSuccessHandler(successHandler());
return filter;
return client;
}
@Bean
public OAuthAuthenticationProvider googleAuthProvider() {
final OAuthAuthenticationProvider authProvider = new OAuthAuthenticationProvider();
authProvider.setProvider(googleProvider());
public Google2Client googleClient() {
final Google2Client client = new Google2Client(googleKey, googleSecret);
client.setCallbackUrl(rootUrl + apiPath + OAUTH_CALLBACK_PATH_SUFFIX + "?client_name=Google2Client");
return authProvider;
return client;
}
}
......@@ -23,10 +23,11 @@ import de.thm.arsnova.entities.User;
import de.thm.arsnova.exceptions.UnauthorizedException;
import de.thm.arsnova.services.IUserService;
import de.thm.arsnova.services.UserSessionService;
import org.scribe.up.provider.impl.FacebookProvider;
import org.scribe.up.provider.impl.Google2Provider;
import org.scribe.up.provider.impl.TwitterProvider;
import org.scribe.up.session.HttpUserSession;
import org.pac4j.core.context.J2EContext;
import org.pac4j.core.exception.HttpAction;
import org.pac4j.oauth.client.FacebookClient;
import org.pac4j.oauth.client.Google2Client;
import org.pac4j.oauth.client.TwitterClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -126,13 +127,13 @@ public class LoginController extends AbstractController {
private DaoAuthenticationProvider daoProvider;
@Autowired(required = false)
private TwitterProvider twitterProvider;
private TwitterClient twitterClient;
@Autowired(required = false)
private Google2Provider googleProvider;
private Google2Client google2Client;
@Autowired(required = false)
private FacebookProvider facebookProvider;
private FacebookClient facebookClient;
@Autowired(required = false)
private LdapAuthenticationProvider ldapAuthenticationProvider;
......@@ -245,7 +246,7 @@ public class LoginController extends AbstractController {
@RequestParam(value = "failureurl", defaultValue = "/") String failureUrl,
final HttpServletRequest request,
final HttpServletResponse response
) throws IOException, ServletException {
) throws HttpAction, IOException, ServletException {
View result = null;
/* Use URLs from a request parameters for redirection as long as the
......@@ -283,16 +284,17 @@ public class LoginController extends AbstractController {
if (casEnabled && "cas".equals(type)) {
casEntryPoint.commence(request, response, null);
} else if (twitterEnabled && "twitter".equals(type)) {
final String authUrl = twitterProvider.getAuthorizationUrl(new HttpUserSession(request));
result = new RedirectView(authUrl);
result = new RedirectView(
twitterClient.getRedirectAction(new J2EContext(request, response)).getLocation());
} else if (facebookEnabled && "facebook".equals(type)) {
facebookProvider.setFields("id,link");
facebookProvider.setScope("");
final String authUrl = facebookProvider.getAuthorizationUrl(new HttpUserSession(request));
result = new RedirectView(authUrl);
facebookClient.setFields("id,link");
facebookClient.setScope("");
result = new RedirectView(
facebookClient.getRedirectAction(new J2EContext(request, response)).getLocation());
} else if (googleEnabled && "google".equals(type)) {
final String authUrl = googleProvider.getAuthorizationUrl(new HttpUserSession(request));
result = new RedirectView(authUrl);
google2Client.setScope(Google2Client.Google2Scope.EMAIL);
result = new RedirectView(
google2Client.getRedirectAction(new J2EContext(request, response)).getLocation());
} else {
response.setStatus(HttpStatus.BAD_REQUEST.value());
}
......
......@@ -19,9 +19,9 @@ package de.thm.arsnova.entities;
import de.thm.arsnova.services.UserSessionService;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.scribe.up.profile.facebook.FacebookProfile;
import org.scribe.up.profile.google.Google2Profile;
import org.scribe.up.profile.twitter.TwitterProfile;
import org.pac4j.oauth.profile.facebook.FacebookProfile;
import org.pac4j.oauth.profile.google2.Google2Profile;
import org.pac4j.oauth.profile.twitter.TwitterProfile;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
......@@ -52,12 +52,12 @@ public class User implements Serializable {
}
public User(TwitterProfile profile) {
setUsername(profile.getScreenName());
setUsername(profile.getUsername());
setType(User.TWITTER);
}
public User(FacebookProfile profile) {
setUsername(profile.getLink());
setUsername(profile.getProfileUrl());
setType(User.FACEBOOK);
}
......
......@@ -17,16 +17,16 @@
*/
package de.thm.arsnova.security;
import com.github.leleuj.ss.oauth.client.authentication.OAuthAuthenticationToken;
import de.thm.arsnova.dao.IDatabaseDao;
import de.thm.arsnova.entities.InterposedQuestion;
import de.thm.arsnova.entities.Question;
import de.thm.arsnova.entities.Session;
import de.thm.arsnova.entities.User;
import de.thm.arsnova.exceptions.UnauthorizedException;
import org.scribe.up.profile.facebook.FacebookProfile;
import org.scribe.up.profile.google.Google2Profile;
import org.scribe.up.profile.twitter.TwitterProfile;
import org.pac4j.oauth.profile.facebook.FacebookProfile;
import org.pac4j.oauth.profile.google2.Google2Profile;
import org.pac4j.oauth.profile.twitter.TwitterProfile;
import org.pac4j.springframework.security.authentication.Pac4jAuthenticationToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.PermissionEvaluator;
......@@ -153,18 +153,18 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
throw new UnauthorizedException();
}
if (authentication instanceof OAuthAuthenticationToken) {
if (authentication instanceof Pac4jAuthenticationToken) {
User user = null;
final OAuthAuthenticationToken token = (OAuthAuthenticationToken) authentication;
if (token.getUserProfile() instanceof Google2Profile) {
final Google2Profile profile = (Google2Profile) token.getUserProfile();
final Pac4jAuthenticationToken token = (Pac4jAuthenticationToken) authentication;
if (token.getProfile() instanceof Google2Profile) {
final Google2Profile profile = (Google2Profile) token.getProfile();
user = new User(profile);
} else if (token.getUserProfile() instanceof TwitterProfile) {
final TwitterProfile profile = (TwitterProfile) token.getUserProfile();
} else if (token.getProfile() instanceof TwitterProfile) {
final TwitterProfile profile = (TwitterProfile) token.getProfile();
user = new User(profile);
} else if (token.getUserProfile() instanceof FacebookProfile) {
final FacebookProfile profile = (FacebookProfile) token.getUserProfile();
} else if (token.getProfile() instanceof FacebookProfile) {
final FacebookProfile profile = (FacebookProfile) token.getProfile();
user = new User(profile);
}
......
......@@ -18,7 +18,6 @@
package de.thm.arsnova.services;
import com.codahale.metrics.annotation.Gauge;
import com.github.leleuj.ss.oauth.client.authentication.OAuthAuthenticationToken;
import de.thm.arsnova.dao.IDatabaseDao;
import de.thm.arsnova.entities.DbUser;
import de.thm.arsnova.entities.User;
......@@ -27,9 +26,10 @@ import de.thm.arsnova.exceptions.NotFoundException;
import de.thm.arsnova.exceptions.UnauthorizedException;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.scribe.up.profile.facebook.FacebookProfile;
import org.scribe.up.profile.google.Google2Profile;
import org.scribe.up.profile.twitter.TwitterProfile;
import org.pac4j.oauth.profile.facebook.FacebookProfile;
import org.pac4j.oauth.profile.google2.Google2Profile;
import org.pac4j.oauth.profile.twitter.TwitterProfile;
import org.pac4j.springframework.security.authentication.Pac4jAuthenticationToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -184,7 +184,7 @@ public class UserService implements IUserService {
User user = null;
if (authentication instanceof OAuthAuthenticationToken) {
if (authentication instanceof Pac4jAuthenticationToken) {
user = getOAuthUser(authentication);
} else if (authentication instanceof CasAuthenticationToken) {
final CasAuthenticationToken token = (CasAuthenticationToken) authentication;
......@@ -213,15 +213,15 @@ public class UserService implements IUserService {
private User getOAuthUser(final Authentication authentication) {
User user = null;
final OAuthAuthenticationToken token = (OAuthAuthenticationToken) authentication;
if (token.getUserProfile() instanceof Google2Profile) {
final Google2Profile profile = (Google2Profile) token.getUserProfile();
final Pac4jAuthenticationToken token = (Pac4jAuthenticationToken) authentication;
if (token.getProfile() instanceof Google2Profile) {
final Google2Profile profile = (Google2Profile) token.getProfile();
user = new User(profile);
} else if (token.getUserProfile() instanceof TwitterProfile) {
final TwitterProfile profile = (TwitterProfile) token.getUserProfile();
} else if (token.getProfile() instanceof TwitterProfile) {
final TwitterProfile profile = (TwitterProfile) token.getProfile();
user = new User(profile);
} else if (token.getUserProfile() instanceof FacebookProfile) {
final FacebookProfile profile = (FacebookProfile) token.getUserProfile();
} else if (token.getProfile() instanceof FacebookProfile) {
final FacebookProfile profile = (FacebookProfile) token.getProfile();
user = new User(profile);
}
return user;
......
......@@ -17,14 +17,11 @@
*/
package de.thm.arsnova.config;
import com.github.leleuj.ss.oauth.client.authentication.OAuthAuthenticationProvider;
import com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationEntryPoint;
import com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationFilter;
import de.thm.arsnova.CasUserDetailsService;
import org.jasig.cas.client.validation.Cas20ProxyTicketValidator;
import org.scribe.up.provider.impl.FacebookProvider;
import org.scribe.up.provider.impl.Google2Provider;
import org.scribe.up.provider.impl.TwitterProvider;
import org.pac4j.oauth.client.FacebookClient;
import org.pac4j.oauth.client.Google2Client;
import org.pac4j.oauth.client.TwitterClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
......@@ -103,52 +100,17 @@ public class TestSecurityConfig extends SecurityConfig {
}
@Override
public FacebookProvider facebookProvider() {
public FacebookClient facebookClient() {
return null;
}
@Override
public OAuthAuthenticationFilter facebookFilter() {
public Google2Client googleClient() {
return null;
}
@Override
public OAuthAuthenticationProvider facebookAuthProvider() {
return null;
}
@Override
public OAuthAuthenticationEntryPoint facebookEntryPoint() {
return null;
}
@Override
public Google2Provider googleProvider() {
return null;
}
@Override
public OAuthAuthenticationFilter googleFilter() {
return null;
}
@Override
public OAuthAuthenticationProvider googleAuthProvider() {
return null;
}
@Override
public TwitterProvider twitterProvider() {
return null;
}
@Override
public OAuthAuthenticationFilter twitterFilter() {
return null;
}
@Override
public OAuthAuthenticationProvider twitterAuthProvider() {
public TwitterClient twitterClient() {
return null;
}
}
......@@ -22,8 +22,10 @@ import org.jasig.cas.client.authentication.AttributePrincipalImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.scribe.up.profile.google.Google2AttributesDefinition;
import org.scribe.up.profile.google.Google2Profile;
import org.pac4j.oauth.profile.JsonHelper;
import org.pac4j.oauth.profile.google2.Google2AttributesDefinition;
import org.pac4j.oauth.profile.google2.Google2Email;
import org.pac4j.oauth.profile.google2.Google2Profile;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
......@@ -36,7 +38,6 @@ import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
......@@ -56,9 +57,13 @@ public class UserServiceTest {
socketid2user.put(UUID.randomUUID(), new User(new UsernamePasswordAuthenticationToken("ptsr00", UUID.randomUUID())));
socketid2user.put(UUID.randomUUID(), new User(new AttributePrincipalImpl("ptstr0")));
Map<String, Object> attributes = new HashMap<>();
attributes.put(Google2AttributesDefinition.EMAIL, "mail@host.com");
Google2Profile profile = new Google2Profile("ptsr00", attributes);
Google2Email email = new Google2Email();
email.setEmail("mail@host.com");
ArrayList<Google2Email> emails = new ArrayList<>();
emails.add(email);
Google2Profile profile = new Google2Profile();
profile.addAttribute(Google2AttributesDefinition.DISPLAY_NAME, "ptsr00");
profile.addAttribute(Google2AttributesDefinition.EMAILS, JsonHelper.toJSONString(emails));
socketid2user.put(UUID.randomUUID(), new User(profile));
List<GrantedAuthority> authorities = new ArrayList<>();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment